xref: /trafficserver/include/tscpp/api/Async.h (revision d77cd731)
1 /**
2   Licensed to the Apache Software Foundation (ASF) under one
3   or more contributor license agreements.  See the NOTICE file
4   distributed with this work for additional information
5   regarding copyright ownership.  The ASF licenses this file
6   to you under the Apache License, Version 2.0 (the
7   "License"); you may not use this file except in compliance
8   with the License.  You may obtain a copy of the License at
9 
10       http://www.apache.org/licenses/LICENSE-2.0
11 
12   Unless required by applicable law or agreed to in writing, software
13   distributed under the License is distributed on an "AS IS" BASIS,
14   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   See the License for the specific language governing permissions and
16   limitations under the License.
17  */
18 
19 /**
20  * @file Async.h
21  * @brief Provides constructs to perform async operations.
22  */
23 
24 #pragma once
25 
26 #include <memory>
27 #include <mutex>
28 #include <unordered_map>
29 #include <utility>
30 
31 #include "tscpp/api/noncopyable.h"
32 
33 namespace atscppapi
34 {
35 #if !defined(ATSCPPAPI_MUTEX_DEFINED_)
36 #define ATSCPPAPI_MUTEX_DEFINED_
37 
38 using Mutex = std::recursive_mutex;
39 
40 #endif
41 
42 /**
43  * @private
44  *
45  * @brief This class represents the interface of a dispatch controller. A dispatch controller
46  * is used to dispatch an event to a receiver. This interface exists so that the types in this
47  * header file can be defined.
48  */
49 class AsyncDispatchControllerBase : noncopyable
50 {
51 public:
52   /**
53    * Dispatches an async event to a receiver.
54    *
55    * @return True if the receiver was still alive.
56    */
57   virtual bool dispatch() = 0;
58 
59   /** Renders dispatch unusable to communicate to receiver */
60   virtual void disable() = 0;
61 
62   /** Returns true if receiver can be communicated with */
63   virtual bool isEnabled() = 0;
64 
~AsyncDispatchControllerBase()65   virtual ~AsyncDispatchControllerBase() {}
66 };
67 
68 /**
69  * @brief AsyncProvider is the interface that providers of async operations must implement.
70  * The system allows decoupling of the lifetime/scope of provider and receiver objects. The
71  * receiver object might have expired before the async operation is complete and the system
72  * handles this case. Because of this decoupling, it is the responsibility of the provider
73  * to manage it's expiration - self-destruct on completion is a good option.
74  */
75 class AsyncProvider
76 {
77 public:
78   /**
79    * This method is invoked when the async operation is requested. This call should be used
80    * to just start the async operation and *not* block this thread. On completion,
81    * getDispatchController() can be used to invoke the receiver.
82    */
83   virtual void run() = 0;
84 
85   /** Base implementation just breaks communication channel with receiver. Implementations
86    * should add business logic here. */
87   virtual void
cancel()88   cancel()
89   {
90     if (dispatch_controller_) {
91       dispatch_controller_->disable();
92     }
93   }
94 
~AsyncProvider()95   virtual ~AsyncProvider() { this->cancel(); }
96 
97 protected:
98   std::shared_ptr<AsyncDispatchControllerBase>
getDispatchController()99   getDispatchController()
100   {
101     return dispatch_controller_;
102   }
103 
104 private:
105   std::shared_ptr<AsyncDispatchControllerBase> dispatch_controller_;
106   void
doRun(std::shared_ptr<AsyncDispatchControllerBase> dispatch_controller)107   doRun(std::shared_ptr<AsyncDispatchControllerBase> dispatch_controller)
108   {
109     dispatch_controller_ = std::move(dispatch_controller);
110     run();
111   }
112   friend class Async;
113 };
114 
115 /**
116  * @private
117  *
118  * @brief Dispatch controller implementation. When invoking the receiver, it verifies that the
119  * receiver is still alive, locks the mutex and then invokes handleAsyncComplete().
120  */
121 template <typename AsyncEventReceiverType, typename AsyncProviderType>
122 class AsyncDispatchController : public AsyncDispatchControllerBase
123 {
124 public:
125   bool
dispatch()126   dispatch() override
127   {
128     bool ret = false;
129     std::lock_guard<Mutex> scopedLock(*dispatch_mutex_);
130     if (event_receiver_) {
131       event_receiver_->handleAsyncComplete(static_cast<AsyncProviderType &>(*provider_));
132       ret = true;
133     }
134     return ret;
135   }
136 
137   void
disable()138   disable() override
139   {
140     std::lock_guard<Mutex> scopedLock(*dispatch_mutex_);
141     if (event_receiver_ != nullptr) {
142       event_receiver_->revokePromise(this);
143       event_receiver_ = nullptr;
144     }
145   }
146 
147   bool
isEnabled()148   isEnabled() override
149   {
150     return (event_receiver_ != nullptr);
151   }
152 
153   /**
154    * Constructor
155    *
156    * @param event_receiver The async complete event will be dispatched to this receiver.
157    * @param provider Async operation provider that is passed to the receiver on dispatch.
158    * @param mutex Mutex of the receiver that is locked during the dispatch
159    */
AsyncDispatchController(AsyncEventReceiverType * event_receiver,AsyncProviderType * provider,std::shared_ptr<Mutex> mutex)160   AsyncDispatchController(AsyncEventReceiverType *event_receiver, AsyncProviderType *provider, std::shared_ptr<Mutex> mutex)
161     : event_receiver_(event_receiver), dispatch_mutex_(std::move(mutex)), provider_(provider)
162   {
163   }
164 
~AsyncDispatchController()165   ~AsyncDispatchController() override {}
166 
167 public:
168   AsyncEventReceiverType *event_receiver_;
169   std::shared_ptr<Mutex> dispatch_mutex_;
170 
171 private:
172   AsyncProviderType *provider_;
173 };
174 
175 /**
176  * @private
177  *
178  * @brief A promise is used to let the dispatch controller know if the receiver is still
179  * alive to receive the async complete dispatch. When the receiver dies, this promise is
180  * broken and it automatically updates the dispatch controller.
181  */
182 template <typename AsyncEventReceiverType, typename AsyncProviderType> class AsyncReceiverPromise : noncopyable
183 {
184 public:
AsyncReceiverPromise(std::shared_ptr<AsyncDispatchController<AsyncEventReceiverType,AsyncProviderType>> dispatch_controller)185   AsyncReceiverPromise(std::shared_ptr<AsyncDispatchController<AsyncEventReceiverType, AsyncProviderType>> dispatch_controller)
186     : dispatch_controller_(dispatch_controller)
187   {
188   }
189 
~AsyncReceiverPromise()190   ~AsyncReceiverPromise()
191   {
192     std::lock_guard<Mutex> scopedLock(*(dispatch_controller_->dispatch_mutex_));
193     dispatch_controller_->event_receiver_ = nullptr;
194   }
195 
196 protected:
197   std::shared_ptr<AsyncDispatchController<AsyncEventReceiverType, AsyncProviderType>> dispatch_controller_;
198 };
199 
200 /**
201  * @brief AsyncReceiver is the interface that receivers of async operations must implement. It is
202  * templated on the type of the async operation provider.
203  */
204 template <typename AsyncProviderType> class AsyncReceiver : noncopyable
205 {
206 public:
207   /**
208    * This method is invoked when the async operation is completed. The
209    * mutex provided during the creation of the async operation will be
210    * automatically locked during the invocation of this method.
211    *
212    * @param provider A reference to the provider which completed the async operation.
213    */
214   virtual void handleAsyncComplete(AsyncProviderType &provider) = 0;
~AsyncReceiver()215   virtual ~AsyncReceiver() {}
216   void
revokePromise(AsyncDispatchController<AsyncReceiver<AsyncProviderType>,AsyncProviderType> * dispatch_controller_ptr)217   revokePromise(AsyncDispatchController<AsyncReceiver<AsyncProviderType>, AsyncProviderType> *dispatch_controller_ptr)
218   {
219     receiver_promises_.erase(dispatch_controller_ptr);
220   }
221 
222 protected:
AsyncReceiver()223   AsyncReceiver() {}
224   friend class Async;
225 
226 private:
227   mutable std::unordered_map<AsyncDispatchController<AsyncReceiver<AsyncProviderType>, AsyncProviderType> *,
228                              std::shared_ptr<AsyncReceiverPromise<AsyncReceiver<AsyncProviderType>, AsyncProviderType>>>
229     receiver_promises_;
230 };
231 
232 /**
233  * @brief This class provides a method to create an async operation.
234  */
235 class Async : noncopyable
236 {
237 public:
238   /**
239    * This method sets up the dispatch controller to link the async operation provider and
240    * receiver and then initiates the operation by invoking the provider.
241    *
242    * @param event_receiver The receiver of the async complete dispatch.
243    * @param provider The provider of the async operation.
244    * @param mutex The mutex that is locked during the dispatch of the async event complete.
245    *              One will be created if nothing is passed in. Transaction plugins should use
246    *              TransactionPlugin::getMutex() here and global plugins can pass an appropriate
247    *              or nullptr mutex.
248    */
249   template <typename AsyncProviderType>
250   static void
execute(AsyncReceiver<AsyncProviderType> * event_receiver,AsyncProviderType * provider,std::shared_ptr<Mutex> mutex)251   execute(AsyncReceiver<AsyncProviderType> *event_receiver, AsyncProviderType *provider, std::shared_ptr<Mutex> mutex)
252   {
253     if (!mutex.get()) {
254       mutex.reset(new Mutex);
255     }
256     std::shared_ptr<AsyncDispatchController<AsyncReceiver<AsyncProviderType>, AsyncProviderType>> dispatcher(
257       new AsyncDispatchController<AsyncReceiver<AsyncProviderType>, AsyncProviderType>(event_receiver, provider, mutex));
258     std::shared_ptr<AsyncReceiverPromise<AsyncReceiver<AsyncProviderType>, AsyncProviderType>> receiver_promise(
259       new AsyncReceiverPromise<AsyncReceiver<AsyncProviderType>, AsyncProviderType>(dispatcher));
260     event_receiver->receiver_promises_[dispatcher.get()] = receiver_promise;
261     provider->doRun(dispatcher);
262   }
263 };
264 
265 } // end namespace atscppapi
266