Belle II Software light-2607-kasei
DataStoreStreamer.cc
1/**************************************************************************
2 * basf2 (Belle II Analysis Software Framework) *
3 * Author: The Belle II Collaboration *
4 * *
5 * See git log for contributors and copyright holders. *
6 * This file is licensed under LGPL-3.0, see LICENSE.md. *
7 **************************************************************************/
8
9#include <framework/pcore/DataStoreStreamer.h>
10#include <framework/pcore/MsgHandler.h>
11#include <framework/pcore/Mergeable.h>
12
13#include <framework/datastore/DataStore.h>
14#include <framework/logging/Logger.h>
15#include <framework/datastore/StoreObjPtr.h>
16#include <framework/dataobjects/EventMetaData.h>
17
18#include <TClonesArray.h>
19#include <TClass.h>
20#include <TStreamerInfo.h>
21#include <TList.h>
22
23#include <unistd.h>
24#include <cstdio> // for NULL, printf
25
26#include <algorithm>
27#include <queue>
28
29using namespace Belle2;
30
31namespace {
32// Thread related
33 static DataStoreStreamer* s_streamer = nullptr;
34
35 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
36 static pthread_mutex_t mutex_thread[DataStoreStreamer::c_maxThreads];
37//static char* evtbuf_thread[DataStoreStreamer::c_maxThreads];
38 static int msg_compLevel[DataStoreStreamer::c_maxThreads];
39
40 static std::queue<char*> my_evtbuf[DataStoreStreamer::c_maxThreads];
41
42 static std::queue<int> my_nobjs;
43 static std::queue<int> my_narrays;
44 static std::queue<std::vector<TObject*>> my_objlist;
45 static std::queue<std::vector<std::string>> my_namelist;
46
47 static int my_decstat[DataStoreStreamer::c_maxThreads];
48};
49
50// the signature is imposed by pthread_create()
51// cppcheck-suppress constParameterCallback
52void* RunDecodeEvtMessage(void* targ)
53{
54 const auto* id = static_cast<const int*>(targ);
55 // DataStoreStreamer::Instance().decodeEvtMessage(*id);
56 s_streamer->decodeEvtMessage(*id);
57 return nullptr;
58}
59
60DataStoreStreamer::DataStoreStreamer(int complevel, bool handleMergeable, int maxthread):
61 m_compressionLevel(complevel),
62 m_handleMergeable(handleMergeable),
63 m_initStatus(0),
64 m_maxthread(maxthread),
65 m_threadin(0)
66 //, m_threadout(0)
67{
68 if ((unsigned int)m_maxthread > c_maxThreads) {
69 B2FATAL("DataStoreStreamer : Too many threads " << m_maxthread);
71 }
73
74 if (m_maxthread > 0) {
75 // Run decoder threads as sustainable detached threads
76 pthread_attr_t thread_attr;
77 pthread_attr_init(&thread_attr);
78 // pthread_attr_setschedpolicy(&thread_attr , SCHED_FIFO);
79 // pthread_attr_setdetachstate(&thread_attr , PTHREAD_CREATE_DETACHED);
80 for (int i = 0; i < m_maxthread; i++) {
81 my_decstat[i] = 0;
82 m_pt[i] = (pthread_t)0;
83 m_id[i] = i;
84 // m_pmsghandler[i] = new MsgHandler ( m_compressionLevel );
85 mutex_thread[i] = PTHREAD_MUTEX_INITIALIZER;
86 msg_compLevel[i] = m_compressionLevel;
87 }
88 for (int i = 0; i < m_maxthread; i++) {
89 // args.evtbuf = m_evtbuf[i];
90 pthread_create(&m_pt[i], nullptr, RunDecodeEvtMessage, static_cast<void*>(&m_id[i]));
91 }
92 pthread_attr_destroy(&thread_attr);
93 }
94 s_streamer = this;
95}
96
97// Destructor
102
103void DataStoreStreamer::setStreamingObjects(const std::vector<std::string>& objlist)
104{
105 m_streamobjnames = objlist;
106}
107
108bool DataStoreStreamer::isMergeable(const TObject* object)
109{
110 return object->InheritsFrom(Mergeable::Class());
111}
112
114{
115 static_cast<Mergeable*>(object)->clear();
116}
117void DataStoreStreamer::mergeIntoExisting(TObject* existing, const TObject* received)
118{
119 auto* existingObject = static_cast<Mergeable*>(existing);
120 existingObject->merge(static_cast<const Mergeable*>(received));
121}
123{
125 for (auto& entryPair : map) {
126 const DataStore::StoreEntry& entry = entryPair.second;
127 if (isMergeable(entry.object)) {
128 static_cast<Mergeable*>(entry.object)->removeSideEffects();
129 static_cast<Mergeable*>(entry.object)->clear();
130 }
131 }
132}
133
134
135// Stream DataStore
136EvtMessage* DataStoreStreamer::streamDataStore(bool addPersistentDurability, bool streamTransientObjects)
137{
138 // Clear Message Handler
139 m_msghandler->clear();
140
141 // Stream objects (for all included durabilities)
142 int narrays = 0;
143 int nobjs = 0;
145 while (true) {
146 auto& map = DataStore::Instance().getStoreEntryMap(durability);
147 for (auto &[name, entry] : map) {
148 //skip transient objects/arrays?
149 if (!streamTransientObjects and entry.dontWriteOut)
150 continue;
151
152 //skip objects not in the list
153 if (!m_streamobjnames.empty()) {
154 auto pos = std::find(m_streamobjnames.begin(), m_streamobjnames.end(), name);
155 if (pos == m_streamobjnames.end()) continue;
156 }
157
158 //verify that bits are unused
159 if (entry.object->TestBit(c_IsTransient)) {
160 B2FATAL("DataStoreStreamer::c_IsTransient bit is set for " << name << "!");
161 }
162 if (entry.object->TestBit(c_IsNull)) {
163 B2FATAL("DataStoreStreamer::c_IsNull bit is set for " << name << "!");
164 }
165 if (entry.object->TestBit(c_PersistentDurability)) {
166 B2FATAL("DataStoreStreamer::c_PersistentDurability bit is set for " << name << "!");
167 }
168 //verify TObject bits are serialised
169 if (entry.object->IsA()->CanIgnoreTObjectStreamer()) {
170 B2FATAL("TObject streamers disabled for " << name << "!");
171 }
172 //store some information in TObject bits to ensure consistent state even if entry.ptr is NULL
173 entry.object->SetBit(c_IsTransient, entry.dontWriteOut);
174 entry.object->SetBit(c_IsNull, (entry.ptr == nullptr));
175 entry.object->SetBit(c_PersistentDurability, (durability == DataStore::c_Persistent));
176 m_msghandler->add(entry.object, name);
177 B2DEBUG(100, "adding item " << name);
178
179 if (entry.isArray)
180 narrays++;
181 else
182 nobjs++;
183
184 //reset bits (are checked to be false when streaming the object)
185 entry.object->SetBit(c_IsTransient, false);
186 entry.object->SetBit(c_IsNull, false);
187 entry.object->SetBit(c_PersistentDurability, false);
188
189 bool merge = m_handleMergeable and entry.ptr != nullptr and isMergeable(entry.object);
190 if (merge)
191 clearMergeable(entry.object);
192 }
193
194 if (addPersistentDurability and durability == DataStore::c_Event)
195 durability = DataStore::c_Persistent;
196 else
197 break;
198 }
199
200 // Encode EvtMessage
201 EvtMessage* msg = m_msghandler->encode_msg(MSG_EVENT);
202 (msg->header())->nObjects = nobjs;
203 (msg->header())->nArrays = narrays;
204
205 return msg;
206}
207
208// Restore DataStore
210{
211 if (msg->type() == MSG_TERMINATE) {
212 B2INFO("Got termination message. Exiting...");
213 //msg doesn't really contain data, set EventMetaData to something equivalent
214 StoreObjPtr<EventMetaData> eventMetaData;
215 if (m_initStatus == 0 && DataStore::Instance().getInitializeActive())
216 eventMetaData.registerInDataStore();
217 eventMetaData.create();
218 eventMetaData->setEndOfData();
219 } else {
220 // Clear Message Handler
221 m_msghandler->clear();
222
223 // List of objects to be restored
224 std::vector<TObject*> objlist;
225 std::vector<std::string> namelist;
226
227 // Decode EvtMessage
228 m_msghandler->decode_msg(msg, objlist, namelist);
229 int nobjs = (msg->header())->nObjects;
230 int narrays = (msg->header())->nArrays;
231 if (unsigned(nobjs + narrays) != objlist.size())
232 B2WARNING("restoreDataStore(): inconsistent #objects/#arrays in header");
233
234 // Restore objects in DataStore
235 for (int i = 0; i < nobjs + narrays; i++) {
236 TObject* obj = objlist.at(i);
237 bool array = (dynamic_cast<TClonesArray*>(obj) != nullptr);
238 if (obj != nullptr) {
239
240 // Read and Build StreamerInfo
241 if (msg->type() == MSG_STREAMERINFO) {
242 restoreStreamerInfos(static_cast<TList*>(obj));
243 return 0;
244 }
245
246 bool isPersistent = obj->TestBit(c_PersistentDurability);
247 DataStore::EDurability durability = isPersistent ? (DataStore::c_Persistent) : (DataStore::c_Event);
248 TClass* cl = obj->IsA();
249 if (array)
250 cl = static_cast<TClonesArray*>(obj)->GetClass();
251 if (m_initStatus == 0 && DataStore::Instance().getInitializeActive()) { //are we called by the module's initialize() function?
253 DataStore::Instance().registerEntry(namelist.at(i), durability, cl, array, flags);
254 }
255 DataStore::StoreEntry* entry = DataStore::Instance().getEntry(StoreAccessorBase(namelist.at(i), durability, cl, array));
256 B2ASSERT("Can not find a data store entry with the name " << namelist.at(i) << ". Did you maybe forget to register it?", entry);
257 //only restore object if it is valid for current event
258 bool ptrIsNULL = obj->TestBit(c_IsNull);
259 if (!ptrIsNULL) {
260 bool merge = m_handleMergeable and !array and entry->ptr != nullptr and isMergeable(obj);
261 if (merge) {
262 B2DEBUG(100, "Will now merge " << namelist.at(i));
263
264 mergeIntoExisting(entry->ptr, obj);
265 delete obj;
266 } else {
267 //note: replace=true
269 StoreAccessorBase(namelist.at(i), durability, cl, array));
270
271 //reset bits of object in DataStore (are checked to be false when streaming the object)
272 obj->SetBit(c_IsTransient, false);
273 obj->SetBit(c_IsNull, false);
274 obj->SetBit(c_PersistentDurability, false);
275 }
276 // B2DEBUG(100, "restoreDS: " << (array ? "Array" : "Object") << ": " << namelist.at(i) << " stored");
277 } else {
278 //usually entry should already be invalidated, but e.g. for CrashHandler, it might not be.
279 if (entry->ptr)
280 entry->invalidate();
281 //not stored, clean up
282 delete obj;
283 }
284 } else {
285 //DataStore always has non-NULL content (whether they're available is a different matter)
286 B2ERROR("restoreDS: " << (array ? "Array" : "Object") << ": " << namelist.at(i) << " is NULL!");
287 }
288 }
289
290 }
291 // Return with normal exit status
292 if (m_initStatus == 0) m_initStatus = 1;
293 return 0;
294}
295
296// Parallel EvtMessage Destreamer implemented using thread
297
299{
300 // EOF case
301 if (evtbuf == nullptr) {
302 printf("queueEvtMessage : NULL evtbuf detected. \n");
303 for (int i = 0; i < m_maxthread; i++) {
304 while (my_evtbuf[i].size() >= c_maxQueueDepth) usleep(10);
305 my_evtbuf[m_threadin].push(evtbuf);
306 }
307 return 0;
308 }
309
310 // Put the event buffer in the queue of current thread
311 for (;;) {
312 if (my_evtbuf[m_threadin].size() < c_maxQueueDepth) {
313 pthread_mutex_lock(&mutex_thread[m_threadin]);
314 my_evtbuf[m_threadin].push(evtbuf);
315 pthread_mutex_unlock(&mutex_thread[m_threadin]);
316 break;
317 }
318 usleep(20);
319 }
320
321 // Switch to next thread
322 my_decstat[m_threadin] = 1; // Event queued for decoding
323 m_threadin++;
325 return 1;
326}
327
329{
330 printf("decodeEvtMessge : started. Thread ID = %d\n", id);
331 // Clear Message Handler
332 // m_msghandler->clear();
333 // MsgHandler* msghandler = new MsgHandler(m_compressionLevel);
334 // MsgHandler* msghandler = m_pmsghandler[id];
335
336 pthread_mutex_lock(&mutex); // Lock thread
337 MsgHandler msghandler(msg_compLevel[id]);
338 pthread_mutex_unlock(&mutex); // Unlock thread
339
340 for (;;) {
341 // Clear message handler event by event
342 // MsgHandler msghandler(m_compressionLevel);
343 // m_pmsghandler[id]->clear();
344 // Wait for event in queue becomes ready
345 msghandler.clear();
346
347 while (my_evtbuf[id].empty()) usleep(10);
348
349 // Pick up event buffer
350 pthread_mutex_lock(&mutex_thread[id]);
351 int nqueue = my_evtbuf[id].size();
352 if (nqueue <= 0) printf("!!!!! Nqueue = %d\n", nqueue);
353 char* evtbuf = my_evtbuf[id].front(); my_evtbuf[id].pop();
354 pthread_mutex_unlock(&mutex_thread[id]);
355
356 // In case of EOF
357 if (evtbuf == nullptr) {
358 printf("decodeEvtMessage: NULL evtbuf detected, nq = %d\n", nqueue);
359 my_nobjs.push(-1);
360 return nullptr;
361 }
362
363 // Construct EvtMessage
364 auto* msg = new EvtMessage(evtbuf);
365
366 // Decode EvtMessage into Objects
367 std::vector<TObject*> objlist;
368 std::vector<std::string> namelist;
369
370 // pthread_mutex_lock(&mutex); // Lock test
371 msghandler.decode_msg(msg, objlist, namelist);
372 // pthread_mutex_unlock(&mutex); // Unlock test
373
374
375 // Queue them for the registration in DataStore
376 while (my_nobjs.size() >= c_maxQueueDepth) usleep(10);
377 pthread_mutex_lock(&mutex); // Lock queueing
378 my_objlist.push(objlist);
379 my_namelist.push(namelist);
380 my_nobjs.push((msg->header())->nObjects);
381 my_narrays.push((msg->header())->nArrays);
382 pthread_mutex_unlock(&mutex); // Unlock queueing
383
384 // Release EvtMessage
385 delete msg;
386 delete[] evtbuf;
387
388 // Preparation for next event
389 my_decstat[id] = 0; // Ready to read next event
390
391 }
392
393 return nullptr;
394}
395
397{
398// Wait for the queue to become ready
399 while (my_nobjs.empty()) usleep(10);
400
401 // Register decoded objects in DataStore
402
403 // Pick up event on the top and remove it from the queue
404 pthread_mutex_lock(&mutex);
405 int nobjs = my_nobjs.front(); my_nobjs.pop();
406 if (nobjs == -1) {
407 printf("restoreDataStore: EOF detected. exiting with status 0\n");
408 pthread_mutex_unlock(&mutex);
409 return 0;
410 }
411 int narrays = my_narrays.front(); my_narrays.pop();
412 std::vector<TObject*> objlist = my_objlist.front(); my_objlist.pop();
413 std::vector<std::string> namelist = my_namelist.front(); my_namelist.pop();
414 pthread_mutex_unlock(&mutex);
415
416 // Restore objects in DataStore
417 //TODO refactor, just copied & pasted right now
418 for (int i = 0; i < nobjs + narrays; i++) {
419 // printf ( "restoring object %d = %s\n", i, namelist.at(i).c_str() );
420 bool array = (dynamic_cast<TClonesArray*>(objlist.at(i)) != nullptr);
421 if (objlist.at(i) != nullptr) {
422 TObject* obj = objlist.at(i);
423 bool isPersistent = obj->TestBit(c_PersistentDurability);
424 DataStore::EDurability durability = isPersistent ? (DataStore::c_Persistent) : (DataStore::c_Event);
425 TClass* cl = obj->IsA();
426 if (array)
427 cl = static_cast<TClonesArray*>(obj)->GetClass();
428 if (m_initStatus == 0 && DataStore::Instance().getInitializeActive()) { //are we called by the module's initialize() function?
430 DataStore::Instance().registerEntry(namelist.at(i), durability, cl, array, flags);
431 }
432 //only restore object if it is valid for current event
433 bool ptrIsNULL = obj->TestBit(c_IsNull);
434 if (!ptrIsNULL) {
436 StoreAccessorBase(namelist.at(i), durability, cl, array));
437 B2DEBUG(100, "restoreDS: " << (array ? "Array" : "Object") << ": " << namelist.at(i) << " stored");
438
439 //reset bits of object in DataStore (are checked to be false when streaming the object)
440 obj->SetBit(c_IsTransient, false);
441 obj->SetBit(c_IsNull, false);
442 obj->SetBit(c_PersistentDurability, false);
443 } else {
444 //not stored, clean up
445 delete obj;
446 }
447 } else {
448 //DataStore always has non-NULL content (whether they're available is a different matter)
449 B2ERROR("restoreDS: " << (array ? "Array" : "Object") << ": " << namelist.at(i) << " is NULL!");
450 }
451 }
452
453 // printf ( "Objects restored in DataStore\n" );
454
455 return 1;
456}
457
459{
460 m_maxthread = maxthread;
461}
462
467
469{
470 // printf ( "Decode thread %d = %d\n", m_threadin, m_done_decode[m_threadin] );
471 return (my_decstat[m_threadin]);
472}
473
475{
476 // printf ( "Decode thread %d = %d\n", m_threadin, m_done_decode[m_threadin] );
478}
479
480
482{
483 //
484 // Copy from TSocket::RecvStreamerInfos()
485 //
486
487 // TList *list = (TList*)mess->ReadObject(TList::Class());
488 TStreamerInfo* info;
489 TObjLink* lnk = list->FirstLink();
490
491 std::vector<std::string> class_name;
492
493 // First call BuildCheck for regular class
494 while (lnk) {
495 info = static_cast<TStreamerInfo*>(lnk->GetObject());
496
497 int ovlap = 0;
498 for (const auto& itr : class_name) {
499 if (strcmp(itr.c_str(), info->GetName()) == 0) {
500 ovlap = 1;
501 B2DEBUG(100, "Regular Class Loop : The class " << info->GetName() << " has already appeared. Skipping...");
502 break;
503 }
504 }
505
506 // If the same class is in the object, ignore it. ( Otherwise it causes error. )
507 if (ovlap == 0) {
508 std::string temp_classname = info->GetName();
509 class_name.push_back(temp_classname);
510
511 TObject* element = info->GetElements()->UncheckedAt(0);
512 Bool_t isstl = element && strcmp("This", element->GetName()) == 0;
513 if (!isstl) {
514 info->BuildCheck();
515 // if (gDebug > 0)
516 B2INFO("importing TStreamerInfo: " << info->GetName() <<
517 " version = " << info->GetClassVersion());
518 }
519 }
520 lnk = lnk->Next();
521 }
522
523
524 class_name.clear();
525 // Then call BuildCheck for stl class
526 lnk = list->FirstLink();
527 while (lnk) {
528 info = static_cast<TStreamerInfo*>(lnk->GetObject());
529
530 int ovlap = 0;
531 for (const auto& itr : class_name) {
532 if (strcmp(itr.c_str(), info->GetName()) == 0) {
533 ovlap = 1;
534 B2DEBUG(100, "STL Class Loop : The class " << info->GetName() << " has already appeared. Skipping...");
535 break;
536 }
537 }
538
539 // If the same class is in the object, ignore it. ( Otherwise it causes error. )
540 if (ovlap == 0) {
541 std::string temp_classname = info->GetName();
542 class_name.push_back(temp_classname);
543
544 TObject* element = info->GetElements()->UncheckedAt(0);
545 Bool_t isstl = element && strcmp("This", element->GetName()) == 0;
546 if (isstl) {
547 info->BuildCheck();
548 // if (gDebug > 0)
549 B2INFO("STL importing TStreamerInfo: " << info->GetName() <<
550 " version = " << info->GetClassVersion());
551 }
552 }
553 lnk = lnk->Next();
554 }
555
556 return 0;
557}
Stream/restore DataStore objects to/from EvtMessage.
static const unsigned int c_maxQueueDepth
Ask Itoh-san.
static void removeSideEffects()
call clear() and removeSideEffects() for all Mergeable objects in datastore (for c_Persistent durabil...
int getMaxThreads()
maximum number of threads.
static int restoreStreamerInfos(const TList *list)
restore StreamerInfo from data in a file
pthread_t m_pt[c_maxThreads]
thread pointer
int queueEvtMessage(char *msg)
Queue EvtMessage for destreaming.
static void * decodeEvtMessage(int id)
Decode EvtMessage and store objects in temporary buffer.
int restoreDataStoreAsync()
Restore objects in DataStore from temporary buffer.
std::vector< std::string > m_streamobjnames
names of object to be streamed
EvtMessage * streamDataStore(bool addPersistentDurability, bool streamTransientObjects=false)
Store DataStore objects in EvtMessage.
static bool isMergeable(const TObject *object)
Is the given object of a type that can be merged?
void setStreamingObjects(const std::vector< std::string > &list)
Set names of objects to be streamed/destreamed.
int m_threadin
current thread?
@ c_PersistentDurability
Object is of persistent durability.
@ c_IsNull
object is not valid for current event, set StoreEntry::ptr to NULL.
@ c_IsTransient
The corresponding StoreEntry has flag c_DontWriteOut.
static const unsigned int c_maxThreads
global maximum number of threads (cannot set higher number).
static void mergeIntoExisting(TObject *existing, const TObject *received)
Assuming both objects are mergeable, merge 'received' into 'existing'.
MsgHandler * m_msghandler
MsgHandler.
int m_decoderStatus[c_maxThreads]
thread decoder status.
int m_initStatus
first event flag.
int m_id[c_maxThreads]
thread index.
bool m_handleMergeable
Whether to handle Mergeable objects.
void setMaxThreads(int)
maximum number of threads.
void setDecoderStatus(int)
Ask Itoh-san about this.
int m_compressionLevel
Compression level in streaming.
int getDecoderStatus()
Ask Itoh-san about this.
static void clearMergeable(TObject *object)
assuming object is mergeable, clear its contents.
int restoreDataStore(EvtMessage *msg)
Restore DataStore objects from EvtMessage.
DataStoreStreamer(int complevel=0, bool handleMergeable=true, int maxthread=0)
Constructor.
@ c_WriteOut
Object/array should be saved by output modules.
Definition DataStore.h:70
@ c_DontWriteOut
Object/array should be NOT saved by output modules.
Definition DataStore.h:71
StoreEntryMap & getStoreEntryMap(EDurability durability)
Get a reference to the object/array map.
Definition DataStore.h:325
EDurability
Durability types.
Definition DataStore.h:58
@ c_Persistent
Object is available during entire execution time.
Definition DataStore.h:60
@ c_Event
Different object in each event, all objects/arrays are invalidated after event() function has been ca...
Definition DataStore.h:59
Belle2::StoreEntry StoreEntry
Wraps a stored array/object, stored under unique (name, durability) key.
Definition DataStore.h:84
static DataStore & Instance()
Instance of singleton Store.
Definition DataStore.cc:53
StoreEntry * getEntry(const StoreAccessorBase &accessor)
Check whether an entry with the correct type is registered in the DataStore map and return it.
Definition DataStore.cc:291
bool createObject(TObject *object, bool replace, const StoreAccessorBase &accessor)
Create a new object/array in the DataStore or add an existing one.
Definition DataStore.cc:314
bool registerEntry(const std::string &name, EDurability durability, TClass *objClass, bool array, EStoreFlags storeFlags)
Register an entry in the DataStore map.
Definition DataStore.cc:187
std::map< std::string, StoreEntry > StoreEntryMap
Map for StoreEntries.
Definition DataStore.h:87
Class to manage streamed object.
Definition EvtMessage.h:59
ERecordType type() const
Get record type.
EvtHeader * header()
Get pointer to EvtHeader.
Abstract base class for objects that can be merged.
Definition Mergeable.h:31
virtual void merge(const Mergeable *other)=0
Merge object 'other' into this one.
A class to encode/decode an EvtMessage.
Definition MsgHandler.h:103
virtual void decode_msg(EvtMessage *msg, std::vector< TObject * > &objlist, std::vector< std::string > &namelist)
Decode an EvtMessage into a vector list of objects with names.
virtual void clear()
Clear object list.
Definition MsgHandler.cc:40
Base class for StoreObjPtr and StoreArray for easier common treatment.
bool registerInDataStore(DataStore::EStoreFlags storeFlags=DataStore::c_WriteOut)
Register the object/array in the DataStore.
bool create(bool replace=false)
Create a default object in the data store.
Type-safe access to single objects in the data store.
Definition StoreObjPtr.h:96
Abstract base class for different kinds of events.
TObject * ptr
The pointer to the returned object, either equal to 'object' or null, depending on whether the object...
Definition StoreEntry.h:51
TObject * object
The pointer to the actual object.
Definition StoreEntry.h:48
void invalidate()
invalidate entry for next event.
Definition StoreEntry.cc:77