Directory: | cvmfs/ |
---|---|
File: | cvmfs/util/async.h |
Date: | 2025-02-09 02:34:19 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 30 | 30 | 100.0% |
Branches: | 8 | 14 | 57.1% |
Line | Branch | Exec | Source |
---|---|---|---|
1 | /** | ||
2 | * This file is part of the CernVM File System. | ||
3 | */ | ||
4 | |||
5 | #ifndef CVMFS_UTIL_ASYNC_H_ | ||
6 | #define CVMFS_UTIL_ASYNC_H_ | ||
7 | |||
8 | #ifdef CVMFS_NAMESPACE_GUARD | ||
9 | namespace CVMFS_NAMESPACE_GUARD { | ||
10 | #endif | ||
11 | |||
12 | /** | ||
13 | * Encapsulates a callback function that handles asynchronous responses. | ||
14 | * | ||
15 | * This is an abstract base class for two different callback function objects. | ||
16 | * There are two specializations: | ||
17 | * --> 1. for static members or global C-like functions | ||
18 | * --> 2. for member functions of arbitrary objects | ||
19 | */ | ||
20 | template <typename ParamT> | ||
21 | class CallbackBase { | ||
22 | public: | ||
23 | 1714072 | virtual ~CallbackBase() {} | |
24 | virtual void operator()(const ParamT &value) const = 0; | ||
25 | }; | ||
26 | |||
27 | template <> | ||
28 | class CallbackBase<void> { | ||
29 | public: | ||
30 | 6 | virtual ~CallbackBase() {} | |
31 | virtual void operator()() const = 0; | ||
32 | }; | ||
33 | |||
34 | /** | ||
35 | * This callback function object can be used to call static members or global | ||
36 | * functions with the following signature: | ||
37 | * void <name>(ParamT <parameter>); | ||
38 | * | ||
39 | * TODO: One might use varidic templates once C++11 will be supported, in order | ||
40 | * to allow for more than one parameter to be passed to the callback. | ||
41 | * | ||
42 | * @param ParamT the type of the parameter to be passed to the callback | ||
43 | */ | ||
44 | template <typename ParamT> | ||
45 | class Callback : public CallbackBase<ParamT> { | ||
46 | public: | ||
47 | typedef void (*CallbackFunction)(const ParamT &value); | ||
48 | |||
49 | 91 | explicit Callback(CallbackFunction function) : function_(function) {} | |
50 | 619481 | void operator()(const ParamT &value) const { function_(value); } | |
51 | |||
52 | private: | ||
53 | CallbackFunction function_; | ||
54 | }; | ||
55 | |||
56 | template <> | ||
57 | class Callback<void> : public CallbackBase<void> { | ||
58 | public: | ||
59 | typedef void (*CallbackFunction)(); | ||
60 | |||
61 | 2 | explicit Callback(CallbackFunction function) : function_(function) {} | |
62 | 5 | void operator()() const { function_(); } | |
63 | |||
64 | private: | ||
65 | CallbackFunction function_; | ||
66 | }; | ||
67 | |||
68 | |||
69 | /** | ||
70 | * A BoundCallback can be used to call a member of an arbitrary object as a | ||
71 | * callback. | ||
72 | * The member must have the following interface: | ||
73 | * void <DelegateT>::<member name>(ParamT <parameter>); | ||
74 | * | ||
75 | * Note: it is the responsibility of the user to ensure that the bound object | ||
76 | * for `delegate` remains alive in the whole time this callback might be | ||
77 | * invoked. | ||
78 | * | ||
79 | * @param ParamT the type of the parameter to be passed to the callback | ||
80 | * @param DelegateT the <class name> of the object the member <member name> | ||
81 | * should be invoked in | ||
82 | */ | ||
83 | template <typename ParamT, class DelegateT> | ||
84 | class BoundCallback : public CallbackBase<ParamT> { | ||
85 | public: | ||
86 | typedef void (DelegateT::*CallbackMethod)(const ParamT &value); | ||
87 | |||
88 | 1146 | BoundCallback(CallbackMethod method, DelegateT *delegate) : | |
89 | 1146 | delegate_(delegate), | |
90 | 1146 | method_(method) {} | |
91 | |||
92 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 335778 times.
|
671553 | void operator()(const ParamT &value) const { (delegate_->*method_)(value); } |
93 | |||
94 | private: | ||
95 | DelegateT* delegate_; | ||
96 | CallbackMethod method_; | ||
97 | }; | ||
98 | |||
99 | template <class DelegateT> | ||
100 | class BoundCallback<void, DelegateT> : public CallbackBase<void> { | ||
101 | public: | ||
102 | typedef void (DelegateT::*CallbackMethod)(); | ||
103 | |||
104 | 4 | BoundCallback(CallbackMethod method, DelegateT *delegate) : | |
105 | 4 | delegate_(delegate), method_(method) {} | |
106 | |||
107 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
12 | void operator()() const { (delegate_->*method_)(); } |
108 | |||
109 | private: | ||
110 | DelegateT* delegate_; | ||
111 | CallbackMethod method_; | ||
112 | }; | ||
113 | |||
114 | |||
115 | /** | ||
116 | * A BoundClosure works exactly the same as a BoundCallback (see above) but, | ||
117 | * allows for an opaque enclosure of an arbitrary chunk of user data on | ||
118 | * creation. When the closure is invoked the provided user data chunk is | ||
119 | * passed as a second argument to the specified callback method. | ||
120 | * | ||
121 | * Note: delegate must be still around when the closure is invoked! | ||
122 | * | ||
123 | * @param ParamT the type of the parameter to be passed to the callback | ||
124 | * @param DelegateT the <class name> of the object the member <member name> | ||
125 | * should be invoked in | ||
126 | * @param ClosureDataT the type of the user data chunk to be passed on invoke | ||
127 | */ | ||
128 | template <typename ParamT, class DelegateT, typename ClosureDataT> | ||
129 | class BoundClosure : public CallbackBase<ParamT> { | ||
130 | public: | ||
131 | typedef void (DelegateT::*CallbackMethod)(const ParamT &value, | ||
132 | const ClosureDataT closure_data); | ||
133 | |||
134 | public: | ||
135 | 1527865 | BoundClosure(CallbackMethod method, | |
136 | DelegateT *delegate, | ||
137 | ClosureDataT data) : | ||
138 | 1527875 | delegate_(delegate), | |
139 | 1527875 | method_(method), | |
140 |
1/2✓ Branch 2 taken 2618 times.
✗ Branch 3 not taken.
|
1527865 | closure_data_(data) {} |
141 | |||
142 | 1528653 | void operator()(const ParamT &value) const { | |
143 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 856439 times.
✓ Branch 4 taken 2617 times.
✗ Branch 5 not taken.
|
1528653 | (delegate_->*method_)(value, closure_data_); |
144 | 1528640 | } | |
145 | |||
146 | private: | ||
147 | DelegateT* delegate_; | ||
148 | CallbackMethod method_; | ||
149 | const ClosureDataT closure_data_; | ||
150 | }; | ||
151 | |||
152 | template <class DelegateT, typename ClosureDataT> | ||
153 | class BoundClosure<void, DelegateT, ClosureDataT> : public CallbackBase<void> { | ||
154 | public: | ||
155 | typedef void (DelegateT::*CallbackMethod)(const ClosureDataT closure_data); | ||
156 | |||
157 | public: | ||
158 | 4 | BoundClosure(CallbackMethod method, | |
159 | DelegateT *delegate, | ||
160 | ClosureDataT data) : | ||
161 | 4 | delegate_(delegate), method_(method), closure_data_(data) {} | |
162 | |||
163 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
4 | void operator()() const { (delegate_->*method_)(closure_data_); } |
164 | |||
165 | private: | ||
166 | DelegateT* delegate_; | ||
167 | CallbackMethod method_; | ||
168 | const ClosureDataT closure_data_; | ||
169 | }; | ||
170 | |||
171 | |||
172 | /** | ||
173 | * This template contains convenience functions to be inherited by classes pro- | ||
174 | * viding callback functionality. You can use this as a base class providing the | ||
175 | * callback parameter your class is going to process. | ||
176 | * Users of your class can profit from the static MakeCallback resp. MakeClosure | ||
177 | * methods to generated Callback objects. Later they can easily be passed back | ||
178 | * to your class. | ||
179 | * | ||
180 | * Note: the convenience methods return a pointer to a callback object. The user | ||
181 | * is responsible to clean up those pointers in some way. If it is a one- | ||
182 | * time callback, it makes sense to delete it inside the inheriting class | ||
183 | * once it was invoked. | ||
184 | * | ||
185 | * TODO: C++11 - Replace this functionality by lambdas | ||
186 | * | ||
187 | * @param ParamT the parameter type of the callbacks to be called | ||
188 | */ | ||
189 | template <class ParamT> | ||
190 | class Callbackable { | ||
191 | public: | ||
192 | typedef CallbackBase<ParamT> CallbackTN; | ||
193 | |||
194 | public: | ||
195 | /** | ||
196 | * Produces a BoundClosure object that captures a closure value passed along | ||
197 | * to the invoked method. | ||
198 | * | ||
199 | * @param method Function pointer to the method to be called | ||
200 | * @param delegate The delegate object on which <method> will be called | ||
201 | * @param closure_data The closure data to be passed along to <method> | ||
202 | */ | ||
203 | template <class DelegateT, typename ClosureDataT> | ||
204 | 855906 | static CallbackTN* MakeClosure( | |
205 | typename BoundClosure<ParamT, DelegateT, ClosureDataT>:: | ||
206 | CallbackMethod method, | ||
207 | DelegateT *delegate, | ||
208 | const ClosureDataT &closure_data) | ||
209 | { | ||
210 | 855906 | return new BoundClosure<ParamT, DelegateT, ClosureDataT>(method, | |
211 | delegate, | ||
212 |
1/2✓ Branch 1 taken 22 times.
✗ Branch 2 not taken.
|
858620 | closure_data); |
213 | } | ||
214 | |||
215 | /** | ||
216 | * Produces a BoundCallback object that invokes a method on a delegate object. | ||
217 | * | ||
218 | * @param method Function pointer to the method to be called | ||
219 | * @param delegate The delegate object on which <method> will be called | ||
220 | */ | ||
221 | template <class DelegateT> | ||
222 | 585 | static CallbackTN* MakeCallback( | |
223 | typename BoundCallback<ParamT, DelegateT>::CallbackMethod method, | ||
224 | DelegateT *delegate) { | ||
225 | 585 | return new BoundCallback<ParamT, DelegateT>(method, delegate); | |
226 | } | ||
227 | |||
228 | /** | ||
229 | * Produces a Callback object that invokes a static function or a globally de- | ||
230 | * fined C-like function. | ||
231 | * | ||
232 | * @param function Function pointer to the function to be invoked | ||
233 | */ | ||
234 | 87 | static CallbackTN* MakeCallback( | |
235 | typename Callback<ParamT>::CallbackFunction function) { | ||
236 | 87 | return new Callback<ParamT>(function); | |
237 | } | ||
238 | }; | ||
239 | |||
240 | |||
241 | /** | ||
242 | * Wrapper function to bind an arbitrary this* to a method call in a C-style | ||
243 | * spawned thread function. | ||
244 | * The method called by the ThreadProxy template is meant to look like this: | ||
245 | * void foo(); | ||
246 | */ | ||
247 | template <class DelegateT> | ||
248 | void ThreadProxy(DelegateT *delegate, | ||
249 | void (DelegateT::*method)()) { | ||
250 | (*delegate.*method)(); | ||
251 | } | ||
252 | |||
253 | |||
254 | |||
255 | #ifdef CVMFS_NAMESPACE_GUARD | ||
256 | } // namespace CVMFS_NAMESPACE_GUARD | ||
257 | #endif | ||
258 | |||
259 | #endif // CVMFS_UTIL_ASYNC_H_ | ||
260 |