• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1[/
2 / Copyright (c) 2014-2017 Vicente J. Botet Escriba
3 /
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)
6 /]
7
8[//////////////////////////////////////////////////////////]
9[section:executors Executors and Schedulers -- EXPERIMENTAL]
10
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 :(]
12
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.]
14
15Executors 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.
16
17[////////////////////]
18[section Introduction]
19
20Multithreaded 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.)
21
22If 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.
23
24# Thread Pools
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
26dequeue 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
31
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.
36
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.
38
39A 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`.
40[endsect]
41
42[/
43[/////////////////////////]
44[section:tutorial Tutorial]
45
46
47[endsect]
48]
49[////////////////]
50[section:examples Examples]
51
52[section:quick_sort Parallel Quick Sort]
53
54
55  #include <boost/thread/executors/basic_thread_pool.hpp>
56  #include <boost/thread/future.hpp>
57  #include <numeric>
58  #include <algorithm>
59  #include <functional>
60  #include <iostream>
61  #include <list>
62
63  template<typename T>
64  struct sorter
65  {
66      boost::basic_thread_pool pool;
67      typedef std::list<T> return_type;
68
69      std::list<T> do_sort(std::list<T> chunk_data)
70      {
71          if(chunk_data.empty()) {
72              return chunk_data;
73          }
74
75          std::list<T> result;
76          result.splice(result.begin(),chunk_data, chunk_data.begin());
77          T const& partition_val=*result.begin();
78
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;});
82
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();
92          }
93          result.splice(result.begin(),new_lower.get());
94          return result;
95      }
96  };
97
98  template<typename T>
99  std::list<T> parallel_quick_sort(std::list<T>& input) {
100      if(input.empty()) {
101          return input;
102      }
103      sorter<T> s;
104      return s.do_sort(input);
105  }
106
107
108[endsect]
109[endsect]
110
111
112[////////////////////////]
113[section:rationale Design Rationale]
114
115The 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.
116
117The 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.
118
119An 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:
120
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.
122
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.
124
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.
126
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.
128
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.)
130
131[heading Closure]
132
133One 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`.
134
135N3785 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:
136
137The 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.
138
139We 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.
140
141The 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.
142
143In addition `std::function<void()>` can not be constructed by moving the closure, so e.g. `std::packaged_task` could not be a Closure.
144
145[/
146[heading Scheduled work]
147
148The 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:
149
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.
152
153In order to manage with all the clocks, there are two alternatives:
154
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`.
157
158The library chose the first of those options, largely for simplicity.
159]
160
161[heading Scheduled work]
162
163The 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`.
164
165
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.
167
168If 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.
169
170      sch.on(tp).after(seconds(i)).submit(boost::bind(fn,i));
171
172This has several advantages:
173
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.
176
177In 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.
178
179[heading Not Handled Exceptions]
180As 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.
181Note 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.
182
183[heading At thread entry]
184
185It 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.
186
187For 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.
188
189[heading Cancelation]
190
191The library does not provision yet for the ability to cancel/interrupt work, though this is a commonly requested feature.
192
193This 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.
194
195We can think also of a cancelable closure that could be used in a more transparent way.
196
197An alternative is to make async return a cancelable_task but this will need also a cancelable closure.
198
199
200[/
201The library would provide in the future a cancelable_task that could support cancelation.
202
203  class cancelation_state
204  {
205    std::atomic<bool> requested;
206    std::atomic<bool> enabled;
207    std::condition_variable* cond;
208    std::mutex cond_mutex;
209  public:
210    cancelation_state() :
211      thread_cond(0)
212    {
213    }
214    void cancel()
215    {
216      requested.store(true, std::memory_order_relaxed);
217      std::lock_guard < std::mutex > lk(cond_mutex);
218      if (cond)
219      {
220        cond->notify_all();
221      }
222    }
223    bool cancellation_requested() const
224    {
225      return requested.load(std::memory_order_relaxed);
226    }
227    void enable()
228    {
229      enable.store(true, std::memory_order_relaxed);
230    }
231    void disable()
232    {
233      enable.store(false, std::memory_order_relaxed);
234    }
235    bool cancellation_enabled() const
236    {
237      return enabled.load(std::memory_order_relaxed);
238    }
239    void set_condition_variable(std::condition_variable& cv)
240    {
241      std::lock_guard < std::mutex > lk(cond_mutex);
242      cond = &cv;
243    }
244    void clear_condition_variable()
245    {
246      std::lock_guard < std::mutex > lk(cond_mutex);
247      cond = 0;
248    }
249    struct clear_cv_on_destruct
250    {
251      ~clear_cv_on_destruct()
252      {
253        this_thread_interrupt_flag.clear_condition_variable();
254      }
255    };
256    void cancelation_point();
257    void cancelable_wait(std::condition_variable& cv, std::unique_lock<std::mutex>& lk)
258    {
259      cancelation_point();
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();
265      cancelation_point();
266    }
267    class disable_cancelation
268    {
269    public:
270      disable_cancelation(const disable_cancelation&)= delete;
271      disable_cancelation& operator=(const disable_cancelation&)= delete;
272      disable_cancelation(cancelable_closure& closure)
273  noexcept    ;
274      ~disable_cancelation() noexcept;
275    };
276    class restore_cancelation
277    {
278    public:
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;
283    };
284  };
285
286  template <class Closure>
287  struct cancelable_closure_mixin: cancelable_closure
288  {
289    void operator()
290    {
291      cancel_point();this->Closure::run();
292    }
293  };
294
295  struct my_clousure: cancelable_closure_mixin<my_clousure>
296  {
297    void run()
298    {
299      while ()
300      {
301        cancel_point();
302      }
303    }
304  }
305]
306
307[heading Current executor]
308
309The library does not provision for the ability to get the current executor, though having access to it could simplify a lot the user code.
310
311The reason is that the user can always use a thread_local variable and reset it using the `at_thread_entry ` member function.
312
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(
316		// at_thread_entry
317		[](basic_thread_pool& pool) {
318			current_executor_state.set_current_executor(pool);
319		}
320	);
321
322[
323[heading Default executor]
324
325The 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.
326
327The user can always define his default executor himself.
328
329	boost::generic_executor_ref default_executor()
330	{
331  		static boost::basic_thread_pool tp(4);
332  		return generic_executor_ref(tp);
333	}
334
335
336[endsect]
337
338
339[/////////////////////]
340[section:ref Reference]
341
342
343[////////////////////////////////]
344[section:concept_closure Concept `Closure`]
345
346
347A type `E` meets the `Closure` requirements if is a model of `Callable(void())` and a model of `CopyConstructible`/`MoveConstructible`.
348
349[endsect]
350[////////////////////////////////]
351[section:concept_executor  Concept `Executor`]
352
353The `Executor` concept models the common operations of all the executors.
354
355A type `E` meets the `Executor` requirements if the following expressions are well-formed and have the specified semantics
356
357* `e.submit(lc);`
358* `e.submit(rc);`
359* `e.close();`
360* `b = e.closed();`
361* `e.try_executing_one();`
362* `e.reschedule_until(p);`
363
364where
365
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`
370
371[/////////////////////////////////////]
372[section:submitlc `e.submit(lc);`]
373
374[variablelist
375
376[[Effects:] [The specified closure will be scheduled for execution at some point in the future.
377If invoked closure throws an exception the executor will call std::terminate, as is the case with threads.]]
378
379[[Synchronization:] [completion of closure on a particular thread happens before destruction of thread's thread local variables.]]
380
381[[Return type:] [`void`.]]
382
383[[Throws:] [sync_queue_is_closed if the thread pool is closed. Whatever exception that can be throw while storing the closure.]]
384
385[[Exception safety:] [If an exception is thrown then the executor state is unmodified.]]
386
387]
388
389[endsect]
390[/////////////////////////////////////]
391[section:submitrc `e.submit(rc);`]
392
393[variablelist
394
395[[Effects:] [The specified closure will be scheduled for execution at some point in the future.
396If invoked closure throws an exception the executor will call std::terminate, as is the case with threads.]]
397
398[[Synchronization:] [completion of closure on a particular thread happens before destruction of thread's thread local variables.]]
399
400[[Return type:] [`void`.]]
401
402[[Throws:] [sync_queue_is_closed if the thread pool is closed. Whatever exception that can be throw while storing the closure.]]
403
404[[Exception safety:] [If an exception is thrown then the executor state is unmodified.]]
405
406]
407
408[endsect]
409[/////////////////////////////////////]
410[section:close `e.close();`]
411
412[variablelist
413
414[[Effects:] [close the executor `e` for submissions.]]
415
416[[Remark:] [The worker threads will work until there is no more closures to run.]]
417
418[[Return type:] [`void`.]]
419
420[[Throws:] [Whatever exception that can be thrown while ensuring the thread safety.]]
421
422[[Exception safety:] [If an exception is thrown then the executor state is unmodified.]]
423
424]
425
426[endsect]
427[/////////////////////////////////////]
428[section:closed `b = e.closed();`]
429
430[variablelist
431
432[[Return type:] [`bool`.]]
433
434[[Return:] [whether the executor is closed for submissions.]]
435
436[[Throws:] [Whatever exception that can be throw while ensuring the thread safety.]]
437
438
439]
440
441[endsect]
442[/////////////////////////////////////]
443[section:try_executing_one `e.try_executing_one();`]
444
445[variablelist
446
447[[Effects:] [try to execute one work.]]
448
449[[Remark:] [whether a work has been executed.]]
450
451[[Return type:] [`bool`.]]
452
453[[Return:] [Whether a work has been executed.]]
454
455[[Throws:] [whatever the current work constructor throws or the `work()` throws.]]
456
457]
458
459[endsect]
460[/////////////////////////////////////]
461[section:reschedule_until `e.reschedule_until(p);`]
462
463[variablelist
464
465[[Requires:] [This must be called from a scheduled work]]
466
467[[Effects:] [reschedule works until `p()`.]]
468
469[[Return type:] [`bool`.]]
470
471[[Return:] [Whether a work has been executed.]]
472
473[[Throws:] [whatever the current work constructor throws or the `work()` throws.]]
474
475]
476
477[endsect]
478
479[endsect]
480
481[/////////////////////////]
482[section:work Class `work`]
483
484  #include <boost/thread/executors/work.hpp>
485  namespace boost {
486    typedef 'implementation_defined' work;
487  }
488
489[variablelist
490
491[[Requires:] [work is a model of 'Closure']]
492
493]
494
495[endsect]
496
497[/////////////////////////////////]
498[section:executor Class `executor`]
499
500Executor abstract base class.
501
502  #include <boost/thread/executors/executor.hpp>
503  namespace boost {
504    class executor
505    {
506    public:
507      typedef  boost::work work;
508
509      executor(executor const&) = delete;
510      executor& operator=(executor const&) = delete;
511
512      executor();
513      virtual ~executor() {};
514
515      virtual void close() = 0;
516      virtual bool closed() = 0;
517
518      virtual void submit(work&& closure) = 0;
519      virtual void submit(work& closure) = 0;
520      template <typename Closure>
521      void submit(Closure&& closure);
522
523      virtual bool try_executing_one() = 0;
524      template <typename Pred>
525      bool reschedule_until(Pred const& pred);
526    };
527  }
528
529[/////////////////////////////////////]
530[section:constructor Constructor `executor()`]
531
532      executor();
533
534[variablelist
535
536[[Effects:] [Constructs an executor. ]]
537
538[[Throws:] [Nothing. ]]
539
540]
541
542
543[endsect]
544[/////////////////////////////////////]
545[section:constructor Destructor `~executor()`]
546
547      virtual ~executor();
548
549[variablelist
550
551[[Effects:] [Destroys the executor.]]
552
553[[Synchronization:] [The completion of all the closures happen before the completion of the executor destructor.]]
554
555]
556
557
558[endsect]
559
560[endsect]
561
562[//////////////////////////////////////////////////////////]
563[section:executor_adaptor Template Class `executor_adaptor`]
564
565Polymorphic adaptor of a model of Executor to an executor.
566
567  #include <boost/thread/executors/executor.hpp>
568  namespace boost {
569    template <typename Executor>
570    class executor_adaptor : public executor
571    {
572      Executor ex; // for exposition only
573    public:
574      typedef  executor::work work;
575
576      executor_adaptor(executor_adaptor const&) = delete;
577      executor_adaptor& operator=(executor_adaptor const&) = delete;
578
579      template <typename ...Args>
580      executor_adaptor(Args&& ... args);
581
582      Executor& underlying_executor() noexcept;
583
584      void close();
585      bool closed();
586
587      void submit(work&& closure);
588      void submit(work& closure);
589
590      bool try_executing_one();
591
592    };
593  }
594
595[/////////////////////////////////////]
596[section:constructor Constructor `executor_adaptor(Args&& ...)`]
597
598      template <typename ...Args>
599      executor_adaptor(Args&& ... args);
600
601[variablelist
602
603[[Effects:] [Constructs an executor_adaptor. ]]
604
605[[Throws:] [Nothing. ]]
606
607]
608
609
610[endsect]
611[/////////////////////////////////////]
612[section:destructor Destructor `~executor_adaptor()`]
613
614      virtual ~executor_adaptor();
615
616[variablelist
617
618[[Effects:] [Destroys the executor_adaptor.]]
619
620[[Synchronization:] [The completion of all the closures happen before the completion of the executor destructor.]]
621
622]
623
624[endsect]
625[/////////////////////////////////////]
626[section:underlying_executor Function member `underlying_executor()`]
627
628      Executor& underlying_executor() noexcept;
629
630[variablelist
631
632[[Return:] [The underlying executor instance. ]]
633
634]
635
636
637[endsect]
638
639[endsect]
640
641[/////////////////////////////////]
642[section:generic_executor_ref Class `generic_executor_ref`]
643
644Executor abstract base class.
645
646  #include <boost/thread/executors/generic_executor_ref.hpp>
647  namespace boost {
648    class generic_executor_ref
649    {
650    public:
651      generic_executor_ref(generic_executor_ref const&);
652      generic_executor_ref& operator=(generic_executor_ref const&);
653
654      template <class Executor>
655      generic_executor_ref(Executor& ex);
656      generic_executor_ref() {};
657
658      void close() = 0;
659      bool closed() = 0;
660
661      template <typename Closure>
662      void submit(Closure&& closure);
663
664      virtual bool try_executing_one() = 0;
665      template <typename Pred>
666      bool reschedule_until(Pred const& pred);
667    };
668  }
669
670[endsect]
671
672[//////////////////////////////////////////////////////////]
673[section: scheduler Template Class `scheduler `]
674
675Scheduler providing time related functions. Note that `scheduler` is not an Executor.
676
677  #include <boost/thread/executors/scheduler.hpp>
678  namespace boost {
679
680    template <class Clock=steady_clock>
681    class scheduler
682    {
683    public:
684      using work = boost::function<void()> ;
685      using clock = Clock;
686
687      scheduler(scheduler const&) = delete;
688      scheduler& operator=(scheduler const&) = delete;
689
690      scheduler();
691      ~scheduler();
692
693      void close();
694      bool closed();
695
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);
700
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);
705
706      template <class Executor>
707      scheduler_executor_wrapper<scheduler, Executor> on(Executor& ex);
708
709    };
710  }
711
712[/////////////////////////////////////]
713[section:constructor Constructor `scheduler()`]
714
715      scheduler();
716
717[variablelist
718
719[[Effects:] [Constructs a `scheduler`. ]]
720
721[[Throws:] [Nothing. ]]
722
723]
724
725
726[endsect]
727[/////////////////////////////////////]
728[section:destructor Destructor `~scheduler()`]
729
730      ~scheduler();
731
732[variablelist
733
734[[Effects:] [Destroys the scheduler.]]
735
736[[Synchronization:] [The completion of all the closures happen before the completion of the executor destructor.]]
737
738]
739
740[endsect]
741[/////////////////////////////////////]
742[section:submit_at Template Function Member `submit_at()`]
743
744      template <class Clock, class Duration, typename Closure>
745      void submit_at(chrono::time_point<Clock,Duration> abs_time, Closure&& closure);
746
747[variablelist
748
749[[Effects:] [Schedule a `closure` to be executed at `abs_time`. ]]
750
751[[Throws:] [Nothing.]]
752
753]
754
755
756[endsect]
757[/////////////////////////////////////]
758[section:submit_after Template Function Member `submit_after()`]
759
760      template <class Rep, class Period, typename Closure>
761      void submit_after(chrono::duration<Rep,Period> rel_time, Closure&& closure);
762
763[variablelist
764
765[[Effects:] [Schedule a `closure` to be executed after `rel_time`. ]]
766
767[[Throws:] [Nothing.]]
768
769]
770
771
772[endsect]
773
774[endsect]
775
776[//////////////////////////////////////////////////////////]
777[section:at_executor Template Class `at_executor`]
778
779
780  #include <boost/thread/executors/scheduler.hpp>
781  namespace boost {
782
783    template <class Scheduler>
784    class at_executor
785    {
786    public:
787      using work = Scheduler::work;
788      using clock = Scheduler::clock;
789
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;
794
795      at_executor(Scheduler& sch, clock::time_point const& tp);
796      ~at_executor();
797
798      void close();
799      bool closed();
800
801      Scheduler& underlying_scheduler();
802
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);
809
810      template <class Executor>
811      resubmit_at_executor<Scheduler, Executor> on(Executor& ex);
812
813    };
814  }
815
816[/////////////////////////////////////]
817[section:constructor Constructor `at_executor(Scheduler&, clock::time_point const&)`]
818
819      at_executor(Scheduler& sch, clock::time_point const& tp);
820
821[variablelist
822
823[[Effects:] [Constructs a `at_executor`. ]]
824
825[[Throws:] [Nothing. ]]
826
827]
828
829
830[endsect]
831[/////////////////////////////////////]
832[section:destructor Destructor `~at_executor()`]
833
834      ~at_executor();
835
836[variablelist
837
838[[Effects:] [Destroys the `at_executor`.]]
839
840[[Synchronization:] [The completion of all the closures happen before the completion of the executor destructor.]]
841
842]
843
844[endsect]
845[/////////////////////////////////////]
846[section:underlying_scheduler Function member `underlying_scheduler()`]
847
848      Scheduler& underlying_scheduler() noexcept;
849
850[variablelist
851
852[[Return:] [The underlying scheduler instance. ]]
853
854]
855
856[endsect]
857[/////////////////////////////////////]
858[section:submit_at Template Function Member `submit()`]
859
860      template <typename Closure>
861      void submit(Closure&& closure);
862
863[variablelist
864
865[[Effects:] [Schedule the `closure` to be executed at the `abs_time` given at construction time. ]]
866
867[[Throws:] [Nothing.]]
868
869]
870
871[endsect]
872[/////////////////////////////////////]
873[section:submit_at Template Function Member `submit_at()`]
874
875      template <class Clock, class Duration, typename Closure>
876      void submit_at(chrono::time_point<Clock,Duration> abs_time, Closure&& closure);
877
878[variablelist
879
880[[Effects:] [Schedule a `closure` to be executed at `abs_time`. ]]
881
882[[Throws:] [Nothing.]]
883
884]
885
886
887[endsect]
888[/////////////////////////////////////]
889[section:submit_after Template Function Member `submit_after()`]
890
891      template <class Rep, class Period, typename Closure>
892      void submit_after(chrono::duration<Rep,Period> rel_time, Closure&& closure);
893
894[variablelist
895
896[[Effects:] [Schedule a `closure` to be executed after `rel_time`. ]]
897
898[[Throws:] [Nothing.]]
899
900]
901
902
903[endsect]
904
905[endsect]
906
907
908[//////////////////////////////////////////////////////////]
909[section:scheduler_executor_wrapper Template Class `scheduler_executor_wrapper`]
910
911  #include <boost/thread/executors/scheduler.hpp>
912  namespace boost {
913
914    template <class Scheduler, class Executor>
915    class scheduler_executor_wrapper
916    {
917    public:
918      using work = Scheduler::work;
919      using clock = Scheduler::clock;
920
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;
925
926      scheduler_executor_wrapper(Scheduler& sch, Executor& ex);
927
928      ~scheduler_executor_wrapper();
929
930      void close();
931      bool closed();
932
933      Executor& underlying_executor();
934      Scheduler& underlying_scheduler();
935
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);
942
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);
947
948    };
949  }
950
951[/////////////////////////////////////]
952[section:constructor Constructor `scheduler_executor_wrapper(Scheduler&, Executor&)`]
953
954      scheduler_executor_wrapper(Scheduler& sch, Executor& ex);
955
956[variablelist
957
958[[Effects:] [Constructs a `scheduler_executor_wrapper`. ]]
959
960[[Throws:] [Nothing. ]]
961
962]
963
964[endsect]
965[/////////////////////////////////////]
966[section:destructor Destructor `~scheduler_executor_wrapper()`]
967
968      ~scheduler_executor_wrapper();
969
970[variablelist
971
972[[Effects:] [Destroys the `scheduler_executor_wrapper`.]]
973
974[[Synchronization:] [The completion of all the closures happen before the completion of the executor destructor.]]
975
976]
977
978[endsect]
979[/////////////////////////////////////]
980[section:underlying_scheduler Function member `underlying_scheduler()`]
981
982      Scheduler& underlying_scheduler() noexcept;
983
984[variablelist
985
986[[Return:] [The underlying scheduler instance. ]]
987
988]
989
990[endsect]
991[/////////////////////////////////////]
992[section:underlying_executor Function member `underlying_executor()`]
993
994      Executor& underlying_executor() noexcept;
995
996[variablelist
997
998[[Return:] [The underlying executor instance. ]]
999
1000]
1001
1002[endsect]
1003[/////////////////////////////////////]
1004[section:submit_at Template Function Member `submit()`]
1005
1006      template <typename Closure>
1007      void submit(Closure&& closure);
1008
1009[variablelist
1010
1011[[Effects:] [Submit the `closure` on the underlying executor. ]]
1012
1013[[Throws:] [Nothing.]]
1014
1015]
1016
1017[endsect]
1018[/////////////////////////////////////]
1019[section:submit_at Template Function Member `submit_at()`]
1020
1021      template <class Clock, class Duration, typename Closure>
1022      void submit_at(chrono::time_point<Clock,Duration> abs_time, Closure&& closure);
1023
1024[variablelist
1025
1026[[Effects:] [Resubmit the `closure` to be executed on the underlying executor at `abs_time`. ]]
1027
1028[[Throws:] [Nothing.]]
1029
1030]
1031
1032[endsect]
1033[/////////////////////////////////////]
1034[section:submit_after Template Function Member `submit_after()`]
1035
1036      template <class Rep, class Period, typename Closure>
1037      void submit_after(chrono::duration<Rep,Period> rel_time, Closure&& closure);
1038
1039[variablelist
1040
1041[[Effects:] [Resubmit the `closure` to be executed on the underlying executor after `rel_time`. ]]
1042
1043[[Throws:] [Nothing.]]
1044
1045]
1046
1047[endsect]
1048
1049[endsect]
1050
1051
1052[//////////////////////////////////////////////////////////]
1053[section:resubmit_at_executor Template Class `resubmit_at_executor`]
1054
1055`Executor` wrapping an `Scheduler`, an `Executor` and a `time_point` providing an `Executor` interface.
1056
1057  #include <boost/thread/executors/scheduler.hpp>
1058  namespace boost {
1059
1060    template <class Scheduler, class Executor>
1061    class resubmit_at_executor
1062    {
1063    public:
1064      using work = Scheduler::work;
1065      using clock = Scheduler::clock;
1066
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;
1071
1072      template <class Duration>
1073      resubmit_at_executor(Scheduler& sch, Executor& ex, clock::time_point<Duration> const& tp);
1074      ~resubmit_at_executor();
1075
1076      void close();
1077      bool closed();
1078
1079      Executor& underlying_executor();
1080      Scheduler& underlying_scheduler();
1081
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);
1088
1089    };
1090  }
1091
1092
1093[/////////////////////////////////////]
1094[section:constructor Constructor `resubmit_at_executor(Scheduler&, Executor&, clock::time_point<Duration>)`]
1095
1096      template <class Duration>
1097      resubmit_at_executor(Scheduler& sch, Executor& ex, clock::time_point<Duration> const& tp);
1098
1099[variablelist
1100
1101[[Effects:] [Constructs a `resubmit_at_executor`. ]]
1102
1103[[Throws:] [Nothing. ]]
1104
1105]
1106
1107
1108[endsect]
1109[/////////////////////////////////////]
1110[section:destructor Destructor `~resubmit_at_executor()`]
1111
1112      ~resubmit_at_executor();
1113
1114[variablelist
1115
1116[[Effects:] [Destroys the executor_adaptor.]]
1117
1118[[Synchronization:] [The completion of all the closures happen before the completion of the executor destructor.]]
1119
1120]
1121
1122[endsect]
1123[/////////////////////////////////////]
1124[section:underlying_executor Function member `underlying_executor()`]
1125
1126      Executor& underlying_executor() noexcept;
1127
1128[variablelist
1129
1130[[Return:] [The underlying executor instance. ]]
1131
1132]
1133
1134[endsect]
1135[/////////////////////////////////////]
1136[section:underlying_scheduler Function member `underlying_scheduler()`]
1137
1138      Scheduler& underlying_scheduler() noexcept;
1139
1140[variablelist
1141
1142[[Return:] [The underlying scheduler instance. ]]
1143
1144]
1145
1146[endsect]
1147[/////////////////////////////////////]
1148[section:submit_at Template Function Member `submit()`]
1149
1150      template <typename Closure>
1151      void submit(Closure&& closure);
1152
1153[variablelist
1154
1155[[Effects:] [Resubmit the `closure` to be executed on the underlying executor at the `abs_time` given at construction time. ]]
1156
1157[[Throws:] [Nothing.]]
1158
1159]
1160
1161[endsect]
1162[/////////////////////////////////////]
1163[section:submit_at Template Function Member `submit_at()`]
1164
1165      template <class Clock, class Duration, typename Closure>
1166      void submit_at(chrono::time_point<Clock,Duration> abs_time, Closure&& closure);
1167
1168[variablelist
1169
1170[[Effects:] [Resubmit the `closure` to be executed on the underlying executor at `abs_time`. ]]
1171
1172[[Throws:] [Nothing.]]
1173
1174]
1175
1176[endsect]
1177[/////////////////////////////////////]
1178[section:submit_after Template Function Member `submit_after()`]
1179
1180      template <class Rep, class Period, typename Closure>
1181      void submit_after(chrono::duration<Rep,Period> rel_time, Closure&& closure);
1182
1183[variablelist
1184
1185[[Effects:] [Resubmit the `closure` to be executed on the underlying executor after `rel_time`. ]]
1186
1187[[Throws:] [Nothing.]]
1188
1189]
1190
1191[endsect]
1192
1193[endsect]
1194
1195
1196[//////////////////////////////////////////////////////////]
1197[/
1198[section:scheduled_executor_ref Template Class `scheduled_executor_ref`]
1199
1200Executor providing time related functions.
1201
1202  #include <boost/thread/executors/scheduled_executor_ref.hpp>
1203  namespace boost {
1204    template <class Executor>
1205    class scheduled_executor_ref
1206    {
1207	Executor& ex;
1208    public:
1209      typedef  executor::work work;
1210
1211      scheduled_executor_ref(scheduled_executor_ref const&) = delete;
1212      scheduled_executor_ref& operator=(scheduled_executor_ref const&) = delete;
1213
1214      template <class Rep, class Period>
1215      scheduled_executor_ref(Executor& ex, chrono::duration<Rep, Period> granularity=chrono::milliseconds(100));
1216
1217      Executor& underlying_executor() noexcept;
1218
1219      void close();
1220      bool closed();
1221
1222      void submit(work&& closure);
1223      void submit(work& closure);
1224      template <typename Closure>
1225      void submit(Closure&& closure);
1226
1227      bool try_executing_one();
1228      template <typename Pred>
1229      bool reschedule_until(Pred const& pred);
1230
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);
1235    };
1236  }
1237
1238[/////////////////////////////////////]
1239[section:constructor Constructor `scheduled_executor_ref(Executor&, chrono::duration<Rep, Period>)`]
1240
1241      template <class Rep, class Period>
1242      scheduled_executor_ref(Executor& ex, chrono::duration<Rep, Period> granularity=chrono::milliseconds(100));
1243
1244[variablelist
1245
1246[[Effects:] [Constructs a scheduled_executor_ref. ]]
1247
1248[[Throws:] [Nothing. ]]
1249
1250]
1251
1252
1253[endsect]
1254[/////////////////////////////////////]
1255[section:destructor Destructor `~scheduled_executor_ref()`]
1256
1257      ~scheduled_executor_ref();
1258
1259[variablelist
1260
1261[[Effects:] [Destroys the executor_adaptor.]]
1262
1263[[Synchronization:] [The completion of all the closures happen before the completion of the executor destructor.]]
1264
1265]
1266
1267[endsect]
1268[/////////////////////////////////////]
1269[section:underlying_executor Function member `underlying_executor()`]
1270
1271      Executor& underlying_executor() noexcept;
1272
1273[variablelist
1274
1275[[Return:] [The underlying executor instance. ]]
1276
1277]
1278
1279[endsect]
1280[/////////////////////////////////////]
1281[section:submit_at Template Function Member `submit()`]
1282
1283      template <typename Closure>
1284      void submit(Closure&& closure);
1285
1286[variablelist
1287
1288[[Effects:] [Resubmit the `closure` to be executed on the underlying executor. ]]
1289
1290[[Throws:] [Nothing.]]
1291
1292]
1293
1294[endsect]
1295[/////////////////////////////////////]
1296[section:submit_at Template Function Member `submit_at()`]
1297
1298      template <class Clock, class Duration, typename Closure>
1299      void submit_at(chrono::time_point<Clock,Duration> abs_time, Closure&& closure);
1300
1301[variablelist
1302
1303[[Effects:] [Schedule a `closure` to be executed at `abs_time`. ]]
1304
1305[[Throws:] [Nothing.]]
1306
1307]
1308
1309
1310[endsect]
1311[/////////////////////////////////////]
1312[section:submit_after Template Function Member `submit_after()`]
1313
1314      template <class Rep, class Period, typename Closure>
1315      void submit_after(chrono::duration<Rep,Period> rel_time, Closure&& closure);
1316
1317[variablelist
1318
1319[[Effects:] [Schedule a `closure` to be executed after `rel_time`. ]]
1320
1321[[Throws:] [Nothing.]]
1322
1323]
1324
1325[endsect]
1326
1327
1328[endsect]
1329]
1330
1331[//////////////////////////////////////////////////////////]
1332[section:serial_executor Template Class `serial_executor`]
1333
1334A serial executor ensuring that there are no two work units that executes concurrently.
1335
1336  #include <boost/thread/executors/serial_executor.hpp>
1337  namespace boost {
1338    template <class Executor>
1339    class serial_executor
1340    {
1341    public:
1342      serial_executor(serial_executor const&) = delete;
1343      serial_executor& operator=(serial_executor const&) = delete;
1344
1345      template <class Executor>
1346      serial_executor(Executor& ex);
1347
1348      Executor& underlying_executor() noexcept;
1349
1350      void close();
1351      bool closed();
1352
1353      template <typename Closure>
1354      void submit(Closure&& closure);
1355
1356      bool try_executing_one();
1357      template <typename Pred>
1358      bool reschedule_until(Pred const& pred);
1359
1360    };
1361  }
1362
1363[/////////////////////////////////////]
1364[section:constructor Constructor `serial_executor(Executor&)`]
1365
1366      template <class Executor>
1367      serial_executor(Executor& ex);
1368
1369[variablelist
1370
1371[[Effects:] [Constructs a serial_executor. ]]
1372
1373[[Throws:] [Nothing. ]]
1374
1375]
1376
1377
1378[endsect]
1379[/////////////////////////////////////]
1380[section:destructor Destructor `~serial_executor()`]
1381
1382      ~serial_executor();
1383
1384[variablelist
1385
1386[[Effects:] [Destroys the serial_executor.]]
1387
1388[[Synchronization:] [The completion of all the closures happen before the completion of the executor destructor.]]
1389
1390]
1391
1392[endsect]
1393[/////////////////////////////////////]
1394[section:underlying_executor Function member `underlying_executor()`]
1395
1396      generic_executor_ref& underlying_executor() noexcept;
1397
1398[variablelist
1399
1400[[Return:] [The underlying executor instance. ]]
1401
1402[[Throws:] [Nothing.]]
1403
1404]
1405
1406
1407[endsect]
1408
1409[endsect]
1410
1411
1412[//////////////////////////////////////////////////////////]
1413[section:inline_executor Class `inline_executor`]
1414
1415A serial executor ensuring that there are no two work units that executes concurrently.
1416
1417  #include <boost/thread/executors/inline_executor.hpp>
1418  namespace boost {
1419    class inline_executor
1420    {
1421    public:
1422      inline_executor(inline_executor const&) = delete;
1423      inline_executor& operator=(inline_executor const&) = delete;
1424
1425      inline_executor();
1426
1427      void close();
1428      bool closed();
1429
1430      template <typename Closure>
1431      void submit(Closure&& closure);
1432
1433      bool try_executing_one();
1434      template <typename Pred>
1435      bool reschedule_until(Pred const& pred);
1436
1437    };
1438  }
1439
1440[/////////////////////////////////////]
1441[section:constructor Constructor `inline_executor()`]
1442
1443      inline_executor();
1444
1445[variablelist
1446
1447[[Effects:] [Constructs an inline_executor. ]]
1448
1449[[Throws:] [Nothing. ]]
1450
1451]
1452
1453
1454[endsect]
1455[/////////////////////////////////////]
1456[section:destructor Destructor `~inline_executor()`]
1457
1458      ~inline_executor();
1459
1460[variablelist
1461
1462[[Effects:] [Destroys the inline_executor.]]
1463
1464[[Synchronization:] [The completion of all the closures happen before the completion of the executor destructor.]]
1465
1466]
1467
1468[endsect]
1469
1470
1471[endsect]
1472
1473
1474
1475
1476[///////////////////////////////////////]
1477[section:basic_thread_pool Class `basic_thread_pool`]
1478
1479A thread pool with up to a fixed number of threads.
1480
1481  #include <boost/thread/executors/basic_thread_pool.hpp>
1482  namespace boost {
1483    class basic_thread_pool
1484    {
1485    public:
1486
1487      basic_thread_pool(basic_thread_pool const&) = delete;
1488      basic_thread_pool& operator=(basic_thread_pool const&) = delete;
1489
1490      basic_thread_pool(unsigned const thread_count = thread::hardware_concurrency());
1491      template <class AtThreadEntry>
1492      basic_thread_pool( unsigned const thread_count, AtThreadEntry at_thread_entry);
1493      ~basic_thread_pool();
1494
1495      void close();
1496      bool closed();
1497
1498      template <typename Closure>
1499      void submit(Closure&& closure);
1500
1501      bool try_executing_one();
1502
1503      template <typename Pred>
1504      bool reschedule_until(Pred const& pred);
1505
1506    };
1507  }
1508
1509[/////////////////////////////////////]
1510[section:constructor Constructor `basic_thread_pool(unsigned const)`]
1511
1512[variablelist
1513
1514[[Effects:] [creates a thread pool that runs closures on `thread_count` threads. ]]
1515
1516[[Throws:] [Whatever exception is thrown while initializing the needed resources. ]]
1517
1518]
1519
1520
1521[endsect]
1522[/////////////////////////////////////]
1523[section:destructor Destructor `~basic_thread_pool()`]
1524
1525     ~basic_thread_pool();
1526
1527[variablelist
1528
1529[[Effects:] [Interrupts and joins all the threads and then destroys the threads.]]
1530
1531[[Synchronization:] [The completion of all the closures happen before the completion of the executor destructor.]]
1532
1533]
1534[endsect]
1535
1536[endsect]
1537
1538[///////////////////////////////////////]
1539[section:thread_executor Class `thread_executor`]
1540
1541A thread_executor with a threads for each task.
1542
1543  #include <boost/thread/executors/thread_executor.hpp>
1544  namespace boost {
1545    class thread_executor
1546    {
1547    public:
1548
1549      thread_executor(thread_executor const&) = delete;
1550      thread_executor& operator=(thread_executor const&) = delete;
1551
1552      thread_executor();
1553      template <class AtThreadEntry>
1554      basic_thread_pool( unsigned const thread_count, AtThreadEntry at_thread_entry);
1555      ~thread_executor();
1556
1557      void close();
1558      bool closed();
1559
1560      template <typename Closure>
1561      void submit(Closure&& closure);
1562
1563    };
1564  }
1565
1566[/////////////////////////////////////]
1567[section:constructor Constructor `thread_executor()`]
1568
1569[variablelist
1570
1571[[Effects:] [creates a thread_executor. ]]
1572
1573[[Throws:] [Whatever exception is thrown while initializing the needed resources. ]]
1574
1575]
1576
1577
1578[endsect]
1579[/////////////////////////////////////]
1580[section:destructor Destructor `~thread_executor()`]
1581
1582     ~thread_executor();
1583
1584[variablelist
1585
1586[[Effects:] [Waits for closures (if any) to complete, then joins and destroys the threads.]]
1587
1588[[Synchronization:] [The completion of all the closures happen before the completion of the executor destructor.]]
1589
1590]
1591[endsect]
1592
1593[endsect]
1594
1595
1596[/////////////////////////////////]
1597[section:loop_executor Class `loop_executor`]
1598
1599A user scheduled executor.
1600
1601  #include <boost/thread/executors/loop_executor.hpp>
1602  namespace boost {
1603    class loop_executor
1604    {
1605    public:
1606
1607      loop_executor(loop_executor const&) = delete;
1608      loop_executor& operator=(loop_executor const&) = delete;
1609
1610      loop_executor();
1611      ~loop_executor();
1612
1613      void close();
1614      bool closed();
1615
1616      template <typename Closure>
1617      void submit(Closure&& closure);
1618
1619      bool try_executing_one();
1620      template <typename Pred>
1621      bool reschedule_until(Pred const& pred);
1622
1623      void loop();
1624      void run_queued_closures();
1625    };
1626  }
1627
1628[/////////////////////////////////////]
1629[section:constructor Constructor `loop_executor()`]
1630
1631      loop_executor();
1632
1633[variablelist
1634
1635[[Effects:] [creates an executor that runs closures using one of its closure-executing methods. ]]
1636
1637[[Throws:] [Whatever exception is thrown while initializing the needed resources. ]]
1638
1639]
1640
1641
1642[endsect]
1643[/////////////////////////////////////]
1644[section:destructor Destructor `~loop_executor()`]
1645
1646      virtual ~loop_executor();
1647
1648[variablelist
1649
1650[[Effects:] [Destroys the executor.]]
1651
1652[[Synchronization:] [The completion of all the closures happen before the completion of the executor destructor.]]
1653
1654]
1655[endsect]
1656[/////////////////////////////////////]
1657[section:loop Function member `loop()`]
1658
1659      void loop();
1660
1661[variablelist
1662
1663[[Return:] [reschedule works until `closed()` or empty. ]]
1664
1665[[Throws:] [whatever the current work constructor throws or the `work()` throws.]]
1666
1667]
1668
1669
1670[endsect]
1671
1672[/////////////////////////////////////]
1673[section:run_queued_closures Function member `run_queued_closures()`]
1674
1675      void run_queued_closures();
1676
1677[variablelist
1678
1679[[Return:] [reschedule the enqueued works. ]]
1680
1681[[Throws:] [whatever the current work constructor throws or the `work()` throws.]]
1682
1683]
1684
1685
1686[endsect]
1687
1688
1689
1690[endsect]
1691
1692[endsect]
1693
1694[endsect]
1695