atscppapi  1.0.9
C++ wrapper for Apache Traffic Server API
 All Classes Files Functions Enumerations Enumerator Macros
Async.h
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2013 LinkedIn Corp. All rights reserved.
3  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
4  * except in compliance with the License. You may obtain a copy of the license at
5  * http://www.apache.org/licenses/LICENSE-2.0
6  *
7  * Unless required by applicable law or agreed to in writing, software distributed under the
8  * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
9  * either express or implied.
10  *
11  */
12 
13 /**
14  * @file Async.h
15  * @author Brian Geffon
16  * @author Manjesh Nilange
17  * @brief Provides constructs to perform async operations.
18  */
19 
20 #pragma once
21 #ifndef ATSCPPAPI_ASYNC_H_
22 #define ATSCPPAPI_ASYNC_H_
23 #include <list>
24 #include <atscppapi/Mutex.h>
25 #include <atscppapi/noncopyable.h>
26 #include <atscppapi/shared_ptr.h>
27 
28 namespace atscppapi {
29 
30 /**
31  * @private
32  *
33  * @brief This class represents the interface of a dispatch controller. A dispatch controller
34  * is used to dispatch an event to a receiver. This interface exists so that the types in this
35  * header file can be defined.
36  */
37 class AsyncDispatchControllerBase : noncopyable {
38 public:
39  /**
40  * Dispatches an async event to a receiver.
41  *
42  * @return True if the receiver was still alive.
43  */
44  virtual bool dispatch() = 0;
45  virtual ~AsyncDispatchControllerBase() { }
46 };
47 
48 /**
49  * @brief AsyncProvider is the interface that providers of async operations must implement.
50  * The system allows decoupling of the lifetime/scope of provider and receiver objects. The
51  * receiver object might have expired before the async operation is complete and the system
52  * handles this case. Because of this decoupling, it is the responsibility of the provider
53  * to manage it's expiration - self-destruct on completion is a good option.
54  */
56 public:
57  /**
58  * This method is invoked when the async operation is requested. This call should be used
59  * to just start the async operation and *not* block this thread.
60  *
61  * @param dispatch_controller provides a way to dispatch an "async complete" event to the
62  * requester.
63  */
64  virtual void run(shared_ptr<AsyncDispatchControllerBase> dispatch_controller) = 0;
65  virtual ~AsyncProvider() { }
66 };
67 
68 /**
69  * @private
70  *
71  * @brief Dispatch controller implementation. When invoking the receiver, it verifies that the
72  * receiver is still alive, locks the mutex and then invokes handleAsyncComplete().
73  */
74 template<typename AsyncEventReceiverType, typename AsyncProviderType>
75 class AsyncDispatchController : public AsyncDispatchControllerBase {
76 public:
77  bool dispatch() {
78  bool ret = false;
79  ScopedSharedMutexLock scopedLock(dispatch_mutex_);
80  if (event_receiver_) {
81  event_receiver_->handleAsyncComplete(static_cast<AsyncProviderType &>(*provider_));
82  ret = true;
83  }
84  return ret;
85  }
86 
87  /**
88  * Constructor
89  *
90  * @param event_receiver The async complete event will be dispatched to this receiver.
91  * @param provider Async operation provider that is passed to the receiver on dispatch.
92  * @param mutex Mutex of the receiver that is locked during the dispatch
93  */
94  AsyncDispatchController(AsyncEventReceiverType *event_receiver, AsyncProviderType *provider, shared_ptr<Mutex> mutex) :
95  event_receiver_(event_receiver), dispatch_mutex_(mutex), provider_(provider) {
96  }
97 
98  virtual ~AsyncDispatchController() { }
99 public:
100  AsyncEventReceiverType *event_receiver_;
101  shared_ptr<Mutex> dispatch_mutex_;
102 private:
103  AsyncProviderType *provider_;
104 };
105 
106 /**
107  * @private
108  *
109  * @brief A promise is used to let the dispatch controller know if the receiver is still
110  * alive to receive the async complete dispatch. When the receiver dies, this promise is
111  * broken and it automatically updates the dispatch controller.
112  */
113 template<typename AsyncEventReceiverType, typename AsyncProviderType>
114 class AsyncReceiverPromise : noncopyable {
115 public:
116  AsyncReceiverPromise(shared_ptr<AsyncDispatchController<AsyncEventReceiverType, AsyncProviderType> > dispatch_controller) :
117  dispatch_controller_(dispatch_controller) { }
118 
119  ~AsyncReceiverPromise() {
120  ScopedSharedMutexLock scopedLock(dispatch_controller_->dispatch_mutex_);
121  dispatch_controller_->event_receiver_ = NULL;
122  }
123 protected:
124  shared_ptr<AsyncDispatchController<AsyncEventReceiverType, AsyncProviderType> > dispatch_controller_;
125 };
126 
127 /**
128  * @brief AsyncReceiver is the interface that receivers of async operations must implement. It is
129  * templated on the type of the async operation provider.
130  */
131 template<typename AsyncProviderType>
132 class AsyncReceiver : noncopyable {
133 public:
134  /**
135  * This method is invoked when the async operation is completed. The
136  * mutex provided during the creation of the async operation will be
137  * automatically locked during the invocation of this method.
138  *
139  * @param provider A reference to the provider which completed the async operation.
140  */
141  virtual void handleAsyncComplete(AsyncProviderType &provider) = 0;
142  virtual ~AsyncReceiver() { }
143 protected:
144  AsyncReceiver() { }
145  friend class Async;
146 private:
147  mutable std::list<shared_ptr<AsyncReceiverPromise<AsyncReceiver<AsyncProviderType>, AsyncProviderType> > > receiver_promises_;
148 };
149 
150 /**
151  * @brief This class provides a method to create an async operation.
152  */
153 class Async : noncopyable {
154 public:
155  /**
156  * This method sets up the dispatch controller to link the async operation provider and
157  * receiver and then initiates the operation by invoking the provider.
158  *
159  * @param event_receiver The receiver of the async complete dispatch.
160  * @param provider The provider of the async operation.
161  * @param mutex The mutex that is locked during the dispatch of the async event complete.
162  * One will be created if nothing is passed in. Transaction plugins should use
163  * TransactionPlugin::getMutex() here and global plugins can pass an appropriate
164  * or NULL mutex.
165  */
166  template<typename AsyncProviderType>
167  static void execute(AsyncReceiver<AsyncProviderType> *event_receiver, AsyncProviderType *provider, shared_ptr<Mutex> mutex) {
168  if (!mutex.get()) {
169  mutex.reset(new Mutex(Mutex::TYPE_RECURSIVE));
170  }
171  shared_ptr<AsyncDispatchController<AsyncReceiver<AsyncProviderType>, AsyncProviderType > > dispatcher(
172  new AsyncDispatchController<AsyncReceiver<AsyncProviderType>, AsyncProviderType >(event_receiver, provider, mutex));
173  shared_ptr<AsyncReceiverPromise<AsyncReceiver<AsyncProviderType>, AsyncProviderType > > receiver_promise(
174  new AsyncReceiverPromise<AsyncReceiver<AsyncProviderType>, AsyncProviderType >(dispatcher));
175  event_receiver->receiver_promises_.push_back(receiver_promise); // now if the event receiver dies, we're safe.
176  provider->run(dispatcher);
177  }
178 };
179 
180 }
181 
182 
183 #endif /* ATSCPPAPI_ASYNC_H_ */