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
40
47class Context {
48 public:
49 Context(const Context&) = delete;
50 Context(Context&&) = delete;
51
52 Context& operator=(const Context&) = delete;
53 Context& operator=(Context&&) = delete;
54
69 void ReEnqueue(); // Implemented inline in task.h after Task is defined.
70
76
77 private:
78 friend class Task;
79
80 constexpr Context() = default;
81};
82
84
86
89enum class RunTaskResult {
91 kActive,
92
95
98};
99
129class Task : public IntrusiveQueue<Task>::Item, private Context {
130 public:
139 explicit constexpr Task(log::Token name = kDefaultName) : name_(name) {}
140
141 Task(const Task&) = delete;
142 Task(Task&&) = delete;
143 Task& operator=(const Task&) = delete;
144 Task& operator=(Task&&) = delete;
145
155 virtual ~Task();
156
166 Poll<> Pend(Context& cx) { return DoPend(cx); }
167
176 bool IsRegistered() const;
177
192 void Deregister() PW_LOCKS_EXCLUDED(internal::lock());
193
198 void Join() PW_LOCKS_EXCLUDED(internal::lock());
199
200 protected:
204 using Context = ::pw::async2::Context;
205
206 private:
207 friend class Dispatcher;
208 friend class Waker;
209 friend Context;
210
211 static constexpr log::Token kDefaultName =
212 PW_LOG_TOKEN("pw_async2", "(anonymous)");
213
218 bool TryDeregister() PW_LOCKS_EXCLUDED(internal::lock());
219
237 virtual Poll<> DoPend(Context&) = 0;
238
239 // Sets this task to use the provided dispatcher.
240 void PostTo(Dispatcher& dispatcher)
241 PW_EXCLUSIVE_LOCKS_REQUIRED(internal::lock()) {
242 PW_DASSERT(state_ == State::kUnposted);
243 PW_DASSERT(dispatcher_ == nullptr);
244 state_ = State::kWoken;
245 dispatcher_ = &dispatcher;
246 }
247
248 // Removes the task from the dispatcher. Returns the ControlBlock* if the
249 // dispatcher has a shared reference to this task.
250 allocator::internal::ControlBlock* Unpost()
251 PW_EXCLUSIVE_LOCKS_REQUIRED(internal::lock());
252
253 // Unposts and releases the dispatcher's shared reference to the task, if any.
254 // Unconditionally releases the lock since it cannot be held while a task
255 // destructor is called.
256 void UnpostAndReleaseRef() PW_UNLOCK_FUNCTION(internal::lock());
257
258 // Unposts and releases the dispatcher's shared reference to the task, if any.
259 // If the dispatcher has a shared reference to the task, the lock is
260 // released to destroy the task, then reacquired. This should ONLY be called
261 // from the Dispatcher's destructor, since no tasks should be posted or run
262 // while the Dispatcher is being destroyed.
263 void UnpostAndReleaseRefFromDispatcherDestructor()
264 PW_EXCLUSIVE_LOCKS_REQUIRED(internal::lock());
265
266 void MarkRunning() PW_EXCLUSIVE_LOCKS_REQUIRED(internal::lock()) {
267 state_ = State::kRunning;
268 }
269
270 // Called by the dispatcher to run this task. The task has already been marked
271 // as running.
272 RunTaskResult RunInDispatcher() PW_LOCKS_EXCLUDED(internal::lock());
273
274 // Called by a waker to wake this task.
275 void Wake() PW_UNLOCK_FUNCTION(internal::lock());
276
277 // Unlinks all `Waker` objects associated with this `Task.`
278 void RemoveAllWakersLocked() PW_EXCLUSIVE_LOCKS_REQUIRED(internal::lock());
279
280 // Adds a `Waker` to the linked list of `Waker`s tracked by this `Task`.
281 void AddWakerLocked(Waker& waker)
282 PW_EXCLUSIVE_LOCKS_REQUIRED(internal::lock()) {
283 wakers_.push_front(waker);
284 }
285
286 // Removes a `Waker` from the linked list of `Waker`s tracked by this `Task`.
287 //
288 // Precondition: the provided waker *must* be in the list of `Waker`s tracked
289 // by this `Task`.
290 void RemoveWakerLocked(Waker& waker)
291 PW_EXCLUSIVE_LOCKS_REQUIRED(internal::lock()) {
292 wakers_.remove(waker);
293 }
294
295 void ReleaseSharedRef(allocator::internal::ControlBlock* control_block)
296 PW_LOCKS_EXCLUDED(internal::lock()) {
297 // Create a SharedPtr to decrement the ref count and possibly delete this.
298 SharedPtr<Task> temp(this, control_block);
299 }
300
301 // Sets the control block for an allocated BEFORE the task is posted. Since
302 // the task hasn't been posted yet, it's not necessary to hold the lock.
303 void SetControlBlockBeforePosted(
304 allocator::internal::ControlBlock& control_block)
306 control_block_ = &control_block;
307 }
308
309 enum class State : unsigned char {
310 kUnposted,
311 kSleeping,
312 kRunning,
313 kDeregisteredButRunning,
314 kWokenWhileRunning,
315 kWoken,
316 };
317
318 // The current state of the task.
319 State state_ PW_GUARDED_BY(internal::lock()) = State::kUnposted;
320
321 // Indicates whether this task needs a waker if it is Pending.
322 enum : bool {
323 kWakerNeeded,
324 kNoWakerNeeded
325 } waker_requirement_ = kWakerNeeded;
326
327 // A pointer to the dispatcher this task is associated with.
328 //
329 // This will be non-null when `state_` is anything other than `kUnposted`.
330 //
331 // This value must be cleared by the dispatcher upon destruction in order to
332 // prevent null access.
333 Dispatcher* dispatcher_ PW_GUARDED_BY(internal::lock()) = nullptr;
334
335 // The memory block that contains this task, if it was dynamically allocated.
336 allocator::internal::ControlBlock* control_block_
337 PW_GUARDED_BY(internal::lock()) = nullptr;
338
339 // Linked list of `Waker` s that may awaken this `Task`.
340 IntrusiveForwardList<Waker> wakers_ PW_GUARDED_BY(internal::lock());
341
342 // Optional user-facing name for the task. If set, it will be included in
343 // debug logs.
344 log::Token name_;
345};
346
348
350
352 static_cast<Task&>(*this).waker_requirement_ = Task::kNoWakerNeeded;
353 return Pending();
354}
355
356inline void Context::ReEnqueue() {
357 Waker(static_cast<Task&>(*this), {}).Wake();
358}
359
361
362} // namespace pw::async2
Definition: intrusive_queue.h:30
Definition: task.h:47
Definition: dispatcher.h:74
Definition: poll.h:138
Definition: task.h:129
virtual Poll DoPend(Context &)=0
Poll Pend(Context &cx)
Definition: task.h:166
bool IsRegistered() const
constexpr Task(log::Token name=kDefaultName)
Definition: task.h:139
Definition: waker.h:155
PendingType Unschedule()
Definition: task.h:351
void ReEnqueue()
Definition: task.h:356
constexpr PendingType Pending()
Returns a value indicating that an operation was not yet able to complete.
Definition: poll.h:353
RunTaskResult
Definition: task.h:89
@ 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.
#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