GCC Code Coverage Report


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