1 /*
2  * Copyright 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef TEST_VENDOR_LIB_ASYNC_MANAGER_H_
18 #define TEST_VENDOR_LIB_ASYNC_MANAGER_H_
19 
20 #include <time.h>
21 
22 #include <chrono>
23 #include <cstdint>
24 #include <functional>
25 #include <map>
26 #include <memory>
27 #include <mutex>
28 #include <set>
29 #include <utility>
30 
31 #include "errno.h"
32 #include "stdio.h"
33 
34 namespace rootcanal {
35 
36 using TaskCallback = std::function<void(void)>;
37 using ReadCallback = std::function<void(int)>;
38 using CriticalCallback = std::function<void(void)>;
39 using AsyncTaskId = uint16_t;
40 using AsyncUserId = uint16_t;
41 constexpr uint16_t kInvalidTaskId = 0;
42 
43 // Manages tasks that should be done in the future. It can watch file
44 // descriptors to call a given callback when it is certain that a non-blocking
45 // read is possible or can call a callback at a specific time (approximately)
46 // and (optionally) repeat the call periodically.
47 // The class is thread safe in the sense that all its member functions can be
48 // called simultaneously from different concurrent threads. The exception to
49 // this rule is the class destructor, which is unsafe to call concurrently with
50 // calls to other class member functions. This exception also has its own
51 // exception: it is safe to destroy the object even if some of its callbacks may
52 // call its member functions, because the destructor will make sure all callback
53 // calling threads are stopped before actually destroying anything. Callbacks
54 // that wait for file descriptor always run on the same thread, so there is no
55 // need of additional synchronization between them. The same applies to task
56 // callbacks since they also run on a thread of their own, however it is
57 // possible for a read callback and a task callback to execute at the same time
58 // (they are guaranteed to run in different threads) so synchronization is
59 // needed to access common state (other than the internal state of the
60 // AsyncManager class). While not required, it is strongly recommended to use
61 // the Synchronize(const CriticalCallback&) member function to execute code
62 // inside critical sections. Callbacks passed to this method on the same
63 // AsyncManager object from different threads are granted to *NOT* run
64 // concurrently.
65 class AsyncManager {
66  public:
67   // Starts watching a file descriptor in a separate thread. The
68   // on_read_fd_ready_callback() will be asynchronously called when it is
69   // guaranteed that a call to read() on the FD will not block. No promise is
70   // made about when in the future the callback will be called, in particular,
71   // it is perfectly possible to have it called before this function returns. A
72   // return of 0 means success, an error code is returned otherwise.
73   int WatchFdForNonBlockingReads(int file_descriptor,
74                                  const ReadCallback& on_read_fd_ready_callback);
75 
76   // If the fd was not being watched before the call will be ignored.
77   void StopWatchingFileDescriptor(int file_descriptor);
78 
79   // Get an identifier for the scheduler so that tasks can be cancelled per user
80   AsyncUserId GetNextUserId();
81 
82   // Schedules an action to occur in the future. Even if the delay given is not
83   // positive the callback will be called asynchronously.
84   AsyncTaskId ExecAsync(AsyncUserId user_id, std::chrono::milliseconds delay,
85                         const TaskCallback& callback);
86 
87   // Schedules an action to occur periodically in the future. If the delay given
88   // is not positive the callback will be asynchronously called once for each
89   // time in the past that it should have been called and then scheduled for
90   // future times.
91   AsyncTaskId ExecAsyncPeriodically(AsyncUserId user_id,
92                                     std::chrono::milliseconds delay,
93                                     std::chrono::milliseconds period,
94                                     const TaskCallback& callback);
95 
96   // Cancels the/every future occurrence of the action specified by this id.
97   // The following invariants will hold:
98   // - The task will not be invoked after this method returns
99   // - If the task is currently running it will block until the task is
100   //   completed, unless cancel is called from the running task.
101   bool CancelAsyncTask(AsyncTaskId async_task_id);
102 
103   // Cancels the/every future occurrence of the action specified by this id.
104   // The following invariants will hold:
105   // - The task will not be invoked after this method returns
106   // - If the task is currently running it will block until the task is
107   //   completed, unless cancel is called from the running task.
108   bool CancelAsyncTasksFromUser(AsyncUserId user_id);
109 
110   // Execs the given code in a synchronized manner. It is guaranteed that code
111   // given on (possibly)concurrent calls to this member function on the same
112   // AsyncManager object will never be executed simultaneously. It is the
113   // class's user's responsibility to ensure that no calls to Synchronize are
114   // made from inside a CriticalCallback, since that would cause a lock to be
115   // acquired twice with unpredictable results. It is strongly recommended to
116   // have very simple CriticalCallbacks, preferably using lambda expressions.
117   void Synchronize(const CriticalCallback& critical_callback);
118 
119   AsyncManager();
120   AsyncManager(const AsyncManager&) = delete;
121   AsyncManager& operator=(const AsyncManager&) = delete;
122 
123   ~AsyncManager();
124 
125  private:
126   // Implementation of the FD watching part of AsyncManager, extracted to its
127   // own class for clarity purposes.
128   class AsyncFdWatcher;
129 
130   // Implementation of the asynchronous tasks part of AsyncManager, extracted to
131   // its own class for clarity purposes.
132   class AsyncTaskManager;
133 
134   // Kept as pointers because we may want to support resetting either without
135   // destroying the other one
136   std::unique_ptr<AsyncFdWatcher> fdWatcher_p_;
137   std::unique_ptr<AsyncTaskManager> taskManager_p_;
138 };
139 }  // namespace rootcanal
140 #endif  // TEST_VENDOR_LIB_ASYNC_MANAGER_H_
141