1 // Copyright 2023 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_async2/dispatcher_base.h" 17 #include "pw_async2/dispatcher_native.h" 18 19 namespace pw::async2 { 20 namespace internal { 21 22 template <typename Pendable> 23 class PendableAsTaskWithOutput : public Task { 24 public: 25 using OutputType = PendOutputOf<Pendable>; PendableAsTaskWithOutput(Pendable & pendable)26 PendableAsTaskWithOutput(Pendable& pendable) 27 : pendable_(pendable), output_(Pending()) {} TakeOutput()28 OutputType&& TakeOutput() { return std::move(*output_); } 29 30 private: DoPend(Context & cx)31 Poll<> DoPend(Context& cx) final { 32 output_ = pendable_.Pend(cx); 33 return output_.Readiness(); 34 } 35 Pendable& pendable_; 36 Poll<OutputType> output_; 37 }; 38 39 } // namespace internal 40 41 /// A single-threaded cooperatively-scheduled runtime for async tasks. 42 class Dispatcher { 43 public: 44 /// Constructs a new async Dispatcher. 45 Dispatcher() = default; 46 Dispatcher(Dispatcher&) = delete; 47 Dispatcher(Dispatcher&&) = delete; 48 Dispatcher& operator=(Dispatcher&) = delete; 49 Dispatcher& operator=(Dispatcher&&) = delete; ~Dispatcher()50 ~Dispatcher() { native_.Deregister(); } 51 52 /// Tells the ``Dispatcher`` to run ``Task`` to completion. 53 /// This method does not block. 54 /// 55 /// After ``Post`` is called, ``Task::Pend`` will be invoked once. 56 /// If ``Task::Pend`` does not complete, the ``Dispatcher`` will wait 57 /// until the ``Task`` is "awoken", at which point it will call ``Pend`` 58 /// again until the ``Task`` completes. 59 /// 60 /// This method is thread-safe and interrupt-safe. Post(Task & task)61 void Post(Task& task) PW_LOCKS_EXCLUDED(dispatcher_lock()) { 62 native_.Post(task); 63 } 64 65 /// Runs tasks until none are able to make immediate progress. RunUntilStalled()66 Poll<> RunUntilStalled() PW_LOCKS_EXCLUDED(dispatcher_lock()) { 67 return native_.DoRunUntilStalled(*this, nullptr); 68 } 69 70 /// Runs tasks until none are able to make immediate progress, or until 71 /// ``task`` completes. 72 /// 73 /// Returns whether ``task`` completed. RunUntilStalled(Task & task)74 Poll<> RunUntilStalled(Task& task) PW_LOCKS_EXCLUDED(dispatcher_lock()) { 75 return native_.DoRunUntilStalled(*this, &task); 76 } 77 78 /// Runs tasks until none are able to make immediate progress, or until 79 /// ``pendable`` completes. 80 /// 81 /// Returns a ``Poll`` containing the possible output of ``pendable``. 82 template <typename Pendable> RunPendableUntilStalled(Pendable & pendable)83 Poll<PendOutputOf<Pendable>> RunPendableUntilStalled(Pendable& pendable) 84 PW_LOCKS_EXCLUDED(dispatcher_lock()) { 85 internal::PendableAsTaskWithOutput task(pendable); 86 Post(task); 87 if (RunUntilStalled(task).IsReady()) { 88 return task.TakeOutput(); 89 } 90 // Ensure that the task is no longer registered, as it will be destroyed 91 // once we return. 92 // 93 // This operation will not block because we are on the dispatcher thread 94 // and the dispatcher is not currently running (we just ran it). 95 task.Deregister(); 96 return Pending(); 97 } 98 99 /// Runs until all tasks complete. RunToCompletion()100 void RunToCompletion() PW_LOCKS_EXCLUDED(dispatcher_lock()) { 101 native_.DoRunToCompletion(*this, nullptr); 102 } 103 104 /// Runs until ``task`` completes. RunToCompletion(Task & task)105 void RunToCompletion(Task& task) PW_LOCKS_EXCLUDED(dispatcher_lock()) { 106 native_.DoRunToCompletion(*this, &task); 107 } 108 109 /// Runs until ``pendable`` completes, returning the output of ``pendable``. 110 template <typename Pendable> RunPendableToCompletion(Pendable & pendable)111 PendOutputOf<Pendable> RunPendableToCompletion(Pendable& pendable) 112 PW_LOCKS_EXCLUDED(dispatcher_lock()) { 113 internal::PendableAsTaskWithOutput task(pendable); 114 Post(task); 115 native_.DoRunToCompletion(*this, &task); 116 return task.TakeOutput(); 117 } 118 119 /// Returns a reference to the native backend-specific dispatcher type. native()120 pw::async2::backend::NativeDispatcher& native() { return native_; } 121 122 private: 123 pw::async2::backend::NativeDispatcher native_; 124 }; 125 126 } // namespace pw::async2 127