fix doc example typo
[boost.git] / boost / asio / detail / strand_service.hpp
blob2c89a6100ba64b257ede40cfc1312b7cf81a1c97
1 //
2 // strand_service.hpp
3 // ~~~~~~~~~~~~~~~~~~
4 //
5 // Copyright (c) 2003-2008 Christopher M. Kohlhoff (chris at kohlhoff dot com)
6 //
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)
9 //
11 #ifndef BOOST_ASIO_DETAIL_STRAND_SERVICE_HPP
12 #define BOOST_ASIO_DETAIL_STRAND_SERVICE_HPP
14 #if defined(_MSC_VER) && (_MSC_VER >= 1200)
15 # pragma once
16 #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
18 #include <boost/asio/detail/push_options.hpp>
20 #include <boost/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 <boost/asio/detail/pop_options.hpp>
27 #include <boost/asio/io_service.hpp>
28 #include <boost/asio/detail/bind_handler.hpp>
29 #include <boost/asio/detail/call_stack.hpp>
30 #include <boost/asio/detail/handler_alloc_helpers.hpp>
31 #include <boost/asio/detail/handler_invoke_helpers.hpp>
32 #include <boost/asio/detail/mutex.hpp>
33 #include <boost/asio/detail/noncopyable.hpp>
34 #include <boost/asio/detail/service_base.hpp>
36 namespace boost {
37 namespace asio {
38 namespace detail {
40 // Default service implementation for a strand.
41 class strand_service
42 : public boost::asio::detail::service_base<strand_service>
44 public:
45 class handler_base;
46 class invoke_current_handler;
47 class post_next_waiter_on_exit;
49 // The underlying implementation of a strand.
50 class strand_impl
52 #if defined (__BORLANDC__)
53 public:
54 #else
55 private:
56 #endif
57 void add_ref()
59 ++ref_count_;
62 void release()
64 if (--ref_count_ == 0)
65 delete this;
68 private:
69 // Only this service will have access to the internal values.
70 friend class strand_service;
71 friend class post_next_waiter_on_exit;
72 friend class invoke_current_handler;
74 strand_impl(strand_service& owner)
75 : owner_(owner),
76 current_handler_(0),
77 first_waiter_(0),
78 last_waiter_(0),
79 ref_count_(0)
81 // Insert implementation into linked list of all implementations.
82 boost::asio::detail::mutex::scoped_lock lock(owner_.mutex_);
83 next_ = owner_.impl_list_;
84 prev_ = 0;
85 if (owner_.impl_list_)
86 owner_.impl_list_->prev_ = this;
87 owner_.impl_list_ = this;
90 ~strand_impl()
92 // Remove implementation from linked list of all implementations.
93 boost::asio::detail::mutex::scoped_lock lock(owner_.mutex_);
94 if (owner_.impl_list_ == this)
95 owner_.impl_list_ = next_;
96 if (prev_)
97 prev_->next_ = next_;
98 if (next_)
99 next_->prev_= prev_;
100 next_ = 0;
101 prev_ = 0;
102 lock.unlock();
104 if (current_handler_)
106 current_handler_->destroy();
109 while (first_waiter_)
111 handler_base* next = first_waiter_->next_;
112 first_waiter_->destroy();
113 first_waiter_ = next;
117 // Mutex to protect access to internal data.
118 boost::asio::detail::mutex mutex_;
120 // The service that owns this implementation.
121 strand_service& owner_;
123 // The handler that is ready to execute. If this pointer is non-null then it
124 // indicates that a handler holds the lock.
125 handler_base* current_handler_;
127 // The start of the list of waiting handlers for the strand.
128 handler_base* first_waiter_;
130 // The end of the list of waiting handlers for the strand.
131 handler_base* last_waiter_;
133 // Storage for posted handlers.
134 typedef boost::aligned_storage<128> handler_storage_type;
135 #if defined(__BORLANDC__)
136 boost::aligned_storage<128> handler_storage_;
137 #else
138 handler_storage_type handler_storage_;
139 #endif
141 // Pointers to adjacent socket implementations in linked list.
142 strand_impl* next_;
143 strand_impl* prev_;
145 // The reference count on the strand implementation.
146 boost::detail::atomic_count ref_count_;
148 #if !defined(__BORLANDC__)
149 friend void intrusive_ptr_add_ref(strand_impl* p)
151 p->add_ref();
154 friend void intrusive_ptr_release(strand_impl* p)
156 p->release();
158 #endif
161 friend class strand_impl;
163 typedef boost::intrusive_ptr<strand_impl> implementation_type;
165 // Base class for all handler types.
166 class handler_base
168 public:
169 typedef void (*invoke_func_type)(handler_base*,
170 strand_service&, implementation_type&);
171 typedef void (*destroy_func_type)(handler_base*);
173 handler_base(invoke_func_type invoke_func, destroy_func_type destroy_func)
174 : next_(0),
175 invoke_func_(invoke_func),
176 destroy_func_(destroy_func)
180 void invoke(strand_service& service_impl, implementation_type& impl)
182 invoke_func_(this, service_impl, impl);
185 void destroy()
187 destroy_func_(this);
190 protected:
191 ~handler_base()
195 private:
196 friend class strand_service;
197 friend class strand_impl;
198 friend class post_next_waiter_on_exit;
199 handler_base* next_;
200 invoke_func_type invoke_func_;
201 destroy_func_type destroy_func_;
204 // Helper class to allow handlers to be dispatched.
205 class invoke_current_handler
207 public:
208 invoke_current_handler(strand_service& service_impl,
209 const implementation_type& impl)
210 : service_impl_(service_impl),
211 impl_(impl)
215 void operator()()
217 impl_->current_handler_->invoke(service_impl_, impl_);
220 friend void* asio_handler_allocate(std::size_t size,
221 invoke_current_handler* this_handler)
223 return this_handler->do_handler_allocate(size);
226 friend void asio_handler_deallocate(void*, std::size_t,
227 invoke_current_handler*)
231 void* do_handler_allocate(std::size_t size)
233 #if defined(__BORLANDC__)
234 BOOST_ASSERT(size <= boost::aligned_storage<128>::size);
235 #else
236 BOOST_ASSERT(size <= strand_impl::handler_storage_type::size);
237 #endif
238 (void)size;
239 return impl_->handler_storage_.address();
242 // The asio_handler_invoke hook is not defined here since the default one
243 // provides the correct behaviour, and including it here breaks MSVC 7.1
244 // in some situations.
246 private:
247 strand_service& service_impl_;
248 implementation_type impl_;
251 // Helper class to automatically enqueue next waiter on block exit.
252 class post_next_waiter_on_exit
254 public:
255 post_next_waiter_on_exit(strand_service& service_impl,
256 implementation_type& impl)
257 : service_impl_(service_impl),
258 impl_(impl),
259 cancelled_(false)
263 ~post_next_waiter_on_exit()
265 if (!cancelled_)
267 boost::asio::detail::mutex::scoped_lock lock(impl_->mutex_);
268 impl_->current_handler_ = impl_->first_waiter_;
269 if (impl_->current_handler_)
271 impl_->first_waiter_ = impl_->first_waiter_->next_;
272 if (impl_->first_waiter_ == 0)
273 impl_->last_waiter_ = 0;
274 lock.unlock();
275 service_impl_.get_io_service().post(
276 invoke_current_handler(service_impl_, impl_));
281 void cancel()
283 cancelled_ = true;
286 private:
287 strand_service& service_impl_;
288 implementation_type& impl_;
289 bool cancelled_;
292 // Class template for a waiter.
293 template <typename Handler>
294 class handler_wrapper
295 : public handler_base
297 public:
298 handler_wrapper(Handler handler)
299 : handler_base(&handler_wrapper<Handler>::do_invoke,
300 &handler_wrapper<Handler>::do_destroy),
301 handler_(handler)
305 static void do_invoke(handler_base* base,
306 strand_service& service_impl, implementation_type& impl)
308 // Take ownership of the handler object.
309 typedef handler_wrapper<Handler> this_type;
310 this_type* h(static_cast<this_type*>(base));
311 typedef handler_alloc_traits<Handler, this_type> alloc_traits;
312 handler_ptr<alloc_traits> ptr(h->handler_, h);
314 post_next_waiter_on_exit p1(service_impl, impl);
316 // Make a copy of the handler so that the memory can be deallocated before
317 // the upcall is made.
318 Handler handler(h->handler_);
320 // A handler object must still be valid when the next waiter is posted
321 // since destroying the last handler might cause the strand object to be
322 // destroyed. Therefore we create a second post_next_waiter_on_exit object
323 // that will be destroyed before the handler object.
324 p1.cancel();
325 post_next_waiter_on_exit p2(service_impl, impl);
327 // Free the memory associated with the handler.
328 ptr.reset();
330 // Indicate that this strand is executing on the current thread.
331 call_stack<strand_impl>::context ctx(impl.get());
333 // Make the upcall.
334 boost_asio_handler_invoke_helpers::invoke(handler, &handler);
337 static void do_destroy(handler_base* base)
339 // Take ownership of the handler object.
340 typedef handler_wrapper<Handler> this_type;
341 this_type* h(static_cast<this_type*>(base));
342 typedef handler_alloc_traits<Handler, this_type> alloc_traits;
343 handler_ptr<alloc_traits> ptr(h->handler_, h);
345 // A sub-object of the handler may be the true owner of the memory
346 // associated with the handler. Consequently, a local copy of the handler
347 // is required to ensure that any owning sub-object remains valid until
348 // after we have deallocated the memory here.
349 Handler handler(h->handler_);
350 (void)handler;
352 // Free the memory associated with the handler.
353 ptr.reset();
356 private:
357 Handler handler_;
360 // Construct a new strand service for the specified io_service.
361 explicit strand_service(boost::asio::io_service& io_service)
362 : boost::asio::detail::service_base<strand_service>(io_service),
363 mutex_(),
364 impl_list_(0)
368 // Destroy all user-defined handler objects owned by the service.
369 void shutdown_service()
371 // Construct a list of all handlers to be destroyed.
372 boost::asio::detail::mutex::scoped_lock lock(mutex_);
373 strand_impl* impl = impl_list_;
374 handler_base* first_handler = 0;
375 while (impl)
377 if (impl->current_handler_)
379 impl->current_handler_->next_ = first_handler;
380 first_handler = impl->current_handler_;
381 impl->current_handler_ = 0;
383 if (impl->first_waiter_)
385 impl->last_waiter_->next_ = first_handler;
386 first_handler = impl->first_waiter_;
387 impl->first_waiter_ = 0;
388 impl->last_waiter_ = 0;
390 impl = impl->next_;
393 // Destroy all handlers without holding the lock.
394 lock.unlock();
395 while (first_handler)
397 handler_base* next = first_handler->next_;
398 first_handler->destroy();
399 first_handler = next;
403 // Construct a new strand implementation.
404 void construct(implementation_type& impl)
406 impl = implementation_type(new strand_impl(*this));
409 // Destroy a strand implementation.
410 void destroy(implementation_type& impl)
412 implementation_type().swap(impl);
415 // Request the io_service to invoke the given handler.
416 template <typename Handler>
417 void dispatch(implementation_type& impl, Handler handler)
419 if (call_stack<strand_impl>::contains(impl.get()))
421 boost_asio_handler_invoke_helpers::invoke(handler, &handler);
423 else
425 // Allocate and construct an object to wrap the handler.
426 typedef handler_wrapper<Handler> value_type;
427 typedef handler_alloc_traits<Handler, value_type> alloc_traits;
428 raw_handler_ptr<alloc_traits> raw_ptr(handler);
429 handler_ptr<alloc_traits> ptr(raw_ptr, handler);
431 boost::asio::detail::mutex::scoped_lock lock(impl->mutex_);
433 if (impl->current_handler_ == 0)
435 // This handler now has the lock, so can be dispatched immediately.
436 impl->current_handler_ = ptr.release();
437 lock.unlock();
438 this->get_io_service().dispatch(invoke_current_handler(*this, impl));
440 else
442 // Another handler already holds the lock, so this handler must join
443 // the list of waiters. The handler will be posted automatically when
444 // its turn comes.
445 if (impl->last_waiter_)
447 impl->last_waiter_->next_ = ptr.get();
448 impl->last_waiter_ = impl->last_waiter_->next_;
450 else
452 impl->first_waiter_ = ptr.get();
453 impl->last_waiter_ = ptr.get();
455 ptr.release();
460 // Request the io_service to invoke the given handler and return immediately.
461 template <typename Handler>
462 void post(implementation_type& impl, Handler handler)
464 // Allocate and construct an object to wrap the handler.
465 typedef handler_wrapper<Handler> value_type;
466 typedef handler_alloc_traits<Handler, value_type> alloc_traits;
467 raw_handler_ptr<alloc_traits> raw_ptr(handler);
468 handler_ptr<alloc_traits> ptr(raw_ptr, handler);
470 boost::asio::detail::mutex::scoped_lock lock(impl->mutex_);
472 if (impl->current_handler_ == 0)
474 // This handler now has the lock, so can be dispatched immediately.
475 impl->current_handler_ = ptr.release();
476 lock.unlock();
477 this->get_io_service().post(invoke_current_handler(*this, impl));
479 else
481 // Another handler already holds the lock, so this handler must join the
482 // list of waiters. The handler will be posted automatically when its turn
483 // comes.
484 if (impl->last_waiter_)
486 impl->last_waiter_->next_ = ptr.get();
487 impl->last_waiter_ = impl->last_waiter_->next_;
489 else
491 impl->first_waiter_ = ptr.get();
492 impl->last_waiter_ = ptr.get();
494 ptr.release();
498 private:
499 // Mutex to protect access to the linked list of implementations.
500 boost::asio::detail::mutex mutex_;
502 // The head of a linked list of all implementations.
503 strand_impl* impl_list_;
506 } // namespace detail
507 } // namespace asio
508 } // namespace boost
510 #if defined(__BORLANDC__)
512 namespace boost {
514 inline void intrusive_ptr_add_ref(
515 boost::asio::detail::strand_service::strand_impl* p)
517 p->add_ref();
520 inline void intrusive_ptr_release(
521 boost::asio::detail::strand_service::strand_impl* p)
523 p->release();
526 } // namespace boost
528 #endif // defined(__BORLANDC__)
530 #include <boost/asio/detail/pop_options.hpp>
532 #endif // BOOST_ASIO_DETAIL_STRAND_SERVICE_HPP