5
// Copyright (c) 2003-2008 Christopher M. Kohlhoff (chris at kohlhoff dot com)
7
// Distributed under the Boost Software License, Version 1.0. (See accompanying
8
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
11
#ifndef ASIO_DETAIL_STRAND_SERVICE_HPP
12
#define ASIO_DETAIL_STRAND_SERVICE_HPP
14
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
16
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
18
#include "asio/detail/push_options.hpp"
20
#include "asio/detail/push_options.hpp"
21
#include <boost/aligned_storage.hpp>
22
#include <boost/assert.hpp>
23
#include <boost/detail/atomic_count.hpp>
24
#include <boost/intrusive_ptr.hpp>
25
#include "asio/detail/pop_options.hpp"
27
#include "asio/io_service.hpp"
28
#include "asio/detail/bind_handler.hpp"
29
#include "asio/detail/call_stack.hpp"
30
#include "asio/detail/handler_alloc_helpers.hpp"
31
#include "asio/detail/handler_invoke_helpers.hpp"
32
#include "asio/detail/mutex.hpp"
33
#include "asio/detail/noncopyable.hpp"
34
#include "asio/detail/service_base.hpp"
39
// Default service implementation for a strand.
41
: public asio::detail::service_base<strand_service>
45
class invoke_current_handler;
46
class post_next_waiter_on_exit;
48
// The underlying implementation of a strand.
51
#if defined (__BORLANDC__)
63
if (--ref_count_ == 0)
68
// Only this service will have access to the internal values.
69
friend class strand_service;
70
friend class post_next_waiter_on_exit;
71
friend class invoke_current_handler;
73
strand_impl(strand_service& owner)
80
// Insert implementation into linked list of all implementations.
81
asio::detail::mutex::scoped_lock lock(owner_.mutex_);
82
next_ = owner_.impl_list_;
84
if (owner_.impl_list_)
85
owner_.impl_list_->prev_ = this;
86
owner_.impl_list_ = this;
91
// Remove implementation from linked list of all implementations.
92
asio::detail::mutex::scoped_lock lock(owner_.mutex_);
93
if (owner_.impl_list_ == this)
94
owner_.impl_list_ = next_;
103
if (current_handler_)
105
current_handler_->destroy();
108
while (first_waiter_)
110
handler_base* next = first_waiter_->next_;
111
first_waiter_->destroy();
112
first_waiter_ = next;
116
// Mutex to protect access to internal data.
117
asio::detail::mutex mutex_;
119
// The service that owns this implementation.
120
strand_service& owner_;
122
// The handler that is ready to execute. If this pointer is non-null then it
123
// indicates that a handler holds the lock.
124
handler_base* current_handler_;
126
// The start of the list of waiting handlers for the strand.
127
handler_base* first_waiter_;
129
// The end of the list of waiting handlers for the strand.
130
handler_base* last_waiter_;
132
// Storage for posted handlers.
133
typedef boost::aligned_storage<128> handler_storage_type;
134
#if defined(__BORLANDC__)
135
boost::aligned_storage<128> handler_storage_;
137
handler_storage_type handler_storage_;
140
// Pointers to adjacent socket implementations in linked list.
144
// The reference count on the strand implementation.
145
boost::detail::atomic_count ref_count_;
147
#if !defined(__BORLANDC__)
148
friend void intrusive_ptr_add_ref(strand_impl* p)
153
friend void intrusive_ptr_release(strand_impl* p)
160
friend class strand_impl;
162
typedef boost::intrusive_ptr<strand_impl> implementation_type;
164
// Base class for all handler types.
168
typedef void (*invoke_func_type)(handler_base*,
169
strand_service&, implementation_type&);
170
typedef void (*destroy_func_type)(handler_base*);
172
handler_base(invoke_func_type invoke_func, destroy_func_type destroy_func)
174
invoke_func_(invoke_func),
175
destroy_func_(destroy_func)
179
void invoke(strand_service& service_impl, implementation_type& impl)
181
invoke_func_(this, service_impl, impl);
195
friend class strand_service;
196
friend class strand_impl;
197
friend class post_next_waiter_on_exit;
199
invoke_func_type invoke_func_;
200
destroy_func_type destroy_func_;
203
// Helper class to allow handlers to be dispatched.
204
class invoke_current_handler
207
invoke_current_handler(strand_service& service_impl,
208
const implementation_type& impl)
209
: service_impl_(service_impl),
216
impl_->current_handler_->invoke(service_impl_, impl_);
219
friend void* asio_handler_allocate(std::size_t size,
220
invoke_current_handler* this_handler)
222
return this_handler->do_handler_allocate(size);
225
friend void asio_handler_deallocate(void*, std::size_t,
226
invoke_current_handler*)
230
void* do_handler_allocate(std::size_t size)
232
#if defined(__BORLANDC__)
233
BOOST_ASSERT(size <= boost::aligned_storage<128>::size);
235
BOOST_ASSERT(size <= strand_impl::handler_storage_type::size);
238
return impl_->handler_storage_.address();
241
// The asio_handler_invoke hook is not defined here since the default one
242
// provides the correct behaviour, and including it here breaks MSVC 7.1
243
// in some situations.
246
strand_service& service_impl_;
247
implementation_type impl_;
250
// Helper class to automatically enqueue next waiter on block exit.
251
class post_next_waiter_on_exit
254
post_next_waiter_on_exit(strand_service& service_impl,
255
implementation_type& impl)
256
: service_impl_(service_impl),
262
~post_next_waiter_on_exit()
266
asio::detail::mutex::scoped_lock lock(impl_->mutex_);
267
impl_->current_handler_ = impl_->first_waiter_;
268
if (impl_->current_handler_)
270
impl_->first_waiter_ = impl_->first_waiter_->next_;
271
if (impl_->first_waiter_ == 0)
272
impl_->last_waiter_ = 0;
274
service_impl_.get_io_service().post(
275
invoke_current_handler(service_impl_, impl_));
286
strand_service& service_impl_;
287
implementation_type& impl_;
291
// Class template for a waiter.
292
template <typename Handler>
293
class handler_wrapper
294
: public handler_base
297
handler_wrapper(Handler handler)
298
: handler_base(&handler_wrapper<Handler>::do_invoke,
299
&handler_wrapper<Handler>::do_destroy),
304
static void do_invoke(handler_base* base,
305
strand_service& service_impl, implementation_type& impl)
307
// Take ownership of the handler object.
308
typedef handler_wrapper<Handler> this_type;
309
this_type* h(static_cast<this_type*>(base));
310
typedef handler_alloc_traits<Handler, this_type> alloc_traits;
311
handler_ptr<alloc_traits> ptr(h->handler_, h);
313
post_next_waiter_on_exit p1(service_impl, impl);
315
// Make a copy of the handler so that the memory can be deallocated before
316
// the upcall is made.
317
Handler handler(h->handler_);
319
// A handler object must still be valid when the next waiter is posted
320
// since destroying the last handler might cause the strand object to be
321
// destroyed. Therefore we create a second post_next_waiter_on_exit object
322
// that will be destroyed before the handler object.
324
post_next_waiter_on_exit p2(service_impl, impl);
326
// Free the memory associated with the handler.
329
// Indicate that this strand is executing on the current thread.
330
call_stack<strand_impl>::context ctx(impl.get());
333
asio_handler_invoke_helpers::invoke(handler, &handler);
336
static void do_destroy(handler_base* base)
338
// Take ownership of the handler object.
339
typedef handler_wrapper<Handler> this_type;
340
this_type* h(static_cast<this_type*>(base));
341
typedef handler_alloc_traits<Handler, this_type> alloc_traits;
342
handler_ptr<alloc_traits> ptr(h->handler_, h);
344
// A sub-object of the handler may be the true owner of the memory
345
// associated with the handler. Consequently, a local copy of the handler
346
// is required to ensure that any owning sub-object remains valid until
347
// after we have deallocated the memory here.
348
Handler handler(h->handler_);
351
// Free the memory associated with the handler.
359
// Construct a new strand service for the specified io_service.
360
explicit strand_service(asio::io_service& io_service)
361
: asio::detail::service_base<strand_service>(io_service),
367
// Destroy all user-defined handler objects owned by the service.
368
void shutdown_service()
370
// Construct a list of all handlers to be destroyed.
371
asio::detail::mutex::scoped_lock lock(mutex_);
372
strand_impl* impl = impl_list_;
373
handler_base* first_handler = 0;
376
if (impl->current_handler_)
378
impl->current_handler_->next_ = first_handler;
379
first_handler = impl->current_handler_;
380
impl->current_handler_ = 0;
382
if (impl->first_waiter_)
384
impl->last_waiter_->next_ = first_handler;
385
first_handler = impl->first_waiter_;
386
impl->first_waiter_ = 0;
387
impl->last_waiter_ = 0;
392
// Destroy all handlers without holding the lock.
394
while (first_handler)
396
handler_base* next = first_handler->next_;
397
first_handler->destroy();
398
first_handler = next;
402
// Construct a new strand implementation.
403
void construct(implementation_type& impl)
405
impl = implementation_type(new strand_impl(*this));
408
// Destroy a strand implementation.
409
void destroy(implementation_type& impl)
411
implementation_type().swap(impl);
414
// Request the io_service to invoke the given handler.
415
template <typename Handler>
416
void dispatch(implementation_type& impl, Handler handler)
418
if (call_stack<strand_impl>::contains(impl.get()))
420
asio_handler_invoke_helpers::invoke(handler, &handler);
424
// Allocate and construct an object to wrap the handler.
425
typedef handler_wrapper<Handler> value_type;
426
typedef handler_alloc_traits<Handler, value_type> alloc_traits;
427
raw_handler_ptr<alloc_traits> raw_ptr(handler);
428
handler_ptr<alloc_traits> ptr(raw_ptr, handler);
430
asio::detail::mutex::scoped_lock lock(impl->mutex_);
432
if (impl->current_handler_ == 0)
434
// This handler now has the lock, so can be dispatched immediately.
435
impl->current_handler_ = ptr.release();
437
this->get_io_service().dispatch(invoke_current_handler(*this, impl));
441
// Another handler already holds the lock, so this handler must join
442
// the list of waiters. The handler will be posted automatically when
444
if (impl->last_waiter_)
446
impl->last_waiter_->next_ = ptr.get();
447
impl->last_waiter_ = impl->last_waiter_->next_;
451
impl->first_waiter_ = ptr.get();
452
impl->last_waiter_ = ptr.get();
459
// Request the io_service to invoke the given handler and return immediately.
460
template <typename Handler>
461
void post(implementation_type& impl, Handler handler)
463
// Allocate and construct an object to wrap the handler.
464
typedef handler_wrapper<Handler> value_type;
465
typedef handler_alloc_traits<Handler, value_type> alloc_traits;
466
raw_handler_ptr<alloc_traits> raw_ptr(handler);
467
handler_ptr<alloc_traits> ptr(raw_ptr, handler);
469
asio::detail::mutex::scoped_lock lock(impl->mutex_);
471
if (impl->current_handler_ == 0)
473
// This handler now has the lock, so can be dispatched immediately.
474
impl->current_handler_ = ptr.release();
476
this->get_io_service().post(invoke_current_handler(*this, impl));
480
// Another handler already holds the lock, so this handler must join the
481
// list of waiters. The handler will be posted automatically when its turn
483
if (impl->last_waiter_)
485
impl->last_waiter_->next_ = ptr.get();
486
impl->last_waiter_ = impl->last_waiter_->next_;
490
impl->first_waiter_ = ptr.get();
491
impl->last_waiter_ = ptr.get();
498
// Mutex to protect access to the linked list of implementations.
499
asio::detail::mutex mutex_;
501
// The head of a linked list of all implementations.
502
strand_impl* impl_list_;
505
} // namespace detail
508
#if defined(__BORLANDC__)
512
inline void intrusive_ptr_add_ref(
513
asio::detail::strand_service::strand_impl* p)
518
inline void intrusive_ptr_release(
519
asio::detail::strand_service::strand_impl* p)
526
#endif // defined(__BORLANDC__)
528
#include "asio/detail/pop_options.hpp"
530
#endif // ASIO_DETAIL_STRAND_SERVICE_HPP