GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/** |
||
2 |
* This file is part of the CernVM File System. |
||
3 |
*/ |
||
4 |
|||
5 |
#ifndef CVMFS_UTIL_PLUGIN_H_ |
||
6 |
#define CVMFS_UTIL_PLUGIN_H_ |
||
7 |
|||
8 |
#include <pthread.h> |
||
9 |
|||
10 |
#include <cassert> |
||
11 |
#include <vector> |
||
12 |
|||
13 |
#include "atomic.h" |
||
14 |
|||
15 |
#ifdef CVMFS_NAMESPACE_GUARD |
||
16 |
namespace CVMFS_NAMESPACE_GUARD { |
||
17 |
#endif |
||
18 |
|||
19 |
/** |
||
20 |
* Used internally by the PolymorphicConstruction template |
||
21 |
* Provides an abstract interface for Factory objects that allow the poly- |
||
22 |
* morphic creation of arbitrary objects at runtime. |
||
23 |
* |
||
24 |
* @param AbstractProductT the abstract base class of all classes that could be |
||
25 |
* polymorphically constructed by this factory |
||
26 |
* @param ParameterT the type of the parameter that is used to figure out |
||
27 |
* which class should be instanciated at runtime |
||
28 |
* @param InfoT wrapper type for introspection data of registered |
||
29 |
* plugins |
||
30 |
*/ |
||
31 |
template <class AbstractProductT, typename ParameterT, typename InfoT> |
||
32 |
class AbstractFactory { |
||
33 |
public: |
||
34 |
461 |
AbstractFactory() {} |
|
35 |
virtual ~AbstractFactory() {} |
||
36 |
|||
37 |
virtual bool WillHandle(const ParameterT ¶m) const = 0; |
||
38 |
virtual AbstractProductT* Construct(const ParameterT ¶m) const = 0; |
||
39 |
virtual InfoT Introspect() const = 0; |
||
40 |
}; |
||
41 |
|||
42 |
|||
43 |
/** |
||
44 |
* Implementation of the AbstractFactory template to wrap the creation of a |
||
45 |
* specific class instance. Namely ConcreteProductT. (Note: still abstract) |
||
46 |
* See the description of PolymorphicCreation for more details |
||
47 |
* |
||
48 |
* @param ConcreteProductT the class that will be instanciated by this factory |
||
49 |
* class (must be derived from AbstractProductT) |
||
50 |
* @param AbstractProductT the base class of all used ConcreteProductT classes |
||
51 |
* @param ParameterT the type of the parameter that is used to poly- |
||
52 |
* morphically create a specific ConcreteProductT |
||
53 |
* @param InfoT wrapper type for introspection data of registered |
||
54 |
* plugins |
||
55 |
*/ |
||
56 |
template <class ConcreteProductT, |
||
57 |
class AbstractProductT, |
||
58 |
typename ParameterT, |
||
59 |
typename InfoT> |
||
60 |
class AbstractFactoryImpl2 : public AbstractFactory<AbstractProductT, |
||
61 |
ParameterT, |
||
62 |
InfoT> |
||
63 |
✗✗✗✗ ✗✗✗✗ ✗ |
461 |
{ |
64 |
public: |
||
65 |
494899 |
inline bool WillHandle(const ParameterT ¶m) const { |
|
66 |
494899 |
return ConcreteProductT::WillHandle(param); |
|
67 |
} |
||
68 |
157124 |
inline AbstractProductT* Construct(const ParameterT ¶m) const { |
|
69 |
157124 |
AbstractProductT* product = new ConcreteProductT(param); |
|
70 |
157124 |
return product; |
|
71 |
} |
||
72 |
}; |
||
73 |
|||
74 |
|||
75 |
/** |
||
76 |
* Template to add an implementation of Introspect() based on the type of InfoT. |
||
77 |
* Generally Introspect() will call ConcreteProductT::GetInfo() and return it's |
||
78 |
* result. However if InfoT = void, this method still needs to be stubbed. |
||
79 |
* (See also the template specialization for InfoT = void below) |
||
80 |
*/ |
||
81 |
template <class ConcreteProductT, |
||
82 |
class AbstractProductT, |
||
83 |
typename ParameterT, |
||
84 |
typename InfoT> |
||
85 |
class AbstractFactoryImpl : |
||
86 |
public AbstractFactoryImpl2<ConcreteProductT, |
||
87 |
AbstractProductT, |
||
88 |
ParameterT, |
||
89 |
InfoT> |
||
90 |
✗✗✗✗ ✗✗✗✗ ✗ |
461 |
{ |
91 |
6 |
inline InfoT Introspect() const { |
|
92 |
6 |
return ConcreteProductT::GetInfo(); |
|
93 |
} |
||
94 |
}; |
||
95 |
|||
96 |
/** |
||
97 |
* Template specialization for InfoT = void that only stubs the abstract method |
||
98 |
* Introspect(). |
||
99 |
*/ |
||
100 |
template <class ConcreteProductT, |
||
101 |
class AbstractProductT, |
||
102 |
typename ParameterT> |
||
103 |
class AbstractFactoryImpl<ConcreteProductT, |
||
104 |
AbstractProductT, |
||
105 |
ParameterT, |
||
106 |
void> : |
||
107 |
public AbstractFactoryImpl2<ConcreteProductT, |
||
108 |
AbstractProductT, |
||
109 |
ParameterT, |
||
110 |
void> |
||
111 |
{ |
||
112 |
inline void Introspect() const {} |
||
113 |
}; |
||
114 |
|||
115 |
/** |
||
116 |
* Template to simplify the polymorphic creation of a number of concrete classes |
||
117 |
* that share the common base class AbstractProductT. Use this to create flexible |
||
118 |
* class hierarchies. |
||
119 |
* |
||
120 |
* The template assumes a number of things from the user classes: |
||
121 |
* 1. AbstractProductT must implement `static void RegisterPlugins()` which |
||
122 |
* will register all available derived classes by calling |
||
123 |
* `RegisterPlugin<DerivedClass>()` for each implemented sub-class. |
||
124 |
* 2. Each derived class of AbstractProductT must implement |
||
125 |
* `static bool WillHandle(const ParameterT ¶m)` that figures out if the |
||
126 |
* concrete class can cope with the given parameter |
||
127 |
* 3. Each derived class must have at least the following constructor: |
||
128 |
* `DerivedClass(const ParameterT ¶m)` which is used to instantiate the |
||
129 |
* concrete class in case it returned true in WillHandle() |
||
130 |
* 4. (OPTIONAL) Both AbstractProductT and ConcreteProductTs can override the |
||
131 |
* virtual method `bool Initialize()` which will be called directly after |
||
132 |
* creation of a ConcreteProductT. If it returns false, the constructed in- |
||
133 |
* stance is deleted and the list of plugins is traversed further. |
||
134 |
* 5. (OPTIONAL) The ConcreteProductTs can implement a `static InfoT GetInfo()` |
||
135 |
* that can be used for run-time introspection of registered plugins using |
||
136 |
* PolymorphicConstruction<AbstractProductT, ParameterT, InfoT>::Introspect() |
||
137 |
* |
||
138 |
* A possible class hierarchy could look like this: |
||
139 |
* |
||
140 |
* PolymorphicConstruction<AbstractNumberCruncher, Parameter> |
||
141 |
* | |
||
142 |
* +--> AbstractNumberCruncher |
||
143 |
* | |
||
144 |
* +--> ConcreteMulticoreNumberCruncher |
||
145 |
* | |
||
146 |
* +--> ConcreteGpuNumberCruncher |
||
147 |
* | |
||
148 |
* +--> ConcreteClusterNumberCruncher |
||
149 |
* |
||
150 |
* In this example AbstractNumberCruncher::RegisterPlugins() will register all |
||
151 |
* three concrete number cruncher classes. Using the whole thing would look like |
||
152 |
* so: |
||
153 |
* |
||
154 |
* Parameter param = Parameter(typicalGpuProblem); |
||
155 |
* AbstractNumberCruncher *polymorphicCruncher = |
||
156 |
* AbstractNumberCruncher::Construct(param); |
||
157 |
* polymorphicCruncher->Crunch(); |
||
158 |
* |
||
159 |
* `polymorphicCruncher` now points to an instance of ConcreteGpuNumberCruncher |
||
160 |
* and can be used as any other polymorphic class with the interface defined in |
||
161 |
* AbstractNumberCruncher. |
||
162 |
* |
||
163 |
* Note: PolymorphicCreation goes through the list of registered plugins in the |
||
164 |
* order they have been registered and instantiates the first class that |
||
165 |
* claims responsibility for the given parameter. |
||
166 |
* |
||
167 |
* @param AbstractProductT the common base class of all classes that should be |
||
168 |
* polymorphically created. In most cases this will be |
||
169 |
* the class that directly inherits from Polymorphic- |
||
170 |
* Construction. |
||
171 |
* @param ParameterT the type of the parameter that is used to poly- |
||
172 |
* morphically instantiate one of the subclasses of |
||
173 |
* AbstractProductT |
||
174 |
* @param InfoT (optional) wrapper type for introspection data of |
||
175 |
* registered plugins. InfoT AbstractProductT::GetInfo() |
||
176 |
* needs to be implemented for each plugin |
||
177 |
*/ |
||
178 |
template <class AbstractProductT, typename ParameterT, typename InfoT> |
||
179 |
157128 |
class PolymorphicConstructionImpl { |
|
180 |
protected: |
||
181 |
typedef AbstractFactory<AbstractProductT, ParameterT, InfoT> Factory; |
||
182 |
typedef std::vector<Factory*> RegisteredPlugins; |
||
183 |
|||
184 |
public: |
||
185 |
✗✓ | 157128 |
virtual ~PolymorphicConstructionImpl() { } |
186 |
|||
187 |
207065 |
static AbstractProductT* Construct(const ParameterT ¶m) { |
|
188 |
207065 |
LazilyRegisterPlugins(); |
|
189 |
|||
190 |
// select and initialize the correct plugin at runtime |
||
191 |
// (polymorphic construction) |
||
192 |
207065 |
typename RegisteredPlugins::const_iterator i = registered_plugins_.begin(); |
|
193 |
207065 |
typename RegisteredPlugins::const_iterator iend = registered_plugins_.end(); |
|
194 |
✓✓ | 582377 |
for (; i != iend; ++i) { |
195 |
✓✓ | 494899 |
if ((*i)->WillHandle(param)) { |
196 |
// create and initialize the class that claimed responsibility |
||
197 |
157124 |
AbstractProductT *product = (*i)->Construct(param); |
|
198 |
✓✓ | 157124 |
if (!product->Initialize()) { |
199 |
✓✗ | 37537 |
delete product; |
200 |
37537 |
continue; |
|
201 |
} |
||
202 |
119587 |
return product; |
|
203 |
} |
||
204 |
} |
||
205 |
|||
206 |
// no plugin found to handle the given parameter... |
||
207 |
87478 |
return NULL; |
|
208 |
} |
||
209 |
|||
210 |
protected: |
||
211 |
207067 |
static void LazilyRegisterPlugins() { |
|
212 |
// Thread Safety Note: |
||
213 |
// Double Checked Locking with atomics! |
||
214 |
// Simply double checking registered_plugins_.empty() is _not_ thread safe |
||
215 |
// since a second thread might find a registered_plugins_ list that is |
||
216 |
// currently under construction and therefore _not_ empty but also _not_ |
||
217 |
// fully initialized! |
||
218 |
// See StackOverflow: http://stackoverflow.com/questions/8097439/lazy-initialized-caching-how-do-i-make-it-thread-safe |
||
219 |
✓✓ | 207067 |
if (atomic_read32(&needs_init_)) { |
220 |
123 |
pthread_mutex_lock(&init_mutex_); |
|
221 |
✓✗ | 123 |
if (atomic_read32(&needs_init_)) { |
222 |
123 |
AbstractProductT::RegisterPlugins(); |
|
223 |
123 |
atomic_dec32(&needs_init_); |
|
224 |
} |
||
225 |
123 |
pthread_mutex_unlock(&init_mutex_); |
|
226 |
} |
||
227 |
|||
228 |
✗✓ | 207067 |
assert(!registered_plugins_.empty()); |
229 |
207067 |
} |
|
230 |
|||
231 |
/** |
||
232 |
* Friend class for testability (see test/common/testutil.h) |
||
233 |
*/ |
||
234 |
friend class PolymorphicConstructionUnittestAdapter; |
||
235 |
|||
236 |
/** |
||
237 |
* Registers a plugin that is polymorphically constructable afterwards. |
||
238 |
* Warning: Multiple registrations of the same ConcreteProductT might lead to |
||
239 |
* undefined behaviour! |
||
240 |
* |
||
241 |
* @param ConcreteProductT the concrete implementation of AbstractProductT |
||
242 |
* that should be registered as constructable. |
||
243 |
* |
||
244 |
* Note: You shall not need to use this method anywhere in your code |
||
245 |
* except in AbstractProductT::RegisterPlugins(). |
||
246 |
*/ |
||
247 |
template <class ConcreteProductT> |
||
248 |
461 |
static void RegisterPlugin() { |
|
249 |
461 |
registered_plugins_.push_back( |
|
250 |
new AbstractFactoryImpl<ConcreteProductT, |
||
251 |
AbstractProductT, |
||
252 |
ParameterT, |
||
253 |
InfoT>()); |
||
254 |
461 |
} |
|
255 |
|||
256 |
6829 |
virtual bool Initialize() { return true; } |
|
257 |
|||
258 |
private: |
||
259 |
/** |
||
260 |
* This method clears the list of registered plugins. |
||
261 |
* Note: A user of PolymorphicConstruction is _not_ supposed to use this! The |
||
262 |
* method is meant to be used solely for testing purposes! In particular |
||
263 |
* a unit test registering a mocked plugin is supposed to clear up after |
||
264 |
* _each_ unit test! see: gtest: SetUp() / TearDown() and |
||
265 |
* PolymorphicConstructionUnittestAdapter |
||
266 |
* |
||
267 |
* DO NOT USE THIS OUTSIDE UNIT TESTS!! |
||
268 |
* -> Global state is nasty! |
||
269 |
*/ |
||
270 |
107 |
static void UnregisterAllPlugins() { |
|
271 |
107 |
registered_plugins_.clear(); |
|
272 |
107 |
needs_init_ = 1; |
|
273 |
107 |
} |
|
274 |
|||
275 |
protected: |
||
276 |
static RegisteredPlugins registered_plugins_; |
||
277 |
|||
278 |
private: |
||
279 |
static atomic_int32 needs_init_; |
||
280 |
static pthread_mutex_t init_mutex_; |
||
281 |
}; |
||
282 |
|||
283 |
|||
284 |
/** |
||
285 |
* Interface template for PolymorphicConstruction. |
||
286 |
* This adds the static method Introspect() to each PolymorphicConstruction type |
||
287 |
* implementation if (and only if) InfoT is not void. |
||
288 |
* Backward compatibility: if InfoT is not defined (i.e. is void), Introspect() |
||
289 |
* is not defined at all! (see template specialization) |
||
290 |
*/ |
||
291 |
template <class AbstractProductT, typename ParameterT, typename InfoT = void> |
||
292 |
class PolymorphicConstruction : |
||
293 |
✗✓✓ | 314256 |
public PolymorphicConstructionImpl<AbstractProductT, ParameterT, InfoT> { |
294 |
private: |
||
295 |
typedef PolymorphicConstructionImpl<AbstractProductT, ParameterT, InfoT> T; |
||
296 |
typedef typename T::RegisteredPlugins RegisteredPlugins; |
||
297 |
|||
298 |
public: |
||
299 |
typedef std::vector<InfoT> IntrospectionData; |
||
300 |
|||
301 |
2 |
static IntrospectionData Introspect() { |
|
302 |
2 |
IntrospectionData introspection_data; |
|
303 |
2 |
introspection_data.reserve(T::registered_plugins_.size()); |
|
304 |
2 |
const RegisteredPlugins &plugins = T::registered_plugins_; |
|
305 |
|||
306 |
2 |
T::LazilyRegisterPlugins(); |
|
307 |
2 |
typename RegisteredPlugins::const_iterator i = plugins.begin(); |
|
308 |
2 |
typename RegisteredPlugins::const_iterator iend = plugins.end(); |
|
309 |
✓✓ | 8 |
for (; i != iend; ++i) { |
310 |
6 |
introspection_data.push_back((*i)->Introspect()); |
|
311 |
} |
||
312 |
|||
313 |
2 |
return introspection_data; |
|
314 |
} |
||
315 |
}; |
||
316 |
|||
317 |
/** |
||
318 |
* Template specialization for backward compatibility that _does not_ implement |
||
319 |
* a static Introspect() method when the InfoT parameter is not given or is void |
||
320 |
*/ |
||
321 |
template <class AbstractProductT, typename ParameterT> |
||
322 |
class PolymorphicConstruction<AbstractProductT, ParameterT, void> : |
||
323 |
public PolymorphicConstructionImpl<AbstractProductT, ParameterT, void> {}; |
||
324 |
|||
325 |
|||
326 |
|||
327 |
template <class AbstractProductT, typename ParameterT, typename InfoT> |
||
328 |
atomic_int32 |
||
329 |
PolymorphicConstructionImpl<AbstractProductT, ParameterT, InfoT>:: |
||
330 |
needs_init_ = 1; |
||
331 |
|||
332 |
template <class AbstractProductT, typename ParameterT, typename InfoT> |
||
333 |
pthread_mutex_t |
||
334 |
PolymorphicConstructionImpl<AbstractProductT, ParameterT, InfoT>::init_mutex_ = |
||
335 |
PTHREAD_MUTEX_INITIALIZER; |
||
336 |
|||
337 |
// init the static member registered_plugins_ inside the |
||
338 |
// PolymorphicConstructionImpl template... whoa, what ugly code :o) |
||
339 |
template <class AbstractProductT, typename ParameterT, typename InfoT> |
||
340 |
typename PolymorphicConstructionImpl<AbstractProductT, ParameterT, InfoT>:: |
||
341 |
RegisteredPlugins |
||
342 |
PolymorphicConstructionImpl<AbstractProductT, ParameterT, InfoT>:: |
||
343 |
✓✓ | 167 |
registered_plugins_; |
344 |
|||
345 |
|||
346 |
#ifdef CVMFS_NAMESPACE_GUARD |
||
347 |
} // namespace CVMFS_NAMESPACE_GUARD |
||
348 |
#endif |
||
349 |
|||
350 |
#endif // CVMFS_UTIL_PLUGIN_H_ |
Generated by: GCOVR (Version 4.1) |