1 /*
2  * Copyright (C) 2024 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 CHRE_UTIL_TRANSACTION_MANAGER_H_
18 #define CHRE_UTIL_TRANSACTION_MANAGER_H_
19 
20 #include <cstdint>
21 #include <type_traits>
22 
23 #include "chre/platform/mutex.h"
24 #include "chre/util/array_queue.h"
25 #include "chre/util/non_copyable.h"
26 #include "chre/util/optional.h"
27 #include "chre/util/time.h"
28 
29 namespace chre {
30 
31 /**
32  * TransactionManager tracks pending transactions.
33  *
34  * Transactions are long running operations identified by an ID.
35  * The TransactionManager makes sure that the transactions will complete only
36  * once after a call to completeTransaction or after the optional timeout
37  * expires whichever comes first. A transaction will be retried by calling
38  * the start callback after no complete call has been made before the retry
39  * wait time.
40  *
41  * Typical usage:
42  * 1. Start a transaction. Get the ID back.
43  * 2. TransactionManager will run start callback with the data.
44  * 3. If the start callback fails or if the transaction is not completed,
45  *    TransactionManager will call the start callback again after the retry
46  *    wait time.
47  * 4. Call completeTransaction with the ID.
48  * 5. TransactionManager will call the complete callback with the data.
49  *
50  * If completeTransaction is not called before the timeout, the transaction
51  * will be completed with a CHRE_ERROR_TIMEOUT.
52  *
53  * Ensure the thread processing the deferred callbacks is completed before the
54  * destruction of the TransactionManager.
55  *
56  * @param TransactionData The data passed to the start and complete callbacks.
57  * @param kMaxTransactions The maximum number of pending transactions.
58  */
59 template <typename TransactionData, size_t kMaxTransactions>
60 class TransactionManager : public NonCopyable {
61  public:
62   /**
63    * Type of the callback called on transaction completion. This callback is
64    * called in the defer callback thread.
65    *
66    * This callback cannot call any of the TransactionManager methods.
67    *
68    * @param data The data for the transaction.
69    * @param errorCode The error code passed to completeTransaction.
70    * @return whether the callback succeeded.
71    */
72   using CompleteCallback = typename std::conditional<
73       std::is_pointer<TransactionData>::value ||
74           std::is_fundamental<TransactionData>::value,
75       bool (*)(TransactionData data, uint8_t errorCode),
76       bool (*)(const TransactionData &data, uint8_t errorCode)>::type;
77 
78   /**
79    * Type of the callback called to start the transaction. This is the action
80    * that will be repeated on a retry of the transaction. This callback is
81    * called in the defer callback thread.
82    *
83    * This callback cannot call any of the TransactionManager methods.
84    *
85    * @param data The data for the transaction.
86    * @return whether the callback succeeded.
87    */
88   using StartCallback =
89       typename std::conditional<std::is_pointer<TransactionData>::value ||
90                                     std::is_fundamental<TransactionData>::value,
91                                 bool (*)(TransactionData data),
92                                 bool (*)(TransactionData &data)>::type;
93 
94   /**
95    * The type of function used to defer a callback. See DeferCallback.
96    *
97    * @param type The type passed from the DeferCallback.
98    * @param data The data passed from the DeferCallback.
99    * @param extraData The extra data passed from the DeferCallback.
100    */
101   using DeferCallbackFunction = void (*)(uint16_t type, void *data,
102                                          void *extraData);
103 
104   /**
105    * Type of the callback used to defer the call of func with data and extraData
106    * after waiting for delay. extraData is ignored if delay > 0 ns.
107    *
108    * This callback cannot call any of the TransactionManager methods.
109    *
110    * @param func The function to call when the callback is executed.
111    * @param data The data to pass to the function.
112    * @param extraData The extra data to pass to the function.
113    * @param delay The nanoseconds delay to wait before calling the function.
114    * @param outTimerHandle The output timer handle if delay > 0 ns.
115    * @return whether the callback succeeded.
116    */
117   using DeferCallback = bool (*)(DeferCallbackFunction func, void *data,
118                                  void *extraData, Nanoseconds delay,
119                                  uint32_t *outTimerHandle);
120 
121   /**
122    * Type of the callback used to cancel a defer call made using the
123    * DeferCallback.
124    *
125    * This callback cannot call any of the TransactionManager methods.
126    *
127    * @param timerHandle the timer handle returned using the DeferCallback.
128    * @return whether the callback was successfully cancelled.
129    */
130   using DeferCancelCallback = bool (*)(uint32_t timerHandle);
131 
132   /**
133    * The callback used to determine which elements to remove
134    * during a flush.
135    *
136    * This callback cannot call any of the TransactionManager methods.
137    */
138   using FlushCallback = typename std::conditional<
139       std::is_pointer<TransactionData>::value ||
140           std::is_fundamental<TransactionData>::value,
141       bool (*)(TransactionData data, void *callbackData),
142       bool (*)(const TransactionData &data, void *callbackData)>::type;
143 
144   /**
145    * The function called when the transaction processing timer is fired.
146    * @see DeferCallbackFunction() for parameter information.
147    */
onTimerFired(uint16_t,void * data,void *)148   static void onTimerFired(uint16_t /* type */, void *data,
149                            void * /* extraData */) {
150     auto transactionManagerPtr = static_cast<TransactionManager *>(data);
151     if (transactionManagerPtr == nullptr) {
152       LOGE("Could not get transaction manager to process transactions");
153       return;
154     }
155 
156     transactionManagerPtr->mTimerHandle = CHRE_TIMER_INVALID;
157     transactionManagerPtr->processTransactions();
158   }
159 
160   TransactionManager() = delete;
161 
162   TransactionManager(StartCallback startCallback,
163                      CompleteCallback completeCallback,
164                      DeferCallback deferCallback,
165                      DeferCancelCallback deferCancelCallback,
166                      Nanoseconds retryWaitTime, Nanoseconds timeout,
167                      uint16_t maxNumRetries = 3)
kStartCallback(startCallback)168       : kStartCallback(startCallback),
169         kCompleteCallback(completeCallback),
170         kDeferCallback(deferCallback),
171         kDeferCancelCallback(deferCancelCallback),
172         kRetryWaitTime(retryWaitTime),
173         kTimeout(timeout),
174         kMaxNumRetries(maxNumRetries) {
175     CHRE_ASSERT(startCallback != nullptr);
176     CHRE_ASSERT(completeCallback != nullptr);
177     CHRE_ASSERT(deferCallback != nullptr);
178     CHRE_ASSERT(deferCancelCallback != nullptr);
179     CHRE_ASSERT(retryWaitTime.toRawNanoseconds() > 0);
180     CHRE_ASSERT(timeout.toRawNanoseconds() == 0 ||
181                 timeout.toRawNanoseconds() > retryWaitTime.toRawNanoseconds());
182   }
183 
184   /**
185    * Completes a transaction.
186    *
187    * The callback registered when starting the transaction is called with the
188    * errorCode if the error is not CHRE_ERROR_TRANSIENT. If the error is
189    * CHRE_ERROR_TRANSIENT, this function marks the transaction as ready
190    * to retry and processes transactions.
191    *
192    * This function is safe to call in any thread.
193    *
194    * Note that the callback will be called at most once on the first call to
195    * this method. For example if the transaction timed out before an explicit
196    * call to completeTransaction, the callback is only invoked for the timeout.
197    *
198    * @param transactionId ID of the transaction to complete.
199    * @param errorCode Error code to pass to the callback.
200    * @return Whether the transaction was completed successfully.
201    */
202   bool completeTransaction(uint32_t transactionId, uint8_t errorCode);
203 
204   /**
205    * Flushes all the pending transactions that match the FlushCallback.
206    *
207    * This function is safe to call in any thread.
208    *
209    * The completion callback is not called.
210    *
211    * @param flushCallback The function that determines which transactions will
212    * be flushed (upon return true).
213    * @param data The data to be passed to the flush callback.
214    * @return The number of flushed transactions.
215    */
216   size_t flushTransactions(FlushCallback flushCallback, void *data);
217 
218   /**
219    * Starts a transaction. This function will mark the transaction as ready to
220    * execute the StartCallback and processes transactions. The StartCallback
221    * will be called only when there are no other pending transactions for the
222    * unique cookie.
223    *
224    * The transaction will complete with a CHRE_ERROR_TIMEOUT if
225    * completeTransaction has not been called before the timeout. The timeout
226    * is calculated from the time the StartCallback is called.
227    *
228    * This function is safe to call in any thread.
229    *
230    * @param data The transaction data and callbacks used to run the transaction.
231    * @param cookie The cookie used to ensure only one transaction will be
232    *        started and pending for a given cookie.
233    * @param id A pointer to the transaction ID that will be populated when
234    *        startTransaction succeed. It must not be null.
235    * @return Whether the transaction was started successfully.
236    */
237   bool startTransaction(const TransactionData &data, uint16_t cookie,
238                         uint32_t *id);
239 
240  private:
241   //! Stores transaction-related data.
242   struct Transaction {
243     uint32_t id;
244     TransactionData data;
245     Nanoseconds nextRetryTime;
246     Nanoseconds timeoutTime;
247     uint16_t cookie;
248     uint16_t numCompletedStartCalls;
249     Optional<uint8_t> errorCode;
250   };
251 
252   /**
253    * Defers processing transactions in the defer callback thread.
254    */
255   void deferProcessTransactions();
256 
257   /**
258    * Calls the complete callback for a transaction if needed. Also updates the
259    * transaction state. Assumes the caller holds the mutex.
260    *
261    * @param transaction The transaction.
262    */
263   void doCompleteTransactionLocked(Transaction &transaction);
264 
265   /**
266    * Calls the start callback for a transaction if needed. Also updates the
267    * transaction state. Assumes the caller holds the mutex.
268    *
269    * @param transaction The transaction.
270    * @param i The index of the transaction in mTransactions.
271    * @param now The current time.
272    */
273   void doStartTransactionLocked(Transaction &transaction, size_t i,
274                                 Nanoseconds now);
275 
276   /**
277    * Generates a pseudo random ID for a transaction in the range of
278    * [0, 2^30 - 1].
279    * @return The generated ID.
280    */
281   uint32_t generatePseudoRandomId();
282 
283   /**
284    * Processes transactions. This function will call the start callback and
285    * complete callback where appropriate and keep track of which transactions
286    * need to be retried next. This function is called in the defer callback
287    * thread and will defer a call to itself at the next time needed to processes
288    * the next transaction.
289    */
290   void processTransactions();
291 
292   //! The start callback.
293   const StartCallback kStartCallback;
294 
295   //! The complete callback.
296   const CompleteCallback kCompleteCallback;
297 
298   //! The defer callback.
299   const DeferCallback kDeferCallback;
300 
301   //! The defer cancel callback.
302   const DeferCancelCallback kDeferCancelCallback;
303 
304   //! The retry wait time.
305   const Nanoseconds kRetryWaitTime;
306 
307   //! The timeout for a transaction.
308   const Nanoseconds kTimeout;
309 
310   //! The maximum number of retries for a transaction.
311   const uint16_t kMaxNumRetries;
312 
313   //! The mutex protecting mTransactions and mTimerHandle.
314   Mutex mMutex;
315 
316   //! The next ID for use when creating a transaction.
317   Optional<uint32_t> mNextTransactionId;
318 
319   //! The timer handle for the timer tracking execution of processTransactions.
320   //! Can only be modified in the defer callback thread.
321   uint32_t mTimerHandle = CHRE_TIMER_INVALID;
322 
323   //! The list of transactions.
324   ArrayQueue<Transaction, kMaxTransactions> mTransactions;
325 };
326 
327 }  // namespace chre
328 
329 #include "chre/util/transaction_manager_impl.h"
330 
331 #endif  // CHRE_UTIL_TRANSACTION_MANAGER_H_
332