1 // Copyright 2022 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 #pragma once 15 16 #include "pw_chrono/system_clock.h" 17 18 namespace pw::async { 19 20 class Task; 21 22 /// Abstract base class for an asynchronous dispatcher loop. 23 /// 24 /// `Dispatcher`s run many short, non-blocking units of work on a single thread. 25 /// This approach has a number of advantages compared with executing concurrent 26 /// tasks on separate threads: 27 /// 28 /// - `Dispatcher`s can make more efficient use of system resources, since they 29 /// don't need to maintain separate thread stacks. 30 /// - `Dispatcher`s can run on systems without thread support, such as no-RTOS 31 /// embedded environments. 32 /// - `Dispatcher`s allow tasks to communicate with one another without the 33 /// synchronization overhead of locks, atomics, fences, or `volatile`. 34 /// 35 /// Thread support: `Dispatcher` methods may be safely invoked from any thread, 36 /// but the resulting tasks will always execute on a single thread. Whether 37 /// or not methods may be invoked from interrupt context is 38 /// implementation-defined. 39 /// 40 /// `VirtualSystemClock`: `Dispatcher` implements `VirtualSystemClock` in order 41 /// to provide a consistent source of (possibly mocked) time information to 42 /// tasks. 43 /// 44 /// A simple default dispatcher implementation is provided by `pw_async_basic`. 45 class Dispatcher : public chrono::VirtualSystemClock { 46 public: 47 ~Dispatcher() override = default; 48 49 /// Post caller-owned |task| to be run on the dispatch loop. 50 /// 51 /// Posted tasks execute in the order they are posted. This ensures that 52 /// tasks can re-post themselves and yield in order to allow other tasks the 53 /// opportunity to execute. 54 /// 55 /// A given |task| must only be posted to a single `Dispatcher`. Post(Task & task)56 virtual void Post(Task& task) { PostAt(task, now()); } 57 58 /// Post caller owned |task| to be run after |delay|. 59 /// 60 /// If |task| was already posted to run at an earlier time (before |delay| 61 /// would expire), |task| must be run at the earlier time, and |task| 62 /// *may* also be run at the later time. PostAfter(Task & task,chrono::SystemClock::duration delay)63 virtual void PostAfter(Task& task, chrono::SystemClock::duration delay) { 64 PostAt(task, now() + delay); 65 } 66 67 /// Post caller owned |task| to be run at |time|. 68 /// 69 /// If |task| was already posted to run before |time|, 70 /// |task| must be run at the earlier time, and |task| *may* also be run at 71 /// the later time. 72 virtual void PostAt(Task& task, chrono::SystemClock::time_point time) = 0; 73 74 /// Prevent a `Post`ed task from starting. 75 /// 76 /// Returns: 77 /// true: the task was successfully canceled and will not be run by the 78 /// dispatcher until `Post`ed again. 79 /// false: the task could not be cancelled because it either was not 80 /// posted, already ran, or is currently running on the `Dispatcher` 81 /// thread. 82 virtual bool Cancel(Task& task) = 0; 83 }; 84 85 } // namespace pw::async 86