GCC Code Coverage Report
Directory: cvmfs/ Exec Total Coverage
File: cvmfs/util/plugin.h Lines: 53 55 96.4 %
Date: 2019-02-03 02:48:13 Branches: 18 43 41.9 %

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 &param) const = 0;
38
  virtual AbstractProductT* Construct(const ParameterT &param) 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 &param) const {
66
494899
    return ConcreteProductT::WillHandle(param);
67
  }
68
157124
  inline AbstractProductT* Construct(const ParameterT &param) 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 &param)` 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 &param)` 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 &param) {
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_