xref: /aosp_15_r20/external/pigweed/pw_async2/public/pw_async2/dispatcher.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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