You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -247,6 +246,8 @@ This executor is good for long running tasks, like objects that run a work loop,
247
246
248
247
***manual executor** - an executor that does not execute coroutines by itself. Application code can execute previously enqueued tasks by manually invoking its execution methods.
249
248
249
+
***derivable executor** - a base class for user defined executors. Although inheriting directly from `concurrencpp::executor` is possible, `derivable_executor` uses the `CRTP` pattern that provides some optimization opportunities for the compiler.
250
+
250
251
***inline executor** - mainly used to override the behavior of other executors. Enqueuing a task is equivalent to invoking it inline.
251
252
252
253
#### Using executors
@@ -358,6 +359,7 @@ class result{
358
359
In either way, after resuming, if the result is a valid value, it is returned.
359
360
Otherwise, operator co_await rethrows the asynchronous exception.
360
361
Throws concurrencpp::errors::empty_result if *this is empty.
362
+
Throws std::invalid_argument if executor is null.
361
363
If this result is ready and force_rescheduling=true, throws any exception that executor::enqueue may throw.
362
364
*/
363
365
auto await_via(
@@ -380,6 +382,7 @@ class result{
380
382
if force_rescheduling = false, then the current coroutine resumes immediately in the calling thread of execution.
381
383
In either way, after resuming, *this is returned in a non-empty form and guaranteed that its status is not result_status::idle.
382
384
Throws concurrencpp::errors::empty_result if *this is empty.
385
+
Throws std::invalid_argument if executor is null.
383
386
If this result is ready and force_rescheduling=true, throws any exception that executor::enqueue may throw.
concurrencpp also provides timers and timer queues.
645
-
Timers are objects that schedule actions to run on an executor within a well-defined interval of time. There are three types of timers - *regular timers*, *onshot-timers* and *delay objects*.
646
-
A timer queue is a concurrencpp worker that manages a collection of timers and processes them in just one thread of execution. In order to create timers, one must use the timer queue in conjunction with an executor.
649
+
Timers are objects that define actions which run on an executor within a well-defined interval of time. There are three types of timers - *regular timers*, *onshot-timers* and *delay objects*.
647
650
648
651
Timers have four properties that describe them:
649
652
@@ -652,16 +655,38 @@ Timers have four properties that describe them:
652
655
1. Due time - from the time of creation, the interval in milliseconds in which the timer will be scheduled to run for the first time
653
656
1. Frequency - from the time the timer callable was scheduled for the first time, the interval in milliseconds the callable will be schedule to run periodically, until the timer is destructed or cancelled.
654
657
658
+
A timer queue is a concurrencpp worker that manages a collection of timers and processes them in just one thread of execution.
659
+
When a timer deadline (whether its due-time or frequency) has reached, the timer queue "fires" the timer by scheduling its callable to run on the timer given executor.
660
+
Just like executors, timer queues also adhere to the RAII concpet. When the runtime object gets out of scope, It shuts down the timer queue, cancelling all pending timers.
661
+
After a timer queue has been shut down, any subsequent call to `make_timer`, `make_onshot_timer` and `make_delay_object` will throw an `errors::timer_queue_shutdown` exceptions.
662
+
Applications must not try to shut down timer queues by themselves.
663
+
655
664
#### `timer_queue` API:
656
665
```cpp
657
666
class timer_queue {
658
667
/*
659
668
Destroyes *this and cancels all associated timers.
660
669
*/
661
670
~timer_queue() noexcept;
671
+
672
+
/*
673
+
Shuts down this timer_queue.
674
+
After this call, invocation of any method besides shutdown
675
+
and shutdown_requested will throw an errors::timer_queue_shutdown.
676
+
If shutdown had been called before, this method has no effect.
677
+
*/
678
+
void shutdown() noexcept;
679
+
680
+
/*
681
+
Returns true if shutdown had been called before, false otherwise.
682
+
*/
683
+
bool shutdown_requested() const noexcept;
684
+
662
685
663
686
/*
664
-
Creates a new running timer where *this is the associated timer_queue
687
+
Creates a new running timer where *this is the associated timer_queue.
688
+
Throws std::invalid_argument if executor is null.
689
+
Throws errors::timer_queue_shutdown if shutdown had been called before.
665
690
*/
666
691
template<class callable_type>
667
692
timer make_timer(
@@ -671,7 +696,9 @@ class timer_queue {
671
696
callable_type&& callable);
672
697
673
698
/*
674
-
Creates a new running timer where *this is associated timer_queue
699
+
Creates a new running timer where *this is associated timer_queue.
700
+
Throws std::invalid_argument if executor is null.
701
+
Throws errors::timer_queue_shutdown if shutdown had been called before.
675
702
*/
676
703
template<class callable_type, class ... argumet_types>
677
704
timer make_timer(
@@ -682,7 +709,9 @@ class timer_queue {
682
709
argumet_types&& ... arguments);
683
710
684
711
/*
685
-
Creates a new one shot timer where *this is associated timer_queue
712
+
Creates a new one shot timer where *this is associated timer_queue.
713
+
Throws std::invalid_argument if executor is null.
714
+
Throws errors::timer_queue_shutdown if shutdown had been called before.
686
715
*/
687
716
template<class callable_type>
688
717
timer make_one_shot_timer(
@@ -691,7 +720,9 @@ class timer_queue {
691
720
callable_type&& callable);
692
721
693
722
/*
694
-
Creates a new one shot timer where *this is associated timer_queue
723
+
Creates a new one shot timer where *this is associated timer_queue.
724
+
Throws std::invalid_argument if executor is null.
725
+
Throws errors::timer_queue_shutdown if shutdown had been called before.
695
726
*/
696
727
template<class callable_type, class ... argumet_types>
697
728
timer make_one_shot_timer(
@@ -701,7 +732,9 @@ class timer_queue {
701
732
argumet_types&& ... arguments);
702
733
703
734
/*
704
-
Creates a new delay object where *this is associated timer_queue
735
+
Creates a new delay object where *this is associated timer_queue.
736
+
Throws std::invalid_argument if executor is null.
737
+
Throws errors::timer_queue_shutdown if shutdown had been called before.
As mentioned before, Applications can create their own custom executor type by implementing the `executor` interface. There are a few points to consider when implementing user defined executors:
973
+
As mentioned before, Applications can create their own custom executor type by inheriting the `derivable_executor` class. There are a few points to consider when implementing user defined executors:
941
974
The most important thing is to remember that executors are used from multiple threads, so implemented methods must be thread-safe.
942
975
Another important thing is to handle shutdown correctly: `shutdown`, `shutdown_requested` and `enqueue` should all monitor the executor state and behave accordingly when invoked:
943
976
*`shutdown` should tell underlying threads to quit and then join them. `shutdown` must also destroy each unexecuted `coroutine_handle` by calling `coroutine_handle::destroy`.
@@ -956,7 +989,7 @@ Another important thing is to handle shutdown correctly: `shutdown`, `shutdown_r
0 commit comments