2 / Copyright (c) 2014-2015 Vicente J. Botet Escriba
4 / Distributed under the Boost Software License, Version 1.0. (See accompanying
5 / file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
8 [//////////////////////////////////////////////////////////]
9 [section:executors Executors and Schedulers -- EXPERIMENTAL]
11 [warning These features are experimental and subject to change in future versions. There are not too much tests yet, so it is possible that you can find out some trivial bugs :(]
13 [note These features are based on the [@http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3785.pdf [*N3785 - Executors and Schedulers revision 3]] C++1y proposal from Chris Mysen, Niklas Gustafsson, Matt Austern, Jeffrey Yasskin. The text that follows has been adapted from this paper to show the differences.]
15 Executors are objects that can execute units of work packaged as function objects. Boost.Thread differs from N3785 mainly in the an Executor doesn't needs to inherit from an abstract class Executor. Static polymorphism is used instead and type erasure is used internally.
17 [////////////////////]
18 [section Introduction]
20 Multithreaded programs often involve discrete (sometimes small) units of work that are executed asynchronously. This often involves passing work units to some component that manages execution. We already have boost::async, which potentially executes a function asynchronously and eventually returns its result in a future. (“As if” by launching a new thread.)
22 If there is a regular stream of small work items then we almost certainly don’t want to launch a new thread for each, and it’s likely that we want at least some control over which thread(s) execute which items. It is often convenient to represent that control as multiple executor objects. This allows programs to start executors when necessary, switch from one executor to another to control execution policy, and use multiple executors to prevent interference and thread exhaustion. Several possible implementations exist of the executor class and in practice there are a number of main groups of executors which have been found to be useful in real-world code (more implementations exist, this is simply a high level classification of them). These differ along a couple main dimensions, how many execution contexts will be used, how they are selected, and how they are prioritized.
25 # Simple unbounded thread pool, which can queue up an unbounded amount of work and maintains a dedicated set of threads (up to some maximum) which
26 dequeue and execute work as available.
27 # Bounded thread pools, which can be implemented as a specialization of the previous ones with a bounded queue or semaphore, which limits the amount of queuing in an attempt to bound the time spent waiting to execute and/or limit resource utilization for work tasks which hold state which is expensive to hold.
28 # Thread-spawning executors, in which each work always executes in a new thread.
29 # Prioritized thread pools, which have works which are not equally prioritized such that work can move to the front of the execution queue if necessary. This requires a special comparator or prioritization function to allow for work ordering and normally is implemented as a blocking priority queue in front of the pool instead of a blocking queue. This has many uses but is a somewhat specialized in nature and would unnecessarily clutter the initial interface.
30 # Work stealing thread pools, this is a specialized use case and is encapsulated in the ForkJoinPool in java, which allows lightweight work to be created by tasks in the pool and either run by the same thread for invocation efficiency or stolen by another thread without additional work. These have been left out until there is a more concrete fork-join proposal or until there is a more clear need as these can be complicated to implement
32 # Mutual exclusion executors
33 # Serial executors, which guarantee all work to be executed such that no two works will execute concurrently. This allows for a sequence of operations to be queued in sequence and that sequential order is maintained and work can be queued on a separate thread but with no mutual exclusion required.
34 # Loop executor, in which one thread donates itself to the executor to execute all queued work. This is related to the serial executor in that it guarantees mutual exclusion, but instead guarantees a particular thread will execute the work. These are particularly useful for testing purposes where code assumes an executor but testing code desires control over execution.
35 # GUI thread executor, where a GUI framework can expose an executor interface to allow other threads to queue up work to be executed as part of the GUI thread. This behaves similarly to a loop executor, but must be implemented as a custom interface as part of the framework.
37 # Inline executors, which execute inline to the thread which calls submit(). This has no queuing and behaves like a normal executor, but always uses the caller’s thread to execute. This allows parallel execution of works, though. This type of executor is often useful when there is an executor required by an interface, but when for performance reasons it’s better not to queue work or switch threads. This is often very useful as an optimization for work continuations which should execute immediately or quickly and can also be useful for optimizations when an interface requires an executor but the work tasks are too small to justify the overhead of a full thread pool.
39 A question arises of which of these executors (or others) be included in this library. There are use cases for these and many other executors. Often it is useful to have more than one implemented executor (e.g. the thread pool) to have more precise control of where the work is executed due to the existence of a GUI thread, or for testing purposes. A few core executors are frequently useful and these have been outlined here as the core of what should be in this library, if common use cases arise for alternative executor implementations, they can be added in the future. The current set provided here are: a basic thread pool `basic_thread_pool`, a serial executor `serial_executor`, a loop executor `loop_executor`, an inline executor `inline_executor` and a thread-spawning executor `thread_executor`.
43 [/////////////////////////]
44 [section:tutorial Tutorial]
50 [section:examples Examples]
52 [section:quick_sort Parallel Quick Sort]
55 #include <boost/thread/executors/basic_thread_pool.hpp>
56 #include <boost/thread/future.hpp>
66 boost::basic_thread_pool pool;
67 typedef std::list<T> return_type;
69 std::list<T> do_sort(std::list<T> chunk_data)
71 if(chunk_data.empty()) {
76 result.splice(result.begin(),chunk_data, chunk_data.begin());
77 T const& partition_val=*result.begin();
79 typename std::list<T>::iterator divide_point =
80 std::partition(chunk_data.begin(), chunk_data.end(),
81 [&](T const& val){return val<partition_val;});
83 std::list<T> new_lower_chunk;
84 new_lower_chunk.splice(new_lower_chunk.end(), chunk_data,
85 chunk_data.begin(), divide_point);
86 boost::future<std::list<T> > new_lower =
87 boost::async(pool, &sorter::do_sort, this, std::move(new_lower_chunk));
88 std::list<T> new_higher(do_sort(chunk_data));
89 result.splice(result.end(),new_higher);
90 while(!new_lower.is_ready()) {
91 pool.schedule_one_or_yield();
93 result.splice(result.begin(),new_lower.get());
99 std::list<T> parallel_quick_sort(std::list<T>& input) {
104 return s.do_sort(input);
112 [////////////////////////]
113 [section:rationale Design Rationale]
115 The authors of Boost.Thread have taken a different approach respect to N3785. Instead of basing all the design on an abstract executor class we make executor concepts. We believe that this is the good direction as a static polymorphic executor can be seen as a dynamic polymorphic executor using a simple adaptor. We believe also that it would make the library more usable, and more convenient for users.
117 The major design decisions concern deciding what a unit of work is, how to manage with units of work and time related functions in a polymorphic way.
119 An Executor is an object that schedules the closures that have been submitted to it, usually asynchronously. There could be multiple models of the Executor class. Some specific design notes:
121 * Thread pools are well know models of the Executor concept, and this library does indeed include a basic_thread_pool class, but other implementations also exist, including the ability to schedule work on GUI threads, scheduling work on a donor thread, as well as several specializations of thread pools.
123 * The choice of which executor to use is explicit. This is important for reasons described in the Motivation section. In particular, consider the common case of an asynchronous operation that itself spawns asynchronous operations. If both operations ran on the same executor, and if that executor had a bounded number of worker threads, then we could get deadlock. Programs often deal with such issues by splitting different kinds of work between different executors.
125 * Even if there could be a strong value in having a default executor, that can be used when detailed control is unnecessary, the authors don't know how to implement it in a portable and robust way.
127 * The library provides Executors based on static and dynamic polymorphism. The static polymorphism interface is intended to be used on contexts that need to have the best performances. The dynamic polymorphism interface has the advantage to been able to change the executor a function is using without making it a template and is possible to pass executors across a binary interface. For some applications, the cost of an additional virtual dispatch could be almost certainly negligible compared to the other operations involved.
129 * Conceptually, an executor puts closures on a queue and at some point executes them. The queue is always unbounded, so adding a closure to an executor never blocks. (Defining “never blocks” formally is challenging, but informally we just mean that submit() is an ordinary function that executes something and returns, rather than waiting for the completion of some potentially long running operation in another thread.)
133 One important question is just what a closure is. This library has a very simple answer: a closure is a `Callable` with no parameters and returning `void`.
135 N3785 choose the more specific `std::function<void()>` as it provides only dynamic polymorphism and states that in practice the implementation of a template based approach or another approach is impractical. The authors of this library think that the template based approach is compatible with a dynamic based approach. They give some arguments:
137 The first one is that a virtual function can not be a template. This is true but it is also true that the executor interface can provide the template functions that call to the virtual public functions. Another reason they give is that "a template parameter would complicate the interface without adding any real generality. In the end an executor class is going to need some kind of type erasure to handle all the different kinds of function objects with `void()` signature, and that’s exactly what std::function already does". We think that it is up to the executor to manage with this implementation details, not to the user.
139 We share all the argument they give related to the `void()` interface of the work unit. A work unit is a closure that takes no arguments and returns no value. This is indeed a limitation on user code, but combined with `boost::async` taking executors as parameters the user has all what she needs.
141 The third one is related to performance. They assert that "any mechanism for storing closures on an executor’s queue will have to use some form of type erasure. There’s no reason to believe that a custom closure mechanism, written just for std::executor and used nowhere else within the standard library, would be better in that respect than `std::function<void()>`". We believe that the implementation can do better that storing the closure on a `std::function<void()>`. e.g. the implementation can use intrusive data to store the closure and the pointers to other nodes needed to store the closures in a given order.
143 In addition `std::function<void()>` can not be constructed by moving the closure, so e.g. `std::packaged_task` could not be a Closure.
146 [heading Scheduled work]
148 The approach of this library respect to scheduled work of the N3785 proposal is quite different. Instead of adding the scheduled operations to a specific scheduled_executor polymorphic interface, we opt by adding two member template functions to a class scheduled_executor that wraps an existing executor. This has several advantages:
150 * The scheduled operations are available for all the executors.
151 * The template functions could accept any chrono `time_point` and `duration` respectively as we are not working with virtual functions.
153 In order to manage with all the clocks, there are two alternatives:
155 * transform the submit_at operation to a `submit_after` operation and let a single `scheduled_executor` manage with a single clock.
156 * have a single instance of a `scheduled_executor<Clock>` for each `CLock`.
158 The library chose the first of those options, largely for simplicity.
161 [heading Scheduled work]
163 The approach of this library respect to scheduled work of the N3785 proposal is quite different. Instead of adding the scheduled operations to a specific scheduled_executor polymorphic interface, we opt by adding a specific `scheduler` class that is not an executor and knows how to manage with the scheduling of timed tasks `submit_at`/`submit_after`.
166 `scheduler` provides executor factories `at`/`after` given a specific `time_point` or a `duration`. The built executors wrap a reference to this scheduler and the time at which the submitted task will be executed.
168 If we want to schedule these operations on an existing executor (as `serial_executor` does), these classes provide a `on` factory taking another executor as parameter and wraps both instance on the returned executor.
170 sch.on(tp).after(seconds(i)).submit(boost::bind(fn,i));
172 This has several advantages:
174 * The scheduled operations are available for all the executors via wrappers.
175 * The template functions could accept any chrono `time_point` and `duration` respectively as we are not working with virtual functions.
177 In order to manage with all the clocks, this library propose generic solution. `scheduler<Clock>` know how to manage with the `submit_at`/`submit_after` `Clock::time_point`/`Clock::duration` tasks. Note that the durations on different clocks differ.
179 [heading Not Handled Exceptions]
180 As in N3785 and based on the same design decision than `std`/`boost::thread` if a user closure throws an exception, the executor must call the `std::terminate` function.
181 Note that when we combine `boost::async` and `Executors`, the exception will be caught by the closure associated to the returned future, so that the exception is stored on the returned future, as for the other `async` overloads.
183 [heading At thread entry]
185 It is common idiom to set some thread local variable at the beginning of a thread. As Executors could instantiate threads internally these Executors shall have the ability to call a user specific function at thread entry on the executor constructor.
187 For executors that don't instantiate any thread and that would use the current thread this function shall be called only for the thread calling the `at_thread_entry` member function.
189 [heading Cancelation]
191 The library does not provision yet for the ability to cancel/interrupt work, though this is a commonly requested feature.
193 This could be managed externally by an additional cancelation object that can be shared between the creator of the unit of work and the unit of work.
195 We can think also of a cancelable closure that could be used in a more transparent way.
197 An alternative is to make async return a cancelable_task but this will need also a cancelable closure.
201 The library would provide in the future a cancelable_task that could support cancelation.
203 class cancelation_state
205 std::atomic<bool> requested;
206 std::atomic<bool> enabled;
207 std::condition_variable* cond;
208 std::mutex cond_mutex;
210 cancelation_state() :
216 requested.store(true, std::memory_order_relaxed);
217 std::lock_guard < std::mutex > lk(cond_mutex);
223 bool cancellation_requested() const
225 return requested.load(std::memory_order_relaxed);
229 enable.store(true, std::memory_order_relaxed);
233 enable.store(false, std::memory_order_relaxed);
235 bool cancellation_enabled() const
237 return enabled.load(std::memory_order_relaxed);
239 void set_condition_variable(std::condition_variable& cv)
241 std::lock_guard < std::mutex > lk(cond_mutex);
244 void clear_condition_variable()
246 std::lock_guard < std::mutex > lk(cond_mutex);
249 struct clear_cv_on_destruct
251 ~clear_cv_on_destruct()
253 this_thread_interrupt_flag.clear_condition_variable();
256 void cancelation_point();
257 void cancelable_wait(std::condition_variable& cv, std::unique_lock<std::mutex>& lk)
260 this_cancelable_state.set_condition_variable(cv);
261 this_cancelable_state::clear_cv_on_destruct guard;
262 interruption_point();
263 cv.wait_for(lk, std::chrono::milliseconds(1));
264 this_cancelable_state.clear_condition_variable();
267 class disable_cancelation
270 disable_cancelation(const disable_cancelation&)= delete;
271 disable_cancelation& operator=(const disable_cancelation&)= delete;
272 disable_cancelation(cancelable_closure& closure)
274 ~disable_cancelation() noexcept;
276 class restore_cancelation
279 restore_cancelation(const restore_cancelation&) = delete;
280 restore_cancelation& operator=(const restore_cancelation&) = delete;
281 explicit restore_cancelation(cancelable_closure& closure, disable_cancelation& disabler) noexcept;
282 ~restore_cancelation() noexcept;
286 template <class Closure>
287 struct cancelable_closure_mixin: cancelable_closure
291 cancel_point();this->Closure::run();
295 struct my_clousure: cancelable_closure_mixin<my_clousure>
307 [heading Current executor]
309 The library does not provision for the ability to get the current executor, though having access to it could simplify a lot the user code.
311 The reason is that the user can always use a thread_local variable and reset it using the `at_thread_entry ` member function.
313 thread_local current_executor_state_type current_executor_state;
314 executor* current_executor() { return current_executor_state.current_executor(); }
315 basic_thread_pool pool(
317 [](basic_thread_pool& pool) {
318 current_executor_state.set_current_executor(pool);
323 [heading Default executor]
325 The library authors share some of the concerns of the C++ standard committee (introduction of a new single shared resource, a singleton, could make it difficult to make it portable to all the environments) and that this library doesn't need to provide a default executor for the time been.
327 The user can always define his default executor himself.
329 boost::generic_executor_ref default_executor()
331 static boost::basic_thread_pool tp(4);
332 return generic_executor_ref(tp);
339 [/////////////////////]
340 [section:ref Reference]
343 [////////////////////////////////]
344 [section:concept_closure Concept `Closure`]
347 A type `E` meets the `Closure` requirements if is a model of `Callable(void())` and a model of `CopyConstructible`/`MoveConstructible`.
350 [////////////////////////////////]
351 [section:concept_executor Concept `Executor`]
353 The `Executor` concept models the common operations of all the executors.
355 A type `E` meets the `Executor` requirements if the following expressions are well-formed and have the specified semantics
361 * `e.try_executing_one();`
362 * `e.reschedule_until(p);`
366 * `e` denotes a value of type `E`,
367 * `lc` denotes a lvalue reference of type `Closure`,
368 * `rc` denotes a rvalue reference of type `Closure`
369 * `p` denotes a value of type `Predicate`
371 [/////////////////////////////////////]
372 [section:submitlc `e.submit(lc);`]
376 [[Effects:] [The specified closure will be scheduled for execution at some point in the future.
377 If invoked closure throws an exception the executor will call std::terminate, as is the case with threads.]]
379 [[Synchronization:] [completion of closure on a particular thread happens before destruction of thread's thread local variables.]]
381 [[Return type:] [`void`.]]
383 [[Throws:] [sync_queue_is_closed if the thread pool is closed. Whatever exception that can be throw while storing the closure.]]
385 [[Exception safety:] [If an exception is thrown then the executor state is unmodified.]]
390 [/////////////////////////////////////]
391 [section:submitrc `e.submit(rc);`]
395 [[Effects:] [The specified closure will be scheduled for execution at some point in the future.
396 If invoked closure throws an exception the executor will call std::terminate, as is the case with threads.]]
398 [[Synchronization:] [completion of closure on a particular thread happens before destruction of thread's thread local variables.]]
400 [[Return type:] [`void`.]]
402 [[Throws:] [sync_queue_is_closed if the thread pool is closed. Whatever exception that can be throw while storing the closure.]]
404 [[Exception safety:] [If an exception is thrown then the executor state is unmodified.]]
409 [/////////////////////////////////////]
410 [section:close `e.close();`]
414 [[Effects:] [close the executor `e` for submissions.]]
416 [[Remark:] [The worker threads will work until there is no more closures to run.]]
418 [[Return type:] [`void`.]]
420 [[Throws:] [Whatever exception that can be thrown while ensuring the thread safety.]]
422 [[Exception safety:] [If an exception is thrown then the executor state is unmodified.]]
427 [/////////////////////////////////////]
428 [section:closed `b = e.closed();`]
432 [[Return type:] [`bool`.]]
434 [[Return:] [whether the executor is closed for submissions.]]
436 [[Throws:] [Whatever exception that can be throw while ensuring the thread safety.]]
442 [/////////////////////////////////////]
443 [section:try_executing_one `e.try_executing_one();`]
447 [[Effects:] [try to execute one work.]]
449 [[Remark:] [whether a work has been executed.]]
451 [[Return type:] [`bool`.]]
453 [[Return:] [Whether a work has been executed.]]
455 [[Throws:] [whatever the current work constructor throws or the `work()` throws.]]
460 [/////////////////////////////////////]
461 [section:reschedule_until `e.reschedule_until(p);`]
465 [[Requires:] [This must be called from a scheduled work]]
467 [[Effects:] [reschedule works until `p()`.]]
469 [[Return type:] [`bool`.]]
471 [[Return:] [Whether a work has been executed.]]
473 [[Throws:] [whatever the current work constructor throws or the `work()` throws.]]
481 [/////////////////////////]
482 [section:work Class `work`]
484 #include <boost/thread/work.hpp>
486 typedef 'implementation_defined' work;
491 [[Requires:] [work is a model of 'Closure']]
497 [/////////////////////////////////]
498 [section:executor Class `executor`]
500 Executor abstract base class.
502 #include <boost/thread/executor.hpp>
507 typedef boost::work work;
509 executor(executor const&) = delete;
510 executor& operator=(executor const&) = delete;
513 virtual ~executor() {};
515 virtual void close() = 0;
516 virtual bool closed() = 0;
518 virtual void submit(work&& closure) = 0;
519 virtual void submit(work& closure) = 0;
520 template <typename Closure>
521 void submit(Closure&& closure);
523 virtual bool try_executing_one() = 0;
524 template <typename Pred>
525 bool reschedule_until(Pred const& pred);
529 [/////////////////////////////////////]
530 [section:constructor Constructor `executor()`]
536 [[Effects:] [Constructs an executor. ]]
538 [[Throws:] [Nothing. ]]
544 [/////////////////////////////////////]
545 [section:constructor Destructor `~executor()`]
551 [[Effects:] [Destroys the executor.]]
553 [[Synchronization:] [The completion of all the closures happen before the completion of the executor destructor.]]
562 [//////////////////////////////////////////////////////////]
563 [section:executor_adaptor Template Class `executor_adaptor`]
565 Polymorphic adaptor of a model of Executor to an executor.
567 #include <boost/thread/executor.hpp>
569 template <typename Executor>
570 class executor_adaptor : public executor
572 Executor ex; // for exposition only
574 typedef executor::work work;
576 executor_adaptor(executor_adaptor const&) = delete;
577 executor_adaptor& operator=(executor_adaptor const&) = delete;
579 template <typename ...Args>
580 executor_adaptor(Args&& ... args);
582 Executor& underlying_executor() noexcept;
587 void submit(work&& closure);
588 void submit(work& closure);
590 bool try_executing_one();
595 [/////////////////////////////////////]
596 [section:constructor Constructor `executor_adaptor(Args&& ...)`]
598 template <typename ...Args>
599 executor_adaptor(Args&& ... args);
603 [[Effects:] [Constructs an executor_adaptor. ]]
605 [[Throws:] [Nothing. ]]
611 [/////////////////////////////////////]
612 [section:destructor Destructor `~executor_adaptor()`]
614 virtual ~executor_adaptor();
618 [[Effects:] [Destroys the executor_adaptor.]]
620 [[Synchronization:] [The completion of all the closures happen before the completion of the executor destructor.]]
625 [/////////////////////////////////////]
626 [section:underlying_executor Function member `underlying_executor()`]
628 Executor& underlying_executor() noexcept;
632 [[Return:] [The underlying executor instance. ]]
641 [/////////////////////////////////]
642 [section:generic_executor_ref Class `generic_executor_ref`]
644 Executor abstract base class.
646 #include <boost/thread/generic_executor_ref.hpp>
648 class generic_executor_ref
651 generic_executor_ref(generic_executor_ref const&);
652 generic_executor_ref& operator=(generic_executor_ref const&);
654 template <class Executor>
655 generic_executor_ref(Executor& ex);
656 generic_executor_ref() {};
661 template <typename Closure>
662 void submit(Closure&& closure);
664 virtual bool try_executing_one() = 0;
665 template <typename Pred>
666 bool reschedule_until(Pred const& pred);
672 [//////////////////////////////////////////////////////////]
673 [section: scheduler Template Class `scheduler `]
675 Scheduler providing time related functions. Note that `scheduler` is not an Executor.
677 #include <boost/thread/executors/scheduler.hpp>
680 template <class Clock=steady_clock>
684 using work = boost::function<void()> ;
687 scheduler(scheduler const&) = delete;
688 scheduler& operator=(scheduler const&) = delete;
696 template <class Duration, typename Closure>
697 void submit_at(chrono::time_point<clock,Duration> abs_time, Closure&& closure);
698 template <class Rep, class Period, typename Closure>
699 void submit_after(chrono::duration<Rep,Period> rel_time, Closure&& closure);
701 template <class Duration>
702 at_executor<scheduler> submit_at(chrono::time_point<clock,Duration> abs_time);
703 template <class Rep, class Period>
704 at_executor<scheduler> submit_after(chrono::duration<Rep,Period> rel_time);
706 template <class Executor>
707 scheduler_executor_wrapper<scheduler, Executor> on(Executor& ex);
712 [/////////////////////////////////////]
713 [section:constructor Constructor `scheduler()`]
719 [[Effects:] [Constructs a `scheduler`. ]]
721 [[Throws:] [Nothing. ]]
727 [/////////////////////////////////////]
728 [section:destructor Destructor `~scheduler()`]
734 [[Effects:] [Destroys the scheduler.]]
736 [[Synchronization:] [The completion of all the closures happen before the completion of the executor destructor.]]
741 [/////////////////////////////////////]
742 [section:submit_at Template Function Member `submit_at()`]
744 template <class Clock, class Duration, typename Closure>
745 void submit_at(chrono::time_point<Clock,Duration> abs_time, Closure&& closure);
749 [[Effects:] [Schedule a `closure` to be executed at `abs_time`. ]]
751 [[Throws:] [Nothing.]]
757 [/////////////////////////////////////]
758 [section:submit_after Template Function Member `submit_after()`]
760 template <class Rep, class Period, typename Closure>
761 void submit_after(chrono::duration<Rep,Period> rel_time, Closure&& closure);
765 [[Effects:] [Schedule a `closure` to be executed after `rel_time`. ]]
767 [[Throws:] [Nothing.]]
776 [//////////////////////////////////////////////////////////]
777 [section:at_executor Template Class `at_executor`]
780 #include <boost/thread/executors/scheduler.hpp>
783 template <class Scheduler>
787 using work = Scheduler::work;
788 using clock = Scheduler::clock;
790 at_executor(at_executor const&) = default;
791 at_executor(at_executor &&) = default;
792 at_executor& operator=(at_executor const&) = default;
793 at_executor& operator=(at_executor &&) = default;
795 at_executor(Scheduler& sch, clock::time_point const& tp);
801 Scheduler& underlying_scheduler();
803 template <class Closure>
804 void submit(Closure&& closure);
805 template <class Duration, typename Work>
806 void submit_at(chrono::time_point<clock,Duration> abs_time, Closure&& closure);
807 template <class Rep, class Period, typename Work>
808 void submit_after(chrono::duration<Rep,Period> rel_time, Closure&& closure);
810 template <class Executor>
811 resubmit_at_executor<Scheduler, Executor> on(Executor& ex);
816 [/////////////////////////////////////]
817 [section:constructor Constructor `at_executor(Scheduler&, clock::time_point const&)`]
819 at_executor(Scheduler& sch, clock::time_point const& tp);
823 [[Effects:] [Constructs a `at_executor`. ]]
825 [[Throws:] [Nothing. ]]
831 [/////////////////////////////////////]
832 [section:destructor Destructor `~at_executor()`]
838 [[Effects:] [Destroys the `at_executor`.]]
840 [[Synchronization:] [The completion of all the closures happen before the completion of the executor destructor.]]
845 [/////////////////////////////////////]
846 [section:underlying_scheduler Function member `underlying_scheduler()`]
848 Scheduler& underlying_scheduler() noexcept;
852 [[Return:] [The underlying scheduler instance. ]]
857 [/////////////////////////////////////]
858 [section:submit_at Template Function Member `submit()`]
860 template <typename Closure>
861 void submit(Closure&& closure);
865 [[Effects:] [Schedule the `closure` to be executed at the `abs_time` given at construction time. ]]
867 [[Throws:] [Nothing.]]
872 [/////////////////////////////////////]
873 [section:submit_at Template Function Member `submit_at()`]
875 template <class Clock, class Duration, typename Closure>
876 void submit_at(chrono::time_point<Clock,Duration> abs_time, Closure&& closure);
880 [[Effects:] [Schedule a `closure` to be executed at `abs_time`. ]]
882 [[Throws:] [Nothing.]]
888 [/////////////////////////////////////]
889 [section:submit_after Template Function Member `submit_after()`]
891 template <class Rep, class Period, typename Closure>
892 void submit_after(chrono::duration<Rep,Period> rel_time, Closure&& closure);
896 [[Effects:] [Schedule a `closure` to be executed after `rel_time`. ]]
898 [[Throws:] [Nothing.]]
908 [//////////////////////////////////////////////////////////]
909 [section:scheduler_executor_wrapper Template Class `scheduler_executor_wrapper`]
911 #include <boost/thread/executors/scheduler.hpp>
914 template <class Scheduler, class Executor>
915 class scheduler_executor_wrapper
918 using work = Scheduler::work;
919 using clock = Scheduler::clock;
921 scheduler_executor_wrapper(scheduler_executor_wrapper const&) = default;
922 scheduler_executor_wrapper(scheduler_executor_wrapper &&) = default;
923 scheduler_executor_wrapper& operator=(scheduler_executor_wrapper const&) = default;
924 scheduler_executor_wrapper& operator=(scheduler_executor_wrapper &&) = default;
926 scheduler_executor_wrapper(Scheduler& sch, Executor& ex);
928 ~scheduler_executor_wrapper();
933 Executor& underlying_executor();
934 Scheduler& underlying_scheduler();
936 template <class Closure>
937 void submit(Closure&& closure);
938 template <class Duration, typename Work>
939 void submit_at(chrono::time_point<clock,Duration> abs_time, Closure&& closure);
940 template <class Rep, class Period, typename Work>
941 void submit_after(chrono::duration<Rep,Period> rel_time, Closure&& closure);
943 template <class Duration>
944 resubmit_at_executor<Scheduler, Executor> at(chrono::time_point<clock,Duration> abs_time);
945 template <class Rep, class Period>
946 resubmit_at_executor<Scheduler, Executor> after(chrono::duration<Rep,Period> rel_time);
951 [/////////////////////////////////////]
952 [section:constructor Constructor `scheduler_executor_wrapper(Scheduler&, Executor&)`]
954 scheduler_executor_wrapper(Scheduler& sch, Executor& ex);
958 [[Effects:] [Constructs a `scheduler_executor_wrapper`. ]]
960 [[Throws:] [Nothing. ]]
965 [/////////////////////////////////////]
966 [section:destructor Destructor `~scheduler_executor_wrapper()`]
968 ~scheduler_executor_wrapper();
972 [[Effects:] [Destroys the `scheduler_executor_wrapper`.]]
974 [[Synchronization:] [The completion of all the closures happen before the completion of the executor destructor.]]
979 [/////////////////////////////////////]
980 [section:underlying_scheduler Function member `underlying_scheduler()`]
982 Scheduler& underlying_scheduler() noexcept;
986 [[Return:] [The underlying scheduler instance. ]]
991 [/////////////////////////////////////]
992 [section:underlying_executor Function member `underlying_executor()`]
994 Executor& underlying_executor() noexcept;
998 [[Return:] [The underlying executor instance. ]]
1003 [/////////////////////////////////////]
1004 [section:submit_at Template Function Member `submit()`]
1006 template <typename Closure>
1007 void submit(Closure&& closure);
1011 [[Effects:] [Submit the `closure` on the underlying executor. ]]
1013 [[Throws:] [Nothing.]]
1018 [/////////////////////////////////////]
1019 [section:submit_at Template Function Member `submit_at()`]
1021 template <class Clock, class Duration, typename Closure>
1022 void submit_at(chrono::time_point<Clock,Duration> abs_time, Closure&& closure);
1026 [[Effects:] [Resubmit the `closure` to be executed on the underlying executor at `abs_time`. ]]
1028 [[Throws:] [Nothing.]]
1033 [/////////////////////////////////////]
1034 [section:submit_after Template Function Member `submit_after()`]
1036 template <class Rep, class Period, typename Closure>
1037 void submit_after(chrono::duration<Rep,Period> rel_time, Closure&& closure);
1041 [[Effects:] [Resubmit the `closure` to be executed on the underlying executor after `rel_time`. ]]
1043 [[Throws:] [Nothing.]]
1052 [//////////////////////////////////////////////////////////]
1053 [section:resubmit_at_executor Template Class `resubmit_at_executor`]
1055 `Executor` wrapping an `Scheduler`, an `Executor` and a `time_point` providing an `Executor` interface.
1057 #include <boost/thread/executors/scheduler.hpp>
1060 template <class Scheduler, class Executor>
1061 class resubmit_at_executor
1064 using work = Scheduler::work;
1065 using clock = Scheduler::clock;
1067 resubmit_at_executor(resubmit_at_executor const&) = default;
1068 resubmit_at_executor(resubmit_at_executor &&) = default;
1069 resubmit_at_executor& operator=(resubmit_at_executor const&) = default;
1070 resubmit_at_executor& operator=(resubmit_at_executor &&) = default;
1072 template <class Duration>
1073 resubmit_at_executor(Scheduler& sch, Executor& ex, clock::time_point<Duration> const& tp);
1074 ~resubmit_at_executor();
1079 Executor& underlying_executor();
1080 Scheduler& underlying_scheduler();
1082 template <class Closure>
1083 void submit(Closure&& closure);
1084 template <class Duration, typename Work>
1085 void submit_at(chrono::time_point<clock,Duration> abs_time, Closure&& closure);
1086 template <class Rep, class Period, typename Work>
1087 void submit_after(chrono::duration<Rep,Period> rel_time, Closure&& closure);
1093 [/////////////////////////////////////]
1094 [section:constructor Constructor `resubmit_at_executor(Scheduler&, Executor&, clock::time_point<Duration>)`]
1096 template <class Duration>
1097 resubmit_at_executor(Scheduler& sch, Executor& ex, clock::time_point<Duration> const& tp);
1101 [[Effects:] [Constructs a `resubmit_at_executor`. ]]
1103 [[Throws:] [Nothing. ]]
1109 [/////////////////////////////////////]
1110 [section:destructor Destructor `~resubmit_at_executor()`]
1112 ~resubmit_at_executor();
1116 [[Effects:] [Destroys the executor_adaptor.]]
1118 [[Synchronization:] [The completion of all the closures happen before the completion of the executor destructor.]]
1123 [/////////////////////////////////////]
1124 [section:underlying_executor Function member `underlying_executor()`]
1126 Executor& underlying_executor() noexcept;
1130 [[Return:] [The underlying executor instance. ]]
1135 [/////////////////////////////////////]
1136 [section:underlying_scheduler Function member `underlying_scheduler()`]
1138 Scheduler& underlying_scheduler() noexcept;
1142 [[Return:] [The underlying scheduler instance. ]]
1147 [/////////////////////////////////////]
1148 [section:submit_at Template Function Member `submit()`]
1150 template <typename Closure>
1151 void submit(Closure&& closure);
1155 [[Effects:] [Resubmit the `closure` to be executed on the underlying executor at the `abs_time` given at construction time. ]]
1157 [[Throws:] [Nothing.]]
1162 [/////////////////////////////////////]
1163 [section:submit_at Template Function Member `submit_at()`]
1165 template <class Clock, class Duration, typename Closure>
1166 void submit_at(chrono::time_point<Clock,Duration> abs_time, Closure&& closure);
1170 [[Effects:] [Resubmit the `closure` to be executed on the underlying executor at `abs_time`. ]]
1172 [[Throws:] [Nothing.]]
1177 [/////////////////////////////////////]
1178 [section:submit_after Template Function Member `submit_after()`]
1180 template <class Rep, class Period, typename Closure>
1181 void submit_after(chrono::duration<Rep,Period> rel_time, Closure&& closure);
1185 [[Effects:] [Resubmit the `closure` to be executed on the underlying executor after `rel_time`. ]]
1187 [[Throws:] [Nothing.]]
1196 [//////////////////////////////////////////////////////////]
1198 [section:scheduled_executor_ref Template Class `scheduled_executor_ref`]
1200 Executor providing time related functions.
1202 #include <boost/thread/executors/scheduled_executor_ref.hpp>
1204 template <class Executor>
1205 class scheduled_executor_ref
1209 typedef executor::work work;
1211 scheduled_executor_ref(scheduled_executor_ref const&) = delete;
1212 scheduled_executor_ref& operator=(scheduled_executor_ref const&) = delete;
1214 template <class Rep, class Period>
1215 scheduled_executor_ref(Executor& ex, chrono::duration<Rep, Period> granularity=chrono::milliseconds(100));
1217 Executor& underlying_executor() noexcept;
1222 void submit(work&& closure);
1223 void submit(work& closure);
1224 template <typename Closure>
1225 void submit(Closure&& closure);
1227 bool try_executing_one();
1228 template <typename Pred>
1229 bool reschedule_until(Pred const& pred);
1231 template <class Clock, class Duration, typename Closure>
1232 void submit_at(chrono::time_point<Clock,Duration> abs_time, Closure&& closure);
1233 template <class Rep, class Period, typename Closure>
1234 void submit_after(chrono::duration<Rep,Period> rel_time, Closure&& closure);
1238 [/////////////////////////////////////]
1239 [section:constructor Constructor `scheduled_executor_ref(Executor&, chrono::duration<Rep, Period>)`]
1241 template <class Rep, class Period>
1242 scheduled_executor_ref(Executor& ex, chrono::duration<Rep, Period> granularity=chrono::milliseconds(100));
1246 [[Effects:] [Constructs a scheduled_executor_ref. ]]
1248 [[Throws:] [Nothing. ]]
1254 [/////////////////////////////////////]
1255 [section:destructor Destructor `~scheduled_executor_ref()`]
1257 ~scheduled_executor_ref();
1261 [[Effects:] [Destroys the executor_adaptor.]]
1263 [[Synchronization:] [The completion of all the closures happen before the completion of the executor destructor.]]
1268 [/////////////////////////////////////]
1269 [section:underlying_executor Function member `underlying_executor()`]
1271 Executor& underlying_executor() noexcept;
1275 [[Return:] [The underlying executor instance. ]]
1280 [/////////////////////////////////////]
1281 [section:submit_at Template Function Member `submit()`]
1283 template <typename Closure>
1284 void submit(Closure&& closure);
1288 [[Effects:] [Resubmit the `closure` to be executed on the underlying executor. ]]
1290 [[Throws:] [Nothing.]]
1295 [/////////////////////////////////////]
1296 [section:submit_at Template Function Member `submit_at()`]
1298 template <class Clock, class Duration, typename Closure>
1299 void submit_at(chrono::time_point<Clock,Duration> abs_time, Closure&& closure);
1303 [[Effects:] [Schedule a `closure` to be executed at `abs_time`. ]]
1305 [[Throws:] [Nothing.]]
1311 [/////////////////////////////////////]
1312 [section:submit_after Template Function Member `submit_after()`]
1314 template <class Rep, class Period, typename Closure>
1315 void submit_after(chrono::duration<Rep,Period> rel_time, Closure&& closure);
1319 [[Effects:] [Schedule a `closure` to be executed after `rel_time`. ]]
1321 [[Throws:] [Nothing.]]
1331 [//////////////////////////////////////////////////////////]
1332 [section:serial_executor Template Class `serial_executor`]
1334 A serial executor ensuring that there are no two work units that executes concurrently.
1336 #include <boost/thread/serial_executor.hpp>
1338 template <class Executor>
1339 class serial_executor
1342 serial_executor(serial_executor const&) = delete;
1343 serial_executor& operator=(serial_executor const&) = delete;
1345 template <class Executor>
1346 serial_executor(Executor& ex);
1348 Executor& underlying_executor() noexcept;
1353 template <typename Closure>
1354 void submit(Closure&& closure);
1356 bool try_executing_one();
1357 template <typename Pred>
1358 bool reschedule_until(Pred const& pred);
1363 [/////////////////////////////////////]
1364 [section:constructor Constructor `serial_executor(Executor&)`]
1366 template <class Executor>
1367 serial_executor(Executor& ex);
1371 [[Effects:] [Constructs a serial_executor. ]]
1373 [[Throws:] [Nothing. ]]
1379 [/////////////////////////////////////]
1380 [section:destructor Destructor `~serial_executor()`]
1386 [[Effects:] [Destroys the serial_executor.]]
1388 [[Synchronization:] [The completion of all the closures happen before the completion of the executor destructor.]]
1393 [/////////////////////////////////////]
1394 [section:underlying_executor Function member `underlying_executor()`]
1396 generic_executor_ref& underlying_executor() noexcept;
1400 [[Return:] [The underlying executor instance. ]]
1402 [[Throws:] [Nothing.]]
1411 [//////////////////////////////////////////////////////////]
1412 [section:generic_serial_executor Class `generic_serial_executor`]
1414 A serial executor ensuring that there are no two work units that executes concurrently.
1416 #include <boost/thread/generic_serial_executor.hpp>
1418 class generic_serial_executor
1421 generic_serial_executor(generic_serial_executor const&) = delete;
1422 generic_serial_executor& operator=(generic_serial_executor const&) = delete;
1424 template <class Executor>
1425 generic_serial_executor(Executor& ex);
1427 generic_executor_ref& underlying_executor() noexcept;
1432 template <typename Closure>
1433 void submit(Closure&& closure);
1435 bool try_executing_one();
1436 template <typename Pred>
1437 bool reschedule_until(Pred const& pred);
1442 [/////////////////////////////////////]
1443 [section:constructor Constructor `generic_serial_executor(Executor&)`]
1445 template <class Executor>
1446 generic_serial_executor(Executor& ex);
1450 [[Effects:] [Constructs a serial_executor. ]]
1452 [[Throws:] [Nothing. ]]
1458 [/////////////////////////////////////]
1459 [section:destructor Destructor `~serial_executor()`]
1461 ~generic_serial_executor();
1465 [[Effects:] [Destroys the serial_executor.]]
1467 [[Synchronization:] [The completion of all the closures happen before the completion of the executor destructor.]]
1472 [/////////////////////////////////////]
1473 [section:underlying_executor Function member `underlying_executor()`]
1475 Executor& underlying_executor() noexcept;
1479 [[Return:] [The underlying executor instance. ]]
1489 [//////////////////////////////////////////////////////////]
1490 [section:inline_executor Class `inline_executor`]
1492 A serial executor ensuring that there are no two work units that executes concurrently.
1494 #include <boost/thread/inline_executor.hpp>
1496 class inline_executor
1499 inline_executor(inline_executor const&) = delete;
1500 inline_executor& operator=(inline_executor const&) = delete;
1507 template <typename Closure>
1508 void submit(Closure&& closure);
1510 bool try_executing_one();
1511 template <typename Pred>
1512 bool reschedule_until(Pred const& pred);
1517 [/////////////////////////////////////]
1518 [section:constructor Constructor `inline_executor()`]
1524 [[Effects:] [Constructs an inline_executor. ]]
1526 [[Throws:] [Nothing. ]]
1532 [/////////////////////////////////////]
1533 [section:destructor Destructor `~inline_executor()`]
1539 [[Effects:] [Destroys the inline_executor.]]
1541 [[Synchronization:] [The completion of all the closures happen before the completion of the executor destructor.]]
1553 [///////////////////////////////////////]
1554 [section:basic_thread_pool Class `basic_thread_pool`]
1556 A thread pool with up to a fixed number of threads.
1558 #include <boost/thread/executors/basic_thread_pool.hpp>
1560 class basic_thread_pool
1564 basic_thread_pool(basic_thread_pool const&) = delete;
1565 basic_thread_pool& operator=(basic_thread_pool const&) = delete;
1567 basic_thread_pool(unsigned const thread_count = thread::hardware_concurrency());
1568 template <class AtThreadEntry>
1569 basic_thread_pool( unsigned const thread_count, AtThreadEntry at_thread_entry);
1570 ~basic_thread_pool();
1575 template <typename Closure>
1576 void submit(Closure&& closure);
1578 bool try_executing_one();
1580 template <typename Pred>
1581 bool reschedule_until(Pred const& pred);
1586 [/////////////////////////////////////]
1587 [section:constructor Constructor `basic_thread_pool(unsigned const)`]
1591 [[Effects:] [creates a thread pool that runs closures on `thread_count` threads. ]]
1593 [[Throws:] [Whatever exception is thrown while initializing the needed resources. ]]
1599 [/////////////////////////////////////]
1600 [section:destructor Destructor `~basic_thread_pool()`]
1602 ~basic_thread_pool();
1606 [[Effects:] [Destroys the thread pool.]]
1608 [[Synchronization:] [The completion of all the closures happen before the completion of the executor destructor.]]
1615 [///////////////////////////////////////]
1616 [section:thread_executor Class `thread_executor`]
1618 A thread_executor with a threads for each task.
1620 #include <boost/thread/executors/thread_executor.hpp>
1622 class thread_executor
1626 thread_executor(thread_executor const&) = delete;
1627 thread_executor& operator=(thread_executor const&) = delete;
1630 template <class AtThreadEntry>
1631 basic_thread_pool( unsigned const thread_count, AtThreadEntry at_thread_entry);
1637 template <typename Closure>
1638 void submit(Closure&& closure);
1643 [/////////////////////////////////////]
1644 [section:constructor Constructor `thread_executor()`]
1648 [[Effects:] [creates a thread_executor. ]]
1650 [[Throws:] [Whatever exception is thrown while initializing the needed resources. ]]
1656 [/////////////////////////////////////]
1657 [section:destructor Destructor `~thread_executor()`]
1663 [[Effects:] [Waits for closures (if any) to complete, then joins and destroys the threads.]]
1665 [[Synchronization:] [The completion of all the closures happen before the completion of the executor destructor.]]
1673 [/////////////////////////////////]
1674 [section:loop_executor Class `loop_executor`]
1676 A user scheduled executor.
1678 #include <boost/thread/loop_executor.hpp>
1684 loop_executor(loop_executor const&) = delete;
1685 loop_executor& operator=(loop_executor const&) = delete;
1693 template <typename Closure>
1694 void submit(Closure&& closure);
1696 bool try_executing_one();
1697 template <typename Pred>
1698 bool reschedule_until(Pred const& pred);
1701 void run_queued_closures();
1705 [/////////////////////////////////////]
1706 [section:constructor Constructor `loop_executor()`]
1712 [[Effects:] [creates an executor that runs closures using one of its closure-executing methods. ]]
1714 [[Throws:] [Whatever exception is thrown while initializing the needed resources. ]]
1720 [/////////////////////////////////////]
1721 [section:destructor Destructor `~loop_executor()`]
1723 virtual ~loop_executor();
1727 [[Effects:] [Destroys the executor.]]
1729 [[Synchronization:] [The completion of all the closures happen before the completion of the executor destructor.]]
1733 [/////////////////////////////////////]
1734 [section:loop Function member `loop()`]
1740 [[Return:] [reschedule works until `closed()` or empty. ]]
1742 [[Throws:] [whatever the current work constructor throws or the `work()` throws.]]
1749 [/////////////////////////////////////]
1750 [section:run_queued_closures Function member `run_queued_closures()`]
1752 void run_queued_closures();
1756 [[Return:] [reschedule the enqueued works. ]]
1758 [[Throws:] [whatever the current work constructor throws or the `work()` throws.]]