Add custom I/O executor support to basic_waitable_timer.
diff --git a/asio/include/asio/basic_waitable_timer.hpp b/asio/include/asio/basic_waitable_timer.hpp
index 56c02d7..3ff8772 100644
--- a/asio/include/asio/basic_waitable_timer.hpp
+++ b/asio/include/asio/basic_waitable_timer.hpp
@@ -23,6 +23,7 @@
#include "asio/detail/io_object_impl.hpp"
#include "asio/detail/throw_error.hpp"
#include "asio/error.hpp"
+#include "asio/executor.hpp"
#include "asio/wait_traits.hpp"
#if defined(ASIO_HAS_MOVE)
@@ -38,7 +39,8 @@
// Forward declaration with defaulted arguments.
template <typename Clock,
- typename WaitTraits = asio::wait_traits<Clock> >
+ typename WaitTraits = asio::wait_traits<Clock>,
+ typename Executor = asio::executor>
class basic_waitable_timer;
#endif // !defined(ASIO_BASIC_WAITABLE_TIMER_FWD_DECL)
@@ -135,12 +137,12 @@
* @li If a wait handler is cancelled, the asio::error_code passed to
* it contains the value asio::error::operation_aborted.
*/
-template <typename Clock, typename WaitTraits>
+template <typename Clock, typename WaitTraits, typename Executor>
class basic_waitable_timer
{
public:
/// The type of the executor associated with the object.
- typedef io_context::executor_type executor_type;
+ typedef Executor executor_type;
/// The clock type.
typedef Clock clock_type;
@@ -160,11 +162,28 @@
* expires_at() or expires_after() functions must be called to set an expiry
* time before the timer can be waited on.
*
- * @param io_context The io_context object that the timer will use to dispatch
+ * @param ex The executor object that the timer will use to dispatch
* handlers for any asynchronous operations performed on the timer.
*/
- explicit basic_waitable_timer(asio::io_context& io_context)
- : impl_(io_context)
+ explicit basic_waitable_timer(const executor_type& ex)
+ : impl_(ex)
+ {
+ }
+
+ /// Constructor.
+ /**
+ * This constructor creates a timer without setting an expiry time. The
+ * expires_at() or expires_after() functions must be called to set an expiry
+ * time before the timer can be waited on.
+ *
+ * @param context The execution context object that the timer will use to
+ * dispatch handlers for any asynchronous operations performed on the timer.
+ */
+ template <typename ExecutionContext,
+ typename = typename enable_if<is_convertible<
+ ExecutionContext&, execution_context&>::value>::type>
+ explicit basic_waitable_timer(ExecutionContext& context)
+ : impl_(context)
{
}
@@ -172,15 +191,36 @@
/**
* This constructor creates a timer and sets the expiry time.
*
- * @param io_context The io_context object that the timer will use to dispatch
+ * @param ex The executor object that the timer will use to dispatch
* handlers for any asynchronous operations performed on the timer.
*
* @param expiry_time The expiry time to be used for the timer, expressed
* as an absolute time.
*/
- basic_waitable_timer(asio::io_context& io_context,
+ basic_waitable_timer(const executor_type& ex, const time_point& expiry_time)
+ : impl_(ex)
+ {
+ asio::error_code ec;
+ impl_.get_service().expires_at(impl_.get_implementation(), expiry_time, ec);
+ asio::detail::throw_error(ec, "expires_at");
+ }
+
+ /// Constructor to set a particular expiry time as an absolute time.
+ /**
+ * This constructor creates a timer and sets the expiry time.
+ *
+ * @param context The execution context object that the timer will use to
+ * dispatch handlers for any asynchronous operations performed on the timer.
+ *
+ * @param expiry_time The expiry time to be used for the timer, expressed
+ * as an absolute time.
+ */
+ template <typename ExecutionContext,
+ typename = typename enable_if<is_convertible<
+ ExecutionContext&, execution_context&>::value>::type>
+ explicit basic_waitable_timer(ExecutionContext& context,
const time_point& expiry_time)
- : impl_(io_context)
+ : impl_(context)
{
asio::error_code ec;
impl_.get_service().expires_at(impl_.get_implementation(), expiry_time, ec);
@@ -191,15 +231,37 @@
/**
* This constructor creates a timer and sets the expiry time.
*
- * @param io_context The io_context object that the timer will use to dispatch
+ * @param ex The executor object that the timer will use to dispatch
* handlers for any asynchronous operations performed on the timer.
*
* @param expiry_time The expiry time to be used for the timer, relative to
* now.
*/
- basic_waitable_timer(asio::io_context& io_context,
+ basic_waitable_timer(const executor_type& ex, const duration& expiry_time)
+ : impl_(ex)
+ {
+ asio::error_code ec;
+ impl_.get_service().expires_after(
+ impl_.get_implementation(), expiry_time, ec);
+ asio::detail::throw_error(ec, "expires_after");
+ }
+
+ /// Constructor to set a particular expiry time relative to now.
+ /**
+ * This constructor creates a timer and sets the expiry time.
+ *
+ * @param ex The executor object that the timer will use to dispatch
+ * handlers for any asynchronous operations performed on the timer.
+ *
+ * @param expiry_time The expiry time to be used for the timer, relative to
+ * now.
+ */
+ template <typename ExecutionContext,
+ typename = typename enable_if<is_convertible<
+ ExecutionContext&, execution_context&>::value>::type>
+ explicit basic_waitable_timer(ExecutionContext& context,
const duration& expiry_time)
- : impl_(io_context)
+ : impl_(context)
{
asio::error_code ec;
impl_.get_service().expires_after(
@@ -663,7 +725,7 @@
void (asio::error_code)> init(handler);
impl_.get_service().async_wait(impl_.get_implementation(),
- init.completion_handler);
+ init.completion_handler, impl_.get_executor());
return init.result.get();
}
@@ -676,7 +738,8 @@
detail::io_object_impl<
detail::deadline_timer_service<
- detail::chrono_time_traits<Clock, WaitTraits> > > impl_;
+ detail::chrono_time_traits<Clock, WaitTraits> >,
+ executor_type > impl_;
};
} // namespace asio
diff --git a/asio/include/asio/detail/deadline_timer_service.hpp b/asio/include/asio/detail/deadline_timer_service.hpp
index f58a6e0..6ea503d 100644
--- a/asio/include/asio/detail/deadline_timer_service.hpp
+++ b/asio/include/asio/detail/deadline_timer_service.hpp
@@ -43,7 +43,7 @@
template <typename Time_Traits>
class deadline_timer_service
- : public service_base<deadline_timer_service<Time_Traits> >
+ : public execution_context_service_base<deadline_timer_service<Time_Traits> >
{
public:
// The time type.
@@ -63,9 +63,9 @@
};
// Constructor.
- deadline_timer_service(asio::io_context& io_context)
- : service_base<deadline_timer_service<Time_Traits> >(io_context),
- scheduler_(asio::use_service<timer_scheduler>(io_context))
+ deadline_timer_service(asio::execution_context& context)
+ : execution_context_service_base<deadline_timer_service<Time_Traits> >(context),
+ scheduler_(asio::use_service<timer_scheduler>(context))
{
scheduler_.init_task();
scheduler_.add_timer_queue(timer_queue_);
@@ -225,14 +225,14 @@
}
// Start an asynchronous wait on the timer.
- template <typename Handler>
- void async_wait(implementation_type& impl, Handler& handler)
+ template <typename Handler, typename IoExecutor>
+ void async_wait(implementation_type& impl, Handler& handler, const IoExecutor& io_ex)
{
// Allocate and construct an operation to wrap the handler.
- typedef wait_handler<Handler> op;
+ typedef wait_handler<Handler, IoExecutor> op;
typename op::ptr p = { asio::detail::addressof(handler),
op::ptr::allocate(handler), 0 };
- p.p = new (p.v) op(handler);
+ p.p = new (p.v) op(handler, io_ex);
impl.might_have_pending_waits = true;
diff --git a/asio/include/asio/detail/handler_work.hpp b/asio/include/asio/detail/handler_work.hpp
index cce5c4b..3b62d0d 100644
--- a/asio/include/asio/detail/handler_work.hpp
+++ b/asio/include/asio/detail/handler_work.hpp
@@ -27,24 +27,41 @@
// A helper class template to allow completion handlers to be dispatched
// through either the new executors framework or the old invocaton hook. The
// primary template uses the new executors framework.
-template <typename Handler, typename Executor
- = typename associated_executor<Handler>::type>
+template <typename Handler,
+ typename IoExecutor = system_executor, typename HandlerExecutor
+ = typename associated_executor<Handler, IoExecutor>::type>
class handler_work
{
public:
explicit handler_work(Handler& handler) ASIO_NOEXCEPT
- : executor_(associated_executor<Handler>::get(handler))
+ : io_executor_(),
+ executor_(asio::get_associated_executor(handler, io_executor_))
+ {
+ }
+
+ handler_work(Handler& handler, const IoExecutor& io_ex) ASIO_NOEXCEPT
+ : io_executor_(io_ex),
+ executor_(asio::get_associated_executor(handler, io_executor_))
{
}
static void start(Handler& handler) ASIO_NOEXCEPT
{
- Executor ex(associated_executor<Handler>::get(handler));
+ HandlerExecutor ex(asio::get_associated_executor(handler));
ex.on_work_started();
}
+ static void start(Handler& handler,
+ const IoExecutor& io_ex) ASIO_NOEXCEPT
+ {
+ HandlerExecutor ex(asio::get_associated_executor(handler, io_ex));
+ ex.on_work_started();
+ io_ex.on_work_started();
+ }
+
~handler_work()
{
+ io_executor_.on_work_finished();
executor_.on_work_finished();
}
@@ -52,7 +69,7 @@
void complete(Function& function, Handler& handler)
{
executor_.dispatch(ASIO_MOVE_CAST(Function)(function),
- associated_allocator<Handler>::get(handler));
+ asio::get_associated_allocator(handler));
}
private:
@@ -60,7 +77,8 @@
handler_work(const handler_work&);
handler_work& operator=(const handler_work&);
- typename associated_executor<Handler>::type executor_;
+ IoExecutor io_executor_;
+ HandlerExecutor executor_;
};
// This specialisation dispatches a handler through the old invocation hook.
@@ -68,7 +86,7 @@
// system_executor will dispatch through the hook anyway. However, by doing
// this we avoid an extra copy of the handler.
template <typename Handler>
-class handler_work<Handler, system_executor>
+class handler_work<Handler, system_executor, system_executor>
{
public:
explicit handler_work(Handler&) ASIO_NOEXCEPT {}
diff --git a/asio/include/asio/detail/impl/scheduler.ipp b/asio/include/asio/detail/impl/scheduler.ipp
index 35bc678..7a51c07 100644
--- a/asio/include/asio/detail/impl/scheduler.ipp
+++ b/asio/include/asio/detail/impl/scheduler.ipp
@@ -23,6 +23,7 @@
#include "asio/detail/reactor.hpp"
#include "asio/detail/scheduler.hpp"
#include "asio/detail/scheduler_thread_info.hpp"
+#include "asio/detail/signal_blocker.hpp"
#include "asio/detail/push_options.hpp"
@@ -84,8 +85,8 @@
thread_info* this_thread_;
};
-scheduler::scheduler(
- asio::execution_context& ctx, int concurrency_hint)
+scheduler::scheduler(asio::execution_context& ctx,
+ int concurrency_hint, bool own_thread)
: asio::detail::execution_context_service_base<scheduler>(ctx),
one_thread_(concurrency_hint == 1
|| !ASIO_CONCURRENCY_HINT_IS_LOCKING(
@@ -99,17 +100,44 @@
outstanding_work_(0),
stopped_(false),
shutdown_(false),
- concurrency_hint_(concurrency_hint)
+ concurrency_hint_(concurrency_hint),
+ thread_(0)
{
ASIO_HANDLER_TRACKING_INIT;
+
+ if (own_thread)
+ {
+ ++outstanding_work_;
+ asio::detail::signal_blocker sb;
+ thread_ = new asio::detail::thread([this] { asio::error_code ec; run(ec); });
+ }
+}
+
+scheduler::~scheduler()
+{
+ if (thread_)
+ {
+ thread_->join();
+ delete thread_;
+ }
}
void scheduler::shutdown()
{
mutex::scoped_lock lock(mutex_);
shutdown_ = true;
+ if (thread_)
+ stop_all_threads(lock);
lock.unlock();
+ // Join thread to ensure task operation is returned to queue.
+ if (thread_)
+ {
+ thread_->join();
+ delete thread_;
+ thread_ = 0;
+ }
+
// Destroy handler objects.
while (!op_queue_.empty())
{
diff --git a/asio/include/asio/detail/io_object_impl.hpp b/asio/include/asio/detail/io_object_impl.hpp
index 524392e..3705476 100644
--- a/asio/include/asio/detail/io_object_impl.hpp
+++ b/asio/include/asio/detail/io_object_impl.hpp
@@ -15,15 +15,19 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+#include <new>
#include "asio/detail/config.hpp"
+#include "asio/detail/type_traits.hpp"
#include "asio/io_context.hpp"
+#include "asio/is_executor.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace detail {
-template <typename IoObjectService>
+template <typename IoObjectService,
+ typename Executor = io_context::executor_type>
class io_object_impl
{
public:
@@ -34,11 +38,23 @@
typedef typename service_type::implementation_type implementation_type;
// The type of the executor associated with the object.
- typedef asio::io_context::executor_type executor_type;
+ typedef Executor executor_type;
- // Construct an I/O object.
- explicit io_object_impl(asio::io_context& io_context)
- : service_(&asio::use_service<IoObjectService>(io_context))
+ // Construct an I/O object using an executor.
+ explicit io_object_impl(const executor_type& ex)
+ : executor_(ex),
+ service_(&asio::use_service<IoObjectService>(ex.context()))
+ {
+ service_->construct(implementation_);
+ }
+
+ // Construct an I/O object using an execution context.
+ template <typename ExecutionContext>
+ explicit io_object_impl(ExecutionContext& context,
+ typename enable_if<is_convertible<
+ ExecutionContext&, execution_context&>::value>::type* = 0)
+ : executor_(context.get_executor()),
+ service_(&asio::use_service<IoObjectService>(context))
{
service_->construct(implementation_);
}
@@ -46,7 +62,8 @@
#if defined(ASIO_HAS_MOVE)
// Move-construct an I/O object.
io_object_impl(io_object_impl&& other)
- : service_(&other.get_service())
+ : executor_(other.get_executor()),
+ service_(&other.get_service())
{
service_->move_construct(implementation_, other.implementation_);
}
@@ -55,8 +72,9 @@
template <typename IoObjectService1>
io_object_impl(IoObjectService1& other_service,
typename IoObjectService1::implementation_type& other_implementation)
- : service_(&asio::use_service<IoObjectService>(
- other_service.get_io_context()))
+ : executor_(other_service.get_io_context().get_executor()), // TODO
+ service_(&asio::use_service<IoObjectService>(
+ other_service.get_io_context()))
{
service_->converting_move_construct(implementation_,
other_service, other_implementation);
@@ -73,9 +91,14 @@
// Move-assign an I/O object.
io_object_impl& operator=(io_object_impl&& other)
{
- service_->move_assign(implementation_,
- *other.service_, other.implementation_);
- service_ = other.service_;
+ if (this != &other)
+ {
+ service_->move_assign(implementation_,
+ *other.service_, other.implementation_);
+ executor_.~executor_type();
+ new (&executor_) executor_type(std::move(other.executor_));
+ service_ = other.service_;
+ }
return *this;
}
#endif // defined(ASIO_HAS_MOVE)
@@ -97,7 +120,7 @@
// Get the executor associated with the object.
executor_type get_executor() ASIO_NOEXCEPT
{
- return service_->get_io_context().get_executor();
+ return executor_;
}
// Get the service associated with the I/O object.
@@ -129,6 +152,9 @@
io_object_impl(const io_object_impl&);
io_object_impl& operator=(const io_object_impl&);
+ // The associated executor.
+ executor_type executor_;
+
// The service associated with the I/O object.
service_type* service_;
diff --git a/asio/include/asio/detail/scheduler.hpp b/asio/include/asio/detail/scheduler.hpp
index 10c29b7..eada5a8 100644
--- a/asio/include/asio/detail/scheduler.hpp
+++ b/asio/include/asio/detail/scheduler.hpp
@@ -25,6 +25,7 @@
#include "asio/detail/op_queue.hpp"
#include "asio/detail/reactor_fwd.hpp"
#include "asio/detail/scheduler_operation.hpp"
+#include "asio/detail/thread.hpp"
#include "asio/detail/thread_context.hpp"
#include "asio/detail/push_options.hpp"
@@ -44,7 +45,10 @@
// Constructor. Specifies the number of concurrent threads that are likely to
// run the scheduler. If set to 1 certain optimisation are performed.
ASIO_DECL scheduler(asio::execution_context& ctx,
- int concurrency_hint = 0);
+ int concurrency_hint = 0, bool own_thread = true);
+
+ // Destructor.
+ ASIO_DECL ~scheduler();
// Destroy all user-defined handler objects owned by the service.
ASIO_DECL void shutdown();
@@ -199,6 +203,9 @@
// The concurrency hint used to initialise the scheduler.
const int concurrency_hint_;
+
+ // The thread that is running the scheduler.
+ asio::detail::thread* thread_;
};
} // namespace detail
diff --git a/asio/include/asio/detail/thread_group.hpp b/asio/include/asio/detail/thread_group.hpp
index 1e400b0..f39f8e5 100644
--- a/asio/include/asio/detail/thread_group.hpp
+++ b/asio/include/asio/detail/thread_group.hpp
@@ -64,6 +64,12 @@
}
}
+ // Test whether the group is empty.
+ bool empty() const
+ {
+ return first_ == 0;
+ }
+
private:
// Structure used to track a single thread in the group.
struct item
diff --git a/asio/include/asio/detail/wait_handler.hpp b/asio/include/asio/detail/wait_handler.hpp
index bd3dc24..5053952 100644
--- a/asio/include/asio/detail/wait_handler.hpp
+++ b/asio/include/asio/detail/wait_handler.hpp
@@ -28,17 +28,18 @@
namespace asio {
namespace detail {
-template <typename Handler>
+template <typename Handler, typename IoExecutor>
class wait_handler : public wait_op
{
public:
ASIO_DEFINE_HANDLER_PTR(wait_handler);
- wait_handler(Handler& h)
+ wait_handler(Handler& h, const IoExecutor& ex)
: wait_op(&wait_handler::do_complete),
- handler_(ASIO_MOVE_CAST(Handler)(h))
+ handler_(ASIO_MOVE_CAST(Handler)(h)),
+ io_executor_(ex)
{
- handler_work<Handler>::start(handler_);
+ handler_work<Handler, IoExecutor>::start(handler_, io_executor_);
}
static void do_complete(void* owner, operation* base,
@@ -48,7 +49,7 @@
// Take ownership of the handler object.
wait_handler* h(static_cast<wait_handler*>(base));
ptr p = { asio::detail::addressof(h->handler_), h, h };
- handler_work<Handler> w(h->handler_);
+ handler_work<Handler, IoExecutor> w(h->handler_, h->io_executor_);
ASIO_HANDLER_COMPLETION((*h));
@@ -75,6 +76,7 @@
private:
Handler handler_;
+ IoExecutor io_executor_;
};
} // namespace detail
diff --git a/asio/include/asio/execution_context.hpp b/asio/include/asio/execution_context.hpp
index 1476d19..49c019c 100644
--- a/asio/include/asio/execution_context.hpp
+++ b/asio/include/asio/execution_context.hpp
@@ -109,13 +109,14 @@
class id;
class service;
-protected:
+public:
/// Constructor.
ASIO_DECL execution_context();
/// Destructor.
ASIO_DECL ~execution_context();
+protected:
/// Shuts down all services in the context.
/**
* This function is implemented as follows:
diff --git a/asio/include/asio/impl/io_context.ipp b/asio/include/asio/impl/io_context.ipp
index 7eb467d..9653ac8 100644
--- a/asio/include/asio/impl/io_context.ipp
+++ b/asio/include/asio/impl/io_context.ipp
@@ -34,13 +34,13 @@
namespace asio {
io_context::io_context()
- : impl_(add_impl(new impl_type(*this, ASIO_CONCURRENCY_HINT_DEFAULT)))
+ : impl_(add_impl(new impl_type(*this, ASIO_CONCURRENCY_HINT_DEFAULT, false)))
{
}
io_context::io_context(int concurrency_hint)
: impl_(add_impl(new impl_type(*this, concurrency_hint == 1
- ? ASIO_CONCURRENCY_HINT_1 : concurrency_hint)))
+ ? ASIO_CONCURRENCY_HINT_1 : concurrency_hint, false)))
{
}
diff --git a/asio/include/asio/impl/system_context.ipp b/asio/include/asio/impl/system_context.ipp
index 8ad5e41..ef36681 100644
--- a/asio/include/asio/impl/system_context.ipp
+++ b/asio/include/asio/impl/system_context.ipp
@@ -34,7 +34,7 @@
};
system_context::system_context()
- : scheduler_(use_service<detail::scheduler>(*this))
+ : scheduler_(add_scheduler(new detail::scheduler(*this, 0, false)))
{
scheduler_.work_started();
@@ -66,6 +66,13 @@
threads_.join();
}
+detail::scheduler& system_context::add_scheduler(detail::scheduler* s)
+{
+ detail::scoped_ptr<detail::scheduler> scoped_impl(s);
+ asio::add_service<detail::scheduler>(*this, scoped_impl.get());
+ return *scoped_impl.release();
+}
+
} // namespace asio
#include "asio/detail/pop_options.hpp"
diff --git a/asio/include/asio/impl/thread_pool.ipp b/asio/include/asio/impl/thread_pool.ipp
index 89583c1..63a35a7 100644
--- a/asio/include/asio/impl/thread_pool.ipp
+++ b/asio/include/asio/impl/thread_pool.ipp
@@ -34,7 +34,7 @@
};
thread_pool::thread_pool()
- : scheduler_(use_service<detail::scheduler>(*this))
+ : scheduler_(add_scheduler(new detail::scheduler(*this, 0, false)))
{
scheduler_.work_started();
@@ -44,7 +44,8 @@
}
thread_pool::thread_pool(std::size_t num_threads)
- : scheduler_(use_service<detail::scheduler>(*this))
+ : scheduler_(add_scheduler(new detail::scheduler(*this, num_threads == 1
+ ? ASIO_CONCURRENCY_HINT_1 : num_threads, false)))
{
scheduler_.work_started();
@@ -65,8 +66,18 @@
void thread_pool::join()
{
- scheduler_.work_finished();
- threads_.join();
+ if (!threads_.empty())
+ {
+ scheduler_.work_finished();
+ threads_.join();
+ }
+}
+
+detail::scheduler& thread_pool::add_scheduler(detail::scheduler* s)
+{
+ detail::scoped_ptr<detail::scheduler> scoped_impl(s);
+ asio::add_service<detail::scheduler>(*this, scoped_impl.get());
+ return *scoped_impl.release();
}
} // namespace asio
diff --git a/asio/include/asio/system_context.hpp b/asio/include/asio/system_context.hpp
index ccd1113..134c9e9 100644
--- a/asio/include/asio/system_context.hpp
+++ b/asio/include/asio/system_context.hpp
@@ -59,6 +59,9 @@
struct thread_function;
+ // Helper function to create the underlying scheduler.
+ ASIO_DECL detail::scheduler& add_scheduler(detail::scheduler* s);
+
// The underlying scheduler.
detail::scheduler& scheduler_;
diff --git a/asio/include/asio/thread_pool.hpp b/asio/include/asio/thread_pool.hpp
index f22f18d..2c098f2 100644
--- a/asio/include/asio/thread_pool.hpp
+++ b/asio/include/asio/thread_pool.hpp
@@ -100,6 +100,9 @@
friend class executor_type;
struct thread_function;
+ // Helper function to create the underlying scheduler.
+ ASIO_DECL detail::scheduler& add_scheduler(detail::scheduler* s);
+
// The underlying scheduler.
detail::scheduler& scheduler_;
diff --git a/asio/include/asio/ts/netfwd.hpp b/asio/include/asio/ts/netfwd.hpp
index 80d7b60..1feb8e0 100644
--- a/asio/include/asio/ts/netfwd.hpp
+++ b/asio/include/asio/ts/netfwd.hpp
@@ -62,7 +62,8 @@
#define ASIO_BASIC_WAITABLE_TIMER_FWD_DECL
template <typename Clock,
- typename WaitTraits = asio::wait_traits<Clock> >
+ typename WaitTraits = wait_traits<Clock>,
+ typename Executor = executor>
class basic_waitable_timer;
#endif // !defined(ASIO_BASIC_WAITABLE_TIMER_FWD_DECL)
diff --git a/asio/src/examples/cpp11/invocation/prioritised_handlers.cpp b/asio/src/examples/cpp11/invocation/prioritised_handlers.cpp
index 1619875..81490c7 100644
--- a/asio/src/examples/cpp11/invocation/prioritised_handlers.cpp
+++ b/asio/src/examples/cpp11/invocation/prioritised_handlers.cpp
@@ -15,7 +15,7 @@
using asio::ip::tcp;
-class handler_priority_queue : asio::execution_context
+class handler_priority_queue : public asio::execution_context
{
public:
template <typename Function>