xref: /aosp_15_r20/external/pigweed/pw_async_fuchsia/public/pw_async_fuchsia/fake_dispatcher.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2024 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 <lib/async-loop/loop.h>
17 #include <zircon/assert.h>
18 #include <zircon/listnode.h>
19 
20 #include "pw_async/task.h"
21 
22 namespace pw::async::test::backend {
23 
24 class NativeFakeDispatcher {
25  public:
26   explicit NativeFakeDispatcher(Dispatcher& test_dispatcher);
~NativeFakeDispatcher()27   ~NativeFakeDispatcher() { DestroyLoop(); }
28 
RequestStop()29   void RequestStop() { stop_requested_ = true; }
30 
31   // Synchronously destroys the loop, runs pending tasks with a cancelled
32   // status, and frees the loop memory. Returns true if any task was invoked.
33   bool DestroyLoop();
34 
35   chrono::SystemClock::time_point now();
36 
37   void Post(Task& task);
38   void PostAfter(Task& task, chrono::SystemClock::duration delay);
39   void PostAt(Task& task, chrono::SystemClock::time_point time);
40 
41   bool Cancel(Task& task);
42 
43   bool RunUntilIdle();
44   bool RunUntil(chrono::SystemClock::time_point end_time);
45   bool RunFor(chrono::SystemClock::duration duration);
46 
47  private:
48   // FakeAsyncLoop is an adapted version of the Zircon async-loop (implemented
49   // in zircon/system/ulib/async-loop/loop.c) for testing. It contains adapted
50   // copies of a subset of the async-loop methods.
51   //
52   // In the method copies, 1) code interfacing with Zircon timers has been
53   // replaced with a simulated timer system and 2) code related to thread
54   // safety/synchronization has been elided.
55   class FakeAsyncLoop {
56    public:
57     explicit FakeAsyncLoop();
~FakeAsyncLoop()58     ~FakeAsyncLoop() { Shutdown(); }
59 
60     // Returns true iff any task was invoked.
61     bool Shutdown();
62     chrono::SystemClock::time_point Now() const;
63     zx_status_t PostTask(async_task_t* task);
64     zx_status_t CancelTask(async_task_t* task);
65     // Returns true iff any task was invoked.
66     bool RunUntilIdle();
67     // Returns true iff any task was invoked.
68     bool Run(zx_time_t deadline, bool once);
Runnable()69     bool Runnable() const { return state_ == ASYNC_LOOP_RUNNABLE; }
70 
71    private:
TaskToNode(async_task_t * task)72     static inline list_node_t* TaskToNode(async_task_t* task) {
73       return reinterpret_cast<list_node_t*>(&task->state);
74     }
NodeToTask(list_node_t * node)75     static inline async_task_t* NodeToTask(list_node_t* node) {
76       return reinterpret_cast<async_task_t*>(reinterpret_cast<char*>(node) -
77                                              offsetof(async_task_t, state));
78     }
79 
80     void InsertTask(async_task_t* task);
81     void RestartTimer();
82     zx_time_t NextDeadline();
83     // Sets task_invoked to true iff a task was invoked.
84     zx_status_t RunOnce(zx_time_t deadline, bool* task_invoked);
85     // Returns true iff a task was invoked.
86     bool DispatchTasks();
87     // Returns true iff a task was invoked.
88     bool CancelAll();
89 
90     // Tracks the current time as viewed by the fake loop.
91     zx_time_t now_ = 0;
92     // Simulated timer. Stores ZX_TIME_INFINITE when no timer is set.
93     zx_time_t next_timer_expiration_ = ZX_TIME_INFINITE;
94 
95     async_loop_state_t state_ = ASYNC_LOOP_RUNNABLE;
96 
97     // True while the loop is busy dispatching tasks.
98     bool dispatching_tasks_ = false;
99     // Pending tasks, earliest deadline first.
100     list_node_t task_list_;
101     // Due tasks, earliest deadline first.
102     list_node_t due_list_;
103     // True if the simulated timer has been set and has not fired yet.
104     bool timer_armed_ = false;
105   };
106 
107   Dispatcher& dispatcher_;
108   FakeAsyncLoop fake_loop_;
109   bool stop_requested_ = false;
110 };
111 
112 }  // namespace pw::async::test::backend
113