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