GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/util/plugin.h
Date: 2024-04-28 02:33:07
Exec Total Coverage
Lines: 49 51 96.1%
Branches: 25 40 62.5%

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 "util/atomic.h"
14 #include "util/concurrency.h"
15
16 #ifdef CVMFS_NAMESPACE_GUARD
17 namespace CVMFS_NAMESPACE_GUARD {
18 #endif
19
20 /**
21 * Used internally by the PolymorphicConstruction template
22 * Provides an abstract interface for Factory objects that allow the poly-
23 * morphic creation of arbitrary objects at runtime.
24 *
25 * @param AbstractProductT the abstract base class of all classes that could be
26 * polymorphically constructed by this factory
27 * @param ParameterT the type of the parameter that is used to figure out
28 * which class should be instantiated at runtime
29 * @param InfoT wrapper type for introspection data of registered
30 * plugins
31 */
32 template <class AbstractProductT, typename ParameterT, typename InfoT>
33 class AbstractFactory {
34 public:
35 284 AbstractFactory() {}
36 virtual ~AbstractFactory() {}
37
38 virtual bool WillHandle(const ParameterT &param) const = 0;
39 virtual AbstractProductT* Construct(const ParameterT &param) const = 0;
40 virtual InfoT Introspect() const = 0;
41 };
42
43
44 /**
45 * Implementation of the AbstractFactory template to wrap the creation of a
46 * specific class instance. Namely ConcreteProductT. (Note: still abstract)
47 * See the description of PolymorphicCreation for more details
48 *
49 * @param ConcreteProductT the class that will be instantiated by this factory
50 * class (must be derived from AbstractProductT)
51 * @param AbstractProductT the base class of all used ConcreteProductT classes
52 * @param ParameterT the type of the parameter that is used to poly-
53 * morphically create a specific ConcreteProductT
54 * @param InfoT wrapper type for introspection data of registered
55 * plugins
56 */
57 template <class ConcreteProductT,
58 class AbstractProductT,
59 typename ParameterT,
60 typename InfoT>
61 class AbstractFactoryImpl2 : public AbstractFactory<AbstractProductT,
62 ParameterT,
63 InfoT>
64 {
65 public:
66 1474817 inline bool WillHandle(const ParameterT &param) const {
67 1474817 return ConcreteProductT::WillHandle(param);
68 }
69 399769 inline AbstractProductT* Construct(const ParameterT &param) const {
70
1/2
✓ Branch 2 taken 250551 times.
✗ Branch 3 not taken.
799475 AbstractProductT* product = new ConcreteProductT(param);
71 800709 return product;
72 }
73 };
74
75
76 /**
77 * Template to add an implementation of Introspect() based on the type of InfoT.
78 * Generally Introspect() will call ConcreteProductT::GetInfo() and return it's
79 * result. However if InfoT = void, this method still needs to be stubbed.
80 * (See also the template specialization for InfoT = void below)
81 */
82 template <class ConcreteProductT,
83 class AbstractProductT,
84 typename ParameterT,
85 typename InfoT>
86 class AbstractFactoryImpl :
87 public AbstractFactoryImpl2<ConcreteProductT,
88 AbstractProductT,
89 ParameterT,
90 InfoT>
91 {
92 12 inline InfoT Introspect() const {
93 12 return ConcreteProductT::GetInfo();
94 }
95 };
96
97 /**
98 * Template specialization for InfoT = void that only stubs the abstract method
99 * Introspect().
100 */
101 template <class ConcreteProductT,
102 class AbstractProductT,
103 typename ParameterT>
104 class AbstractFactoryImpl<ConcreteProductT,
105 AbstractProductT,
106 ParameterT,
107 void> :
108 public AbstractFactoryImpl2<ConcreteProductT,
109 AbstractProductT,
110 ParameterT,
111 void>
112 {
113 inline void Introspect() const {}
114 };
115
116 /**
117 * Template to simplify the polymorphic creation of a number of concrete classes
118 * that share the common base class AbstractProductT. Use this to create flexible
119 * class hierarchies.
120 *
121 * The template assumes a number of things from the user classes:
122 * 1. AbstractProductT must implement `static void RegisterPlugins()` which
123 * will register all available derived classes by calling
124 * `RegisterPlugin<DerivedClass>()` for each implemented sub-class.
125 * 2. Each derived class of AbstractProductT must implement
126 * `static bool WillHandle(const ParameterT &param)` that figures out if the
127 * concrete class can cope with the given parameter
128 * 3. Each derived class must have at least the following constructor:
129 * `DerivedClass(const ParameterT &param)` which is used to instantiate the
130 * concrete class in case it returned true in WillHandle()
131 * 4. (OPTIONAL) Both AbstractProductT and ConcreteProductTs can override the
132 * virtual method `bool Initialize()` which will be called directly after
133 * creation of a ConcreteProductT. If it returns false, the constructed in-
134 * stance is deleted and the list of plugins is traversed further.
135 * 5. (OPTIONAL) The ConcreteProductTs can implement a `static InfoT GetInfo()`
136 * that can be used for run-time introspection of registered plugins using
137 * PolymorphicConstruction<AbstractProductT, ParameterT, InfoT>::Introspect()
138 *
139 * A possible class hierarchy could look like this:
140 *
141 * PolymorphicConstruction<AbstractNumberCruncher, Parameter>
142 * |
143 * +--> AbstractNumberCruncher
144 * |
145 * +--> ConcreteMulticoreNumberCruncher
146 * |
147 * +--> ConcreteGpuNumberCruncher
148 * |
149 * +--> ConcreteClusterNumberCruncher
150 *
151 * In this example AbstractNumberCruncher::RegisterPlugins() will register all
152 * three concrete number cruncher classes. Using the whole thing would look like
153 * so:
154 *
155 * Parameter param = Parameter(typicalGpuProblem);
156 * AbstractNumberCruncher *polymorphicCruncher =
157 * AbstractNumberCruncher::Construct(param);
158 * polymorphicCruncher->Crunch();
159 *
160 * `polymorphicCruncher` now points to an instance of ConcreteGpuNumberCruncher
161 * and can be used as any other polymorphic class with the interface defined in
162 * AbstractNumberCruncher.
163 *
164 * Note: PolymorphicCreation goes through the list of registered plugins in the
165 * order they have been registered and instantiates the first class that
166 * claims responsibility for the given parameter.
167 *
168 * @param AbstractProductT the common base class of all classes that should be
169 * polymorphically created. In most cases this will be
170 * the class that directly inherits from Polymorphic-
171 * Construction.
172 * @param ParameterT the type of the parameter that is used to poly-
173 * morphically instantiate one of the subclasses of
174 * AbstractProductT
175 * @param InfoT (optional) wrapper type for introspection data of
176 * registered plugins. InfoT AbstractProductT::GetInfo()
177 * needs to be implemented for each plugin
178 */
179 template <class AbstractProductT, typename ParameterT, typename InfoT>
180 class PolymorphicConstructionImpl {
181 protected:
182 typedef AbstractFactory<AbstractProductT, ParameterT, InfoT> Factory;
183 typedef std::vector<Factory*> RegisteredPlugins;
184
185 public:
186 800300 virtual ~PolymorphicConstructionImpl() { }
187
188 449871 static AbstractProductT* Construct(const ParameterT &param) {
189
1/2
✓ Branch 1 taken 450083 times.
✗ Branch 2 not taken.
449871 LazilyRegisterPlugins();
190
191 // select and initialize the correct plugin at runtime
192 // (polymorphic construction)
193 450083 typename RegisteredPlugins::const_iterator i = registered_plugins_.begin();
194 450161 typename RegisteredPlugins::const_iterator iend = registered_plugins_.end();
195
2/2
✓ Branch 2 taken 737610 times.
✓ Branch 3 taken 87605 times.
825467 for (; i != iend; ++i) {
196
3/4
✓ Branch 2 taken 737390 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 399740 times.
✓ Branch 5 taken 337650 times.
737610 if ((*i)->WillHandle(param)) {
197 // create and initialize the class that claimed responsibility
198
1/2
✓ Branch 2 taken 400259 times.
✗ Branch 3 not taken.
399740 AbstractProductT *product = (*i)->Construct(param);
199
3/4
✓ Branch 1 taken 400172 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 37428 times.
✓ Branch 4 taken 362744 times.
400259 if (!product->Initialize()) {
200
1/2
✓ Branch 0 taken 37428 times.
✗ Branch 1 not taken.
37428 delete product;
201 37428 continue;
202 }
203 362744 return product;
204 }
205 }
206
207 // no plugin found to handle the given parameter...
208 87605 return NULL;
209 }
210
211 protected:
212 449923 static void LazilyRegisterPlugins() {
213 // Thread Safety Note:
214 // Double Checked Locking with atomics!
215 // Simply double checking registered_plugins_.empty() is _not_ thread safe
216 // since a second thread might find a registered_plugins_ list that is
217 // currently under construction and therefore _not_ empty but also _not_
218 // fully initialized!
219 // See StackOverflow: http://stackoverflow.com/questions/8097439/lazy-initialized-caching-how-do-i-make-it-thread-safe
220
2/2
✓ Branch 1 taken 74 times.
✓ Branch 2 taken 450460 times.
449923 if (atomic_read32(&needs_init_)) {
221 74 MutexLockGuard m(&init_mutex_);
222
1/2
✓ Branch 1 taken 74 times.
✗ Branch 2 not taken.
74 if (atomic_read32(&needs_init_)) {
223
1/2
✓ Branch 1 taken 74 times.
✗ Branch 2 not taken.
74 AbstractProductT::RegisterPlugins();
224 74 atomic_dec32(&needs_init_);
225 }
226 74 }
227
228
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 450114 times.
450534 assert(!registered_plugins_.empty());
229 450114 }
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 505 static void RegisterPlugin() {
249 505 registered_plugins_.push_back(
250
2/4
✓ Branch 2 taken 284 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 284 times.
✗ Branch 6 not taken.
505 new AbstractFactoryImpl<ConcreteProductT,
251 AbstractProductT,
252 ParameterT,
253 InfoT>());
254 505 }
255
256 250293 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 74 static void UnregisterAllPlugins() {
271 74 registered_plugins_.clear();
272 74 needs_init_ = 1;
273 74 }
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 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
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 introspection_data.reserve(T::registered_plugins_.size());
304 2 const RegisteredPlugins &plugins = T::registered_plugins_;
305
306
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 T::LazilyRegisterPlugins();
307 2 typename RegisteredPlugins::const_iterator i = plugins.begin();
308 2 typename RegisteredPlugins::const_iterator iend = plugins.end();
309
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 2 times.
8 for (; i != iend; ++i) {
310
2/4
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 6 times.
✗ Branch 6 not taken.
6 introspection_data.push_back((*i)->Introspect());
311 }
312
313 4 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 registered_plugins_;
344
345
346 #ifdef CVMFS_NAMESPACE_GUARD
347 } // namespace CVMFS_NAMESPACE_GUARD
348 #endif
349
350 #endif // CVMFS_UTIL_PLUGIN_H_
351