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