Can Boost.Asio strand.dispatch() block?
I'm experimenting with Boost.Asio strand
for a serve开发者_开发知识库r I'm writing and wanted to clarify a few things.
Let's assume we have SomeClass
with something like this:
void SomeClass::foo()
{
strand_.dispatch(boost::bind(&SomeClass::bar, this));
}
Further, the strand
has an io_service
with multiple threads calling run()
.
In the documentation on strand::dispatch()
I read it guarantees that handlers posted or dispatched through the strand will not execute concurrently and if this is met the handler may execute in this function. What decides if the handler gets to be executed immediately?
In this case with multiple threads, will dispatch block if multiple io_service threads call SomeClass::foo
concurrently? I.e. block for all but the one that gets executed. If so, is the handlers executed in order (the order they called dispatch()
)?
Logically, it would be impossible for this guarantee to be met without some possibility of (b)locking. The code for dispatch
(in detail/impl/strand_service.hpp from 1.46.1) confirms this. That's the nice thing about Boost, it's all there for you to see. There is info on order of handler invocation in the docs here, including a comment that sometimes no ordering guarantee is provided.
template <typename Handler>
void strand_service::dispatch(strand_service::implementation_type& impl,
Handler handler)
{
// If we are already in the strand then the handler can run immediately.
if (call_stack<strand_impl>::contains(impl))
{
boost::asio::detail::fenced_block b;
boost_asio_handler_invoke_helpers::invoke(handler, handler);
return;
}
// Allocate and construct an operation to wrap the handler.
typedef completion_handler<Handler> op;
typename op::ptr p = { boost::addressof(handler),
boost_asio_handler_alloc_helpers::allocate(
sizeof(op), handler), 0 };
p.p = new (p.v) op(handler);
// If we are running inside the io_service, and no other handler is queued
// or running, then the handler can run immediately.
bool can_dispatch = call_stack<io_service_impl>::contains(&io_service_);
impl->mutex_.lock();
bool first = (++impl->count_ == 1);
if (can_dispatch && first)
{
// Immediate invocation is allowed.
impl->mutex_.unlock();
// Memory must be releaesed before any upcall is made.
p.reset();
// Indicate that this strand is executing on the current thread.
call_stack<strand_impl>::context ctx(impl);
// Ensure the next handler, if any, is scheduled on block exit.
on_dispatch_exit on_exit = { &io_service_, impl };
(void)on_exit;
boost::asio::detail::fenced_block b;
boost_asio_handler_invoke_helpers::invoke(handler, handler);
return;
}
// Immediate invocation is not allowed, so enqueue for later.
impl->queue_.push(p.p);
impl->mutex_.unlock();
p.v = p.p = 0;
// The first handler to be enqueued is responsible for scheduling the
// strand.
if (first)
io_service_.post_immediate_completion(impl);
}
精彩评论