C/C++ API Reference
Loading...
Searching...
No Matches
task.h
1// Copyright 2025 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_allocator/internal/control_block.h"
17#include "pw_allocator/shared_ptr.h"
18#include "pw_assert/assert.h"
19#include "pw_async2/internal/lock.h"
20#include "pw_async2/poll.h"
21#include "pw_async2/waker.h"
22#include "pw_containers/intrusive_forward_list.h"
23#include "pw_containers/intrusive_list.h"
24#include "pw_containers/intrusive_queue.h"
25#include "pw_log/tokenized_args.h"
26#include "pw_sync/lock_annotations.h"
27
28namespace pw::async2 {
29
31
33#define PW_ASYNC_TASK_NAME(name) PW_LOG_TOKEN_EXPR("pw_async2", name)
34
35class Dispatcher;
36
38
45class Context {
46 public:
47 Context(const Context&) = delete;
48 Context(Context&&) = delete;
49
50 Context& operator=(const Context&) = delete;
51 Context& operator=(Context&&) = delete;
52
67 void ReEnqueue(); // Implemented inline in task.h after Task is defined.
68
74
75 private:
76 friend class Task;
77
78 constexpr Context() = default;
79};
80
83enum class RunTaskResult {
85 kActive,
86
89
92};
93
123class Task : public IntrusiveQueue<Task>::Item, private Context {
124 public:
133 explicit constexpr Task(log::Token name = kDefaultName) : name_(name) {}
134
135 Task(const Task&) = delete;
136 Task(Task&&) = delete;
137 Task& operator=(const Task&) = delete;
138 Task& operator=(Task&&) = delete;
139
149 virtual ~Task();
150
160 Poll<> Pend(Context& cx) { return DoPend(cx); }
161
170 bool IsRegistered() const;
171
186 void Deregister() PW_LOCKS_EXCLUDED(internal::lock());
187
192 void Join() PW_LOCKS_EXCLUDED(internal::lock());
193
194 protected:
198 using Context = ::pw::async2::Context;
199
200 private:
201 friend class Dispatcher;
202 friend class Waker;
203 friend Context;
204
205 static constexpr log::Token kDefaultName =
206 PW_LOG_TOKEN("pw_async2", "(anonymous)");
207
212 bool TryDeregister() PW_LOCKS_EXCLUDED(internal::lock());
213
231 virtual Poll<> DoPend(Context&) = 0;
232
233 // Sets this task to use the provided dispatcher.
234 void PostTo(Dispatcher& dispatcher)
235 PW_EXCLUSIVE_LOCKS_REQUIRED(internal::lock()) {
236 PW_DASSERT(state_ == State::kUnposted);
237 PW_DASSERT(dispatcher_ == nullptr);
238 state_ = State::kWoken;
239 dispatcher_ = &dispatcher;
240 }
241
242 // Removes the task from the dispatcher. Returns the ControlBlock* if the
243 // dispatcher has a shared reference to this task.
244 allocator::internal::ControlBlock* Unpost()
245 PW_EXCLUSIVE_LOCKS_REQUIRED(internal::lock());
246
247 // Unposts and releases the dispatcher's shared reference to the task, if any.
248 // Unconditionally releases the lock since it cannot be held while a task
249 // destructor is called.
250 void UnpostAndReleaseRef() PW_UNLOCK_FUNCTION(internal::lock());
251
252 // Unposts and releases the dispatcher's shared reference to the task, if any.
253 // If the dispatcher has a shared reference to the task, the lock is
254 // released to destroy the task, then reacquired. This should ONLY be called
255 // from the Dispatcher's destructor, since no tasks should be posted or run
256 // while the Dispatcher is being destroyed.
257 void UnpostAndReleaseRefFromDispatcherDestructor()
258 PW_EXCLUSIVE_LOCKS_REQUIRED(internal::lock());
259
260 void MarkRunning() PW_EXCLUSIVE_LOCKS_REQUIRED(internal::lock()) {
261 state_ = State::kRunning;
262 }
263
264 // Called by the dispatcher to run this task. The task has already been marked
265 // as running.
266 RunTaskResult RunInDispatcher() PW_LOCKS_EXCLUDED(internal::lock());
267
268 // Called by a waker to wake this task.
269 void Wake() PW_UNLOCK_FUNCTION(internal::lock());
270
271 // Unlinks all `Waker` objects associated with this `Task.`
272 void RemoveAllWakersLocked() PW_EXCLUSIVE_LOCKS_REQUIRED(internal::lock());
273
274 // Adds a `Waker` to the linked list of `Waker`s tracked by this `Task`.
275 void AddWakerLocked(Waker& waker)
276 PW_EXCLUSIVE_LOCKS_REQUIRED(internal::lock()) {
277 wakers_.push_front(waker);
278 }
279
280 // Removes a `Waker` from the linked list of `Waker`s tracked by this `Task`.
281 //
282 // Precondition: the provided waker *must* be in the list of `Waker`s tracked
283 // by this `Task`.
284 void RemoveWakerLocked(Waker& waker)
285 PW_EXCLUSIVE_LOCKS_REQUIRED(internal::lock()) {
286 wakers_.remove(waker);
287 }
288
289 void ReleaseSharedRef(allocator::internal::ControlBlock* control_block)
290 PW_LOCKS_EXCLUDED(internal::lock()) {
291 // Create a SharedPtr to decrement the ref count and possibly delete this.
292 SharedPtr<Task> temp(this, control_block);
293 }
294
295 // Sets the control block for an allocated BEFORE the task is posted. Since
296 // the task hasn't been posted yet, it's not necessary to hold the lock.
297 void SetControlBlockBeforePosted(
298 allocator::internal::ControlBlock& control_block)
300 control_block_ = &control_block;
301 }
302
303 enum class State : unsigned char {
304 kUnposted,
305 kSleeping,
306 kRunning,
307 kDeregisteredButRunning,
308 kWokenWhileRunning,
309 kWoken,
310 };
311
312 // The current state of the task.
313 State state_ PW_GUARDED_BY(internal::lock()) = State::kUnposted;
314
315 // Indicates whether this task needs a waker if it is Pending.
316 enum : bool {
317 kWakerNeeded,
318 kNoWakerNeeded
319 } waker_requirement_ = kWakerNeeded;
320
321 // A pointer to the dispatcher this task is associated with.
322 //
323 // This will be non-null when `state_` is anything other than `kUnposted`.
324 //
325 // This value must be cleared by the dispatcher upon destruction in order to
326 // prevent null access.
327 Dispatcher* dispatcher_ PW_GUARDED_BY(internal::lock()) = nullptr;
328
329 // The memory block that contains this task, if it was dynamically allocated.
330 allocator::internal::ControlBlock* control_block_
331 PW_GUARDED_BY(internal::lock()) = nullptr;
332
333 // Linked list of `Waker` s that may awaken this `Task`.
334 IntrusiveForwardList<Waker> wakers_ PW_GUARDED_BY(internal::lock());
335
336 // Optional user-facing name for the task. If set, it will be included in
337 // debug logs.
338 log::Token name_;
339};
340
342 static_cast<Task&>(*this).waker_requirement_ = Task::kNoWakerNeeded;
343 return Pending();
344}
345
346inline void Context::ReEnqueue() {
347 Waker(static_cast<Task&>(*this), {}).Wake();
348}
349
351
352} // namespace pw::async2
Definition: intrusive_queue.h:30
Definition: task.h:45
Definition: dispatcher.h:74
Definition: poll.h:138
Definition: task.h:123
Definition: waker.h:155
RunTaskResult
Definition: task.h:83
@ kDeregistered
The task was removed from the dispatcher by another thread.
@ kCompleted
The task finished running.
@ kActive
The task is still posted to the dispatcher.
constexpr PendingType Pending()
Returns a value indicating that an operation was not yet able to complete.
Definition: poll.h:353
PendingType Unschedule()
Definition: task.h:341
void ReEnqueue()
Definition: task.h:346
virtual Poll DoPend(Context &)=0
Poll Pend(Context &cx)
Definition: task.h:160
bool IsRegistered() const
constexpr Task(log::Token name=kDefaultName)
Definition: task.h:133
#define PW_LOG_TOKEN(...)
Definition: tokenized_args.h:63
#define PW_GUARDED_BY(x)
Definition: lock_annotations.h:60
#define PW_NO_LOCK_SAFETY_ANALYSIS
Definition: lock_annotations.h:296
#define PW_EXCLUSIVE_LOCKS_REQUIRED(...)
Definition: lock_annotations.h:147
#define PW_UNLOCK_FUNCTION(...)
Definition: lock_annotations.h:249
#define PW_LOCKS_EXCLUDED(...)
Definition: lock_annotations.h:178
The Pigweed namespace.
Definition: alignment.h:27
Definition: poll.h:52