Project
Loading...
Searching...
No Matches
ServiceRegistry.h
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11#ifndef O2_FRAMEWORK_SERVICEREGISTRY_H_
12#define O2_FRAMEWORK_SERVICEREGISTRY_H_
13
19
20#include <array>
21#include <string>
22#include <type_traits>
23#include <typeinfo>
24#include <atomic>
25#include <mutex>
26
27namespace o2::framework
28{
29
30struct DeviceState;
31
36
37struct NoLocking {
38 void lock() {}
39 void unlock() {}
40};
41
42struct O2_DPL_CAPABILITY("mutex") MutexLock {
43 void lock() O2_DPL_ACQUIRE() { mutex.lock(); }
44 void unlock() O2_DPL_RELEASE() { mutex.unlock(); }
45 std::mutex& mutex;
46};
47
48// A pointer to a service. Includes the locking policy
49// for that service.
50template <typename T, typename LOCKING = NoLocking>
51class service_ptr : LOCKING
52{
53 public:
54 service_ptr(T* ptr, LOCKING policy) : LOCKING(policy), mPtr{ptr} { this->lock(); }
55 ~service_ptr() { this->unlock(); }
58 T& operator*() { return *mPtr; }
59 T* operator->() { return mPtr; }
60
61 private:
62 T* mPtr;
63};
64
65template <typename, typename = void>
69
70template <typename T>
71struct ServiceKindExtractor<T, std::void_t<decltype(T::service_kind)>> : std::is_same<decltype(T::service_kind), enum ServiceKind> {
72 constexpr static ServiceKind kind = T::service_kind;
73};
74
75template <typename T>
77
79 struct Salt {
80 short streamId = 0;
81 short dataProcessorId = 0;
82 };
83
84 enum struct SpecialStreamId : short {
85 Global = 0,
86 Callback = -1,
87 Invalid = -2
88 };
89
90 enum struct SpecialDataProcessorId : short {
91 Device = -1,
92 Invalid = -2
93 };
94
95 static constexpr Salt GLOBAL_CONTEXT_SALT{0, 0};
96
97 struct InstanceId {
98 uint32_t id = 0;
99 };
100
101 struct Index {
102 int32_t index = -1;
103 };
104
105 struct SpecIndex {
106 int index = -1;
107 };
108
109 // Metadata about the service. This
110 // might be interesting for debugging purposes.
111 // however it's not used to uniquely identify
112 // the service.
113 struct Meta {
115 char const* name = nullptr;
116 // The index in the
118 };
119
120 // Unique identifier for a service.
121 // While we use the salted hash to find the bucket
122 // in the hashmap, the service can be uniquely identified
123 // only by this 64 bit value.
128
129 static constexpr int32_t valueFromSalt(Salt salt) { return ((int32_t)salt.streamId) << 16 | salt.dataProcessorId; }
130 static constexpr uint64_t valueFromKey(Key key) { return ((uint64_t)key.typeHash.hash) << 32 | ((uint64_t)valueFromSalt(key.salt)); }
131
133 constexpr static int32_t MAX_DISTANCE = 8;
135 constexpr static uint32_t MAX_SERVICES = 256;
137 constexpr static uint32_t MAX_SERVICES_MASK = MAX_SERVICES - 1;
138
145 {
146 return GLOBAL_CONTEXT_SALT;
147 }
148
151 static Salt globalStreamSalt(short streamId)
152 {
153 return {streamId, 0};
154 }
155
161 static Salt dataProcessorSalt(short /* dataProcessorId */)
162 {
163 // FIXME: old behaviour for now
164 // return {0, dataProcessorId};
165 return GLOBAL_CONTEXT_SALT;
166 }
167
171 static Salt streamSalt(short streamId, short dataProcessorId)
172 {
173 // FIXME: old behaviour for now
174 // return {streamId, dataProcessorId};
175 return {streamId, dataProcessorId};
176 }
177
179 {
180 // Fold the whole salt down into the low bits. The slot is the low bits of
181 // this (see indexFromInstance) while streamId sits at bit 16 of
182 // valueFromSalt, so using that directly gives every stream the same slot
183 // for a given service: they pile into one probe window, and once it is
184 // MAX_DISTANCE deep the next registration is refused -- reported against
185 // whichever service happened to lose, not the one which filled it.
186 //
187 // Widening the table does not help on its own: the mask stays below bit 16
188 // until MAX_SERVICES passes 65536.
189 uint32_t mixed = static_cast<uint32_t>(static_cast<uint16_t>(salt.streamId)) * 0x9E3779B9u ^
190 static_cast<uint32_t>(static_cast<uint16_t>(salt.dataProcessorId));
191 return InstanceId{type.hash ^ mixed};
192 }
193
195 {
196 static_assert(MAX_SERVICES_MASK < 0x7FFFFFFF, "MAX_SERVICES_MASK must be smaller than 0x7FFFFFFF");
197 return Index{static_cast<int32_t>(id.id & MAX_SERVICES_MASK)};
198 }
199
201 mutable std::vector<ServicePostRenderGUIHandle> mPostRenderGUIHandles;
202
204 void throwError(const char* name, int64_t hash, int64_t streamId, int64_t dataprocessorId) const;
205
206 public:
207 using hash_type = decltype(TypeIdHelpers::uniqueId<void>());
209
212
215
219
228 void declareService(ServiceSpec const& spec, DeviceState& state, fair::mq::ProgOptions& options, ServiceRegistry::Salt salt = ServiceRegistry::globalDeviceSalt());
229
230 void bindService(ServiceRegistry::Salt salt, ServiceSpec const& spec, void* service) const;
231
232 void lateBindStreamServices(DeviceState& state, fair::mq::ProgOptions& options, ServiceRegistry::Salt salt);
233
238 void registerService(ServiceTypeHash typeHash, void* service, ServiceKind kind, Salt salt, char const* name = nullptr, ServiceRegistry::SpecIndex specIndex = SpecIndex{-1}) const;
239
240 // Lookup a given @a typeHash for a given @a threadId at
241 // a unique (per typeHash) location. There might
242 // be other typeHash which sit in the same place, but
243 // the if statement will rule them out. As long as
244 // only one thread writes in a given i + id location
245 // as guaranteed by the atomic, mServicesKey[i + id] will
246 // either be 0 or the final value.
247 // This method should NEVER register a new service, event when requested.
248 int getPos(ServiceTypeHash typeHash, Salt salt) const;
249
250 // Basic, untemplated API. This will require explicitly
251 // providing the @a typeHash for the Service type,
252 // there @a threadId of the thread asking the service
253 // and the @a kind of service. This
254 // method might trigger the registering of a new service
255 // if the service is not a stream service and the global
256 // zero service is available.
257 // Use this API only if you know what you are doing.
258 void* get(ServiceTypeHash typeHash, Salt salt, ServiceKind kind, char const* name = nullptr) const;
259
262 {
263 ServiceRegistry::registerService({handle.hash}, handle.instance, handle.kind, salt, handle.name.c_str());
264 }
265
266 mutable std::vector<ServiceSpec> mSpecs;
267 mutable std::array<std::atomic<Key>, MAX_SERVICES + MAX_DISTANCE> mServicesKey;
268 mutable std::array<void*, MAX_SERVICES + MAX_DISTANCE> mServicesValue;
269 mutable std::array<Meta, MAX_SERVICES + MAX_DISTANCE> mServicesMeta;
270 mutable std::array<std::atomic<bool>, MAX_SERVICES + MAX_DISTANCE> mServicesBooked;
271 mutable std::recursive_mutex mMutex;
272 mutable int64_t mLastLock = -1;
273 mutable std::atomic<int> lockCounter = {0};
274
276 template <class I, class C, enum ServiceKind K = ServiceKind::Serial>
277 requires std::is_base_of_v<I, C>
279 {
280 // This only works for concrete implementations of the type T.
281 // We need type elision as we do not want to know all the services in
282 // advance
283 constexpr ServiceTypeHash typeHash{TypeIdHelpers::uniqueId<I>()};
284 ServiceRegistry::registerService(typeHash, reinterpret_cast<void*>(service), K, salt, typeid(C).name());
285 }
286
288 template <class I, class C, enum ServiceKind K = ServiceKind::Serial>
289 requires std::is_base_of_v<I, C>
291 {
292 // This only works for concrete implementations of the type T.
293 // We need type elision as we do not want to know all the services in
294 // advance
295 constexpr ServiceTypeHash typeHash{TypeIdHelpers::uniqueId<I const>()};
296 this->registerService(typeHash, reinterpret_cast<void*>(const_cast<C*>(service)), K, salt, typeid(C).name());
297 }
298
300 template <typename T>
301 requires(std::is_const_v<T> == false)
302 bool active(Salt salt) const
303 {
304 constexpr ServiceTypeHash typeHash{TypeIdHelpers::uniqueId<T>()};
305 if (this->getPos(typeHash, GLOBAL_CONTEXT_SALT) != -1) {
306 return true;
307 }
308 auto result = this->getPos(typeHash, salt) != -1;
309 return result;
310 }
311
315 template <typename T>
316 T& get(Salt salt) const
317 {
318 constexpr ServiceTypeHash typeHash{TypeIdHelpers::uniqueId<T>()};
319 auto ptr = this->get(typeHash, salt, ServiceKindExtractor<T>::kind, typeid(T).name());
320 if (O2_BUILTIN_LIKELY(ptr != nullptr)) {
321 if constexpr (std::is_const_v<T>) {
322 return *reinterpret_cast<T const*>(ptr);
323 } else {
324 return *reinterpret_cast<T*>(ptr);
325 }
326 }
327 throwError(typeid(T).name(), typeHash.hash, salt.streamId, salt.dataProcessorId);
329 }
330
337 void lock(Salt salt) const O2_DPL_ACQUIRE(mMutex);
338
341 void unlock(Salt salt) const O2_DPL_RELEASE(mMutex);
342};
343
344} // namespace o2::framework
345
346#endif // O2_FRAMEWORK_SERVICEREGISTRY_H_
benchmark::State & state
#define O2_BUILTIN_UNREACHABLE
#define O2_BUILTIN_LIKELY(x)
uint32_t hash
TBranch * ptr
#define O2_DPL_ACQUIRE(...)
#define O2_DPL_RELEASE(...)
#define O2_DPL_CAPABILITY(x)
StringRef key
service_ptr(service_ptr< T, LOCKING > const &)=delete
service_ptr(T *ptr, LOCKING policy)
service_ptr & operator=(service_ptr< T, LOCKING > const &)=delete
GLuint64EXT * result
Definition glcorearb.h:5662
GLuint index
Definition glcorearb.h:781
GLuint const GLchar * name
Definition glcorearb.h:781
GLint GLint GLsizei GLint GLenum GLenum type
Definition glcorearb.h:275
Defining ITS Vertex explicitly as messageable.
Definition Cartesian.h:288
ServiceKind
The kind of service we are asking for.
constexpr ServiceKind service_kind_v
Running state information of a given device.
Definition DeviceState.h:34
ServiceKind kind
Kind of service.
void * instance
Type erased pointer to a service.
unsigned int hash
Unique hash associated to the type of service.
std::string name
Mnemonic name to use for the service.
static constexpr ServiceKind kind
std::array< std::atomic< Key >, MAX_SERVICES+MAX_DISTANCE > mServicesKey
void unlock(Salt salt) const O2_DPL_RELEASE(mMutex)
static constexpr Salt GLOBAL_CONTEXT_SALT
std::vector< ServiceSpec > mSpecs
static Salt streamSalt(short streamId, short dataProcessorId)
std::array< void *, MAX_SERVICES+MAX_DISTANCE > mServicesValue
void registerService(C const *service, Salt salt=ServiceRegistry::globalDeviceSalt())
decltype(TypeIdHelpers::uniqueId< void >()) hash_type
static constexpr int32_t MAX_DISTANCE
The maximum distance a entry can be from the optimal slot.
static constexpr int32_t valueFromSalt(Salt salt)
void lateBindStreamServices(DeviceState &state, fair::mq::ProgOptions &options, ServiceRegistry::Salt salt)
void declareService(ServiceSpec const &spec, DeviceState &state, fair::mq::ProgOptions &options, ServiceRegistry::Salt salt=ServiceRegistry::globalDeviceSalt())
void registerService(ServiceHandle handle, Salt salt=ServiceRegistry::globalDeviceSalt())
Register a service given an handle.
static Salt globalStreamSalt(short streamId)
void registerService(ServiceTypeHash typeHash, void *service, ServiceKind kind, Salt salt, char const *name=nullptr, ServiceRegistry::SpecIndex specIndex=SpecIndex{-1}) const
int getPos(ServiceTypeHash typeHash, Salt salt) const
ServiceRegistry & operator=(ServiceRegistry const &other)
void preExitCallbacks()
Invoke callbacks on exit.
bool active(Salt salt) const
Check if service of type T is currently active.
constexpr Index indexFromInstance(InstanceId id) const
static constexpr uint32_t MAX_SERVICES
The number of slots in the hashmap.
void bindService(ServiceRegistry::Salt salt, ServiceSpec const &spec, void *service) const
static constexpr uint64_t valueFromKey(Key key)
void throwError(const char *name, int64_t hash, int64_t streamId, int64_t dataprocessorId) const
To hide exception throwing from QC.
std::array< Meta, MAX_SERVICES+MAX_DISTANCE > mServicesMeta
void * get(ServiceTypeHash typeHash, Salt salt, ServiceKind kind, char const *name=nullptr) const
std::vector< ServicePostRenderGUIHandle > mPostRenderGUIHandles
Callbacks to be executed after the main GUI has been drawn.
void lock(Salt salt) const O2_DPL_ACQUIRE(mMutex)
std::array< std::atomic< bool >, MAX_SERVICES+MAX_DISTANCE > mServicesBooked
static constexpr uint32_t MAX_SERVICES_MASK
The mask to use to calculate the initial slot id.
constexpr InstanceId instanceFromTypeSalt(ServiceTypeHash type, Salt salt) const
void registerService(C *service, Salt salt=ServiceRegistry::globalDeviceSalt())
static Salt dataProcessorSalt(short)
VectorOfTObjectPtrs other