// $Id$ /************************************************************ * The Data Acquisition Backbone Core (DABC) * ************************************************************ * Copyright (C) 2009 - * * GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * * Planckstr. 1, 64291 Darmstadt, Germany * * Contact: http://dabc.gsi.de * ************************************************************ * This software can be used under the GPL license * * agreements as stated in LICENSE.txt file * * which is part of the distribution. * ************************************************************/ #include "dabc/Object.h" #include #include #include "dabc/Manager.h" #include "dabc/logging.h" #include "dabc/threads.h" #include "dabc/defines.h" #include "dabc/ConfigBase.h" #include "dabc/Worker.h" #include "dabc/Thread.h" #include "dabc/ReferencesVector.h" #include "dabc/Worker.h" #include "dabc/Thread.h" unsigned dabc::Object::gNumInstances = 0; unsigned dabc::Object::gNumCreated = 0; namespace dabc { const char* xmlDeviceNode = "Device"; const char* xmlThreadNode = "Thread"; const char* xmlMemoryPoolNode = "MemoryPool"; const char* xmlModuleNode = "Module"; const char* xmlConnectionNode = "Connection"; const char* xmlQueueAttr = "queue"; const char* xmlBindAttr = "bind"; const char* xmlSignalAttr = "signal"; const char* xmlRateAttr = "rate"; const char* xmlLoopAttr = "loop"; const char* xmlInputQueueSize = "InputQueueSize"; const char* xmlOutputQueueSize = "OutputQueueSize"; const char* xmlInlineDataSize = "InlineDataSize"; const char* xmlPoolName = "PoolName"; const char* xmlWorkPool = "Pool"; const char* xmlFixedLayout = "FixedLayout"; const char* xmlCleanupTimeout = "CleanupTimeout"; const char* xmlBufferSize = "BufferSize"; const char* xmlNumBuffers = "NumBuffers"; const char* xmlRefCoeff = "RefCoeff"; const char* xmlNumSegments = "NumSegments"; const char* xmlAlignment = "Alignment"; const char* xmlShowInfo = "ShowInfo"; const char* xmlNumInputs = "NumInputs"; const char* xmlNumOutputs = "NumOutputs"; const char* xmlInputPrefix = "Input"; const char* xmlInputMask = "Input%u"; const char* xmlOutputPrefix = "Output"; const char* xmlOutputMask = "Output%u"; const char* xmlUseAcknowledge = "UseAcknowledge"; const char* xmlFlushTimeout = "FlushTimeout"; const char* xmlConnTimeout = "ConnTimeout"; const char* xmlTrueValue = "true"; const char* xmlFalseValue = "false"; const char* xmlMcastAddr = "McastAddr"; const char* xmlMcastPort = "McastPort"; const char* xmlMcastRecv = "McastRecv"; const char* xmlProtocol = "Protocol"; const char* xmlHostName = "HostName"; const char* xmlFileName = "FileName"; const char* xmlFileNumber = "FileNumber"; const char* xmlFileSizeLimit = "FileSizeLimit"; const char* xml_maxsize = "maxsize"; const char* xml_number = "number"; const char* typeThread = "dabc::Thread"; const char* typeDevice = "dabc::Device"; const char* typeSocketDevice = "dabc::SocketDevice"; const char* typeSocketThread = "dabc::SocketThread"; const char* typeApplication = "dabc::Application"; #ifdef DABC_EXTRA_CHECKS PointersVector gObjectGarbageCollector; Mutex gObjectGarbageMutex; #endif } /** Resolve problem with child-parent mutex locking. Now this completely avoidable. * Parent pointer was and is safe - as long object is exists parent reference is preserved, * only in destructor parent will be released. Child reference can disappear in any time, * therefore it was difficult to get reference on child while only pointer is not enough - * somebody else can remove it very fast. Therefore fObjectBlock counter is introduced. * As long it is non-zero no any removal of childs are allowed. As result, reference on * all childs are preserved. One can release parent lock, acquire reference on child * (which includes child mutex locking) and after that again parent lock and decrement of block counter. * This only limits situation during removal of childs - it all correspondent methods one should respect block counter. */ dabc::Object::Object(const std::string& name) : fObjectFlags(0), fObjectParent(), fObjectName(name), fObjectMutex(0), fObjectRefCnt(0), fObjectChilds(0), fObjectBlock(0) { DOUT3("Created object %s %p", GetName(), this); SetFlag(flIsOwner, true); Constructor(); } dabc::Object::Object(Reference parent, const std::string& name) : fObjectFlags(0), fObjectParent(parent), fObjectName(name), fObjectMutex(0), fObjectRefCnt(0), fObjectChilds(0), fObjectBlock(0) { DOUT3("Object created %s %p", GetName(), this); SetFlag(flIsOwner, true); Constructor(); } dabc::Object::Object(const ConstructorPair& pair) : fObjectFlags(0), fObjectParent(pair.parent), fObjectName(pair.name), fObjectMutex(0), fObjectRefCnt(0), fObjectChilds(0), fObjectBlock(0) { DOUT3("Object created %s %p", GetName(), this); SetFlag(flIsOwner, true); Constructor(); } dabc::Object::~Object() { Destructor(); DOUT3("Object destroyed %s %p", GetName(), this); } void dabc::Object::SetOwner(bool on) { LockGuard lock(fObjectMutex); SetFlag(flIsOwner, on); } bool dabc::Object::IsOwner() const { LockGuard lock(fObjectMutex); return GetFlag(flIsOwner); } bool dabc::Object::IsLogging() const { LockGuard lock(fObjectMutex); return GetFlag(flLogging); } void dabc::Object::SetLogging(bool on) { LockGuard lock(fObjectMutex); return SetFlag(flLogging, on); } bool dabc::Object::IsName(const char* str, int len) const { if ((len==0) || (str==0)) return false; if (len<0) return fObjectName.compare(str) == 0; return ((int) fObjectName.length()==len) && (fObjectName.compare(0, len, str,len) == 0); } void dabc::Object::Constructor() { gNumInstances++; // use non-recursive mutexes to see faster deadlock problems // FIXME: should we use recursive in final version? fObjectMutex = new Mutex(false); SetState(stNormal); fObjectParent.AddChild(this); } void dabc::Object::Destructor() { #ifdef DABC_EXTRA_CHECKS { LockGuard lock(gObjectGarbageMutex); gObjectGarbageCollector.remove(this); } #endif Mutex* m = 0; ReferencesVector* chlds(0); { LockGuard lock(fObjectMutex); if ((GetState() != stDestructor) && (fObjectRefCnt!=0)) { EOUT("Object %p %s deleted not via Destroy method refcounter %u", this, GetName(), fObjectRefCnt); } SetState(stDestructor); if (fObjectRefCnt!=0) { EOUT("!!!!!!!!!!!! Destructor called when refcounter %u obj:%s %p", fObjectRefCnt, GetName(), this); // Object* obj = (Object*) 29387898; // delete obj; } if (fObjectChilds!=0) { EOUT("!!!!!!! CHILDS %u ARE NOT DELETED completely obj:%s %p", fObjectChilds->GetSize(), GetName(), this); if (fObjectBlock > 0) EOUT("!!!! CHILDS ARE STILL BLOCKED %d!!!!!!!", fObjectBlock); chlds = fObjectChilds; fObjectChilds = 0; } m = fObjectMutex; fObjectMutex = 0; } // from this point on children are not seen to outer world, // one can destroy them one after another if (chlds!=0) { chlds->Clear(); delete chlds; } // release reference on parent, remove from child should be already done fObjectParent.Release(); // destroy mutex delete m; m =0; gNumInstances--; } bool dabc::Object::IncReference(bool withmutex) { if (withmutex) { dabc::LockGuard lock(fObjectMutex); if (GetState() == stDestructor) { EOUT("OBJ:%p %s %s Inc reference during destructor", this, GetName(), ClassName()); return false; // EOUT("Obj:%p %s Class:%s IncReference %u inside destructor :(", // this, GetName(), ClassName(), fObjectRefCnt); } fObjectRefCnt++; if (GetFlag(flLogging)) DOUT0("Obj:%s %p Class:%s IncReference +----- %u thrd:%s", GetName(), this, ClassName(), fObjectRefCnt, dabc::mgr.CurrentThread().GetName()); return true; } if (fObjectMutex==0) return false; if (!fObjectMutex->IsLocked()) { EOUT("Obj:%p %s Class:%s IncReference mutex is not lock but declared so", this, GetName(), ClassName()); return false; } if (GetFlag(flLogging)) DOUT0("Obj:%s %p Class:%s IncReference -+---- %u", GetName(), this, ClassName(), fObjectRefCnt); fObjectRefCnt++; return true; } void dabc::Object::SetCleanupBit() { dabc::LockGuard lock(fObjectMutex); SetFlag(flCleanup, true); } bool dabc::Object::_IsNormalState() { return GetState() == stNormal; } bool dabc::Object::IsNormalState() { dabc::LockGuard lock(fObjectMutex); return _IsNormalState(); } bool dabc::Object::DecReference(bool ask_to_destroy, bool do_decrement, bool from_thread) { // check destroyment before we lock the mutex // if method returns true, object destructor should follow bool viathrd = false; { dabc::LockGuard lock(fObjectMutex); // if (GetFlag(flLogging)) { // DOUT0("Call DecReference for object %p %s refcnt = %d, ask_to_destroy %s do_decrement %s state %d from_thread %s", this, GetName(), fObjectRefCnt, DBOOL(ask_to_destroy), DBOOL(do_decrement), GetState(), DBOOL(from_thread)); // } // if (GetFlag(flLogging)) // DOUT0("Obj:%s %p Class:%s DecReference %u destroy %s dodec %s state %d numchilds %u flags %x", // GetName(), this, ClassName(), // fObjectRefCnt, DBOOL(ask_to_destroy), DBOOL(do_decrement), GetState(), // (fObjectChilds ? fObjectChilds->GetSize() : 0), fObjectFlags); if (do_decrement) { if (fObjectRefCnt==0) { EOUT("Reference counter is already 0"); throw dabc::Exception(ex_Object, "Reference counter is 0 - cannot decrease", GetName()); return false; } // if (IsLogging()) DOUT0("Dec object refcounter in 389"); fObjectRefCnt--; if (GetFlag(flLogging)) DOUT0("Obj:%s %p Class:%s DecReference ----+- %u thrd:%s", GetName(), this, ClassName(), fObjectRefCnt, dabc::mgr.CurrentThread().GetName()); } switch (GetState()) { case stConstructor: EOUT("Object %s is not yet constructed - most probably, big failure", GetName()); return false; case stNormal: // if autodestroy flag specified, object will be destroyed when no more references are existing if (GetFlag(flAutoDestroy) && (fObjectRefCnt==0)) ask_to_destroy = true; if (!ask_to_destroy) return false; viathrd = GetFlag(flHasThread); break; // we are waiting for the thread, only thread call can unblock case stWaitForThread: if (!from_thread) { DOUT2("Object %p %s tried to destroy not from thread - IGNORE", this, GetName()); return false; } break; // recursive call, ignore it case stDoingDestroy: return false; // if object already so far, it can be destroyed when no references remained case stWaitForDestructor: if ((fObjectRefCnt == 0) && _NoOtherReferences()) { if (_DoDeleteItself()) return false; // once return true, never do it again // returning true means that destructor will be immediately call SetState(stDestructor); return true; } #ifdef DABC_EXTRA_CHECKS { LockGuard lock(gObjectGarbageMutex); if (!gObjectGarbageCollector.has_ptr(this)) gObjectGarbageCollector.push_back(this); } #endif return false; // in principal error, we are inside destructor case stDestructor: return false; } if (viathrd) { SetState(stWaitForThread); // we delegate reference counter to the thread fObjectRefCnt++; if (GetFlag(flLogging)) DOUT0("Obj:%s %p Class:%s IncReference --+--- %u", GetName(), this, ClassName(), fObjectRefCnt); } else { SetState(stDoingDestroy); } } if (viathrd) { if (AskToDestroyByThread()) return false; // it means thread do not want to destroy us, let do it normal way dabc::LockGuard lock(fObjectMutex); SetState(stDoingDestroy); // thread reject destroyment, therefore refcounter can be decreased // if (IsLogging()) DOUT0("Dec object refcounter in 459"); fObjectRefCnt--; if (GetFlag(flLogging)) DOUT0("Obj:%s %p Class:%s DecReference -----+ %u", GetName(), this, ClassName(), fObjectRefCnt); } if (IsLogging()) DOUT0("Calling object %p cleanup from_thrd=%s do_decrement=%s", this, DBOOL(from_thread), DBOOL(do_decrement)); // Main point of all destroyment - call cleanup in correct place ObjectCleanup(); // if (IsLogging()) DOUT0("Did object cleanup"); if (IsLogging()) DOUT0("DecRefe after cleanup %p cleanup %s mgr %p", this, DBOOL(GetFlag(flCleanup)), dabc::mgr()); { LockGuard guard(fObjectMutex); if (GetFlag(flCleanup) && dabc::mgr()) { // cleanup will be done by manager via reference fObjectRefCnt++; SetState(stWaitForDestructor); if (GetFlag(flLogging)) DOUT0("Obj:%s %p Class:%s IncReference ---+-- %u", GetName(), this, ClassName(), fObjectRefCnt); } else if ((fObjectRefCnt==0) && _NoOtherReferences()) { // no need to deal with manager - can call destructor immediately DOUT3("Obj:%p can be destroyed", this); if (_DoDeleteItself()) { SetState(stWaitForDestructor); return false; } SetState(stDestructor); return true; } else { /** somebody else has reference, when it release reference object will be automatically destroyed */ SetState(stWaitForDestructor); #ifdef DABC_EXTRA_CHECKS { LockGuard lock(gObjectGarbageMutex); if (!gObjectGarbageCollector.has_ptr(this)) gObjectGarbageCollector.push_back(this); } #endif return false; } } // special case - we already increment reference counter Reference ref; ref.fObj = this; if (dabc::mgr()->DestroyObject(ref)) return false; // here reference will be released if manager does not accept destroyment LockGuard guard(fObjectMutex); if ((fObjectRefCnt==0) && _NoOtherReferences()) { // no need to deal with manager - can call destructor immediately if (_DoDeleteItself()) { SetState(stWaitForDestructor); return false; } DOUT3("Obj:%p can be destroyed", this); SetState(stDestructor); return true; } return false; } bool dabc::Object::CallDestroyFromThread() { return DecReference(true, true, true); } void dabc::Object::DeleteThis() { if (IsLogging()) DOUT1("OBJ:%p %s DELETETHIS cnt %u", this, GetName(), fObjectRefCnt); { LockGuard lock(fObjectMutex); if (GetState()!=stNormal) { // EOUT("Object %p already on the way to destroyment - do not harm it", this); return; } // this flag force object to be cleanup via manager SetFlag(flCleanup, true); } Destroy(this); } void dabc::Object::ObjectCleanup() { ReferencesVector* chlds = 0; int cnt = 1000000; while (--cnt>0) { dabc::LockGuard lock(fObjectMutex); if (GetState() != stDoingDestroy) { EOUT("obj:%p name:%s class:%s Something completely wrong - cleanup in wrong state %u ", this, GetName(), ClassName(), GetState()); } if (fObjectChilds == 0) break; if (fObjectBlock>0) continue; // we deleting childs immediately chlds = fObjectChilds; fObjectChilds = 0; break; } #ifdef DABC_EXTRA_CHECKS if (cnt < 999990) DOUT0("Object %s Retry %d time before childs were unblocked", 1000000-cnt); #endif if (cnt==0) { EOUT("HARD error!!!! - For a long time fObjectBlock=%d is blocked in object %p %s", fObjectBlock, this, GetName()); exit(055); } // first we delete all childs !!!! if (chlds!=0) { if (IsLogging()) DOUT0("Obj:%p %s Deleting childs %u", this, GetName(), chlds->GetSize()); delete chlds; if (IsLogging()) DOUT0("Obj:%p %s Deleting childs done", this, GetName()); } if (IsLogging()) { DOUT0("Obj:%p %s refcnt %u Before remove from parent %p", this, GetName(), fObjectRefCnt, fObjectParent()); DOUT0("++++++++++++++++ AT DELETING APP %p +++++++++++++", dabc::mgr.app().GetObject()); } // Than we remove reference on the object from parent if (fObjectParent()) fObjectParent()->RemoveChild(this); if (IsLogging()) { DOUT0("Obj:%p %s refcnt %u after remove from parent", this, GetName(), fObjectRefCnt); DOUT0("++++++++++++++++ AT DELETING APP %p +++++++++++++", dabc::mgr.app().GetObject()); } DOUT3("Obj:%s Class:%s Finish cleanup numrefs %u", GetName(), ClassName(), NumReferences()); } unsigned dabc::Object::NumReferences() { dabc::LockGuard lock(fObjectMutex); return fObjectRefCnt; } dabc::Reference dabc::Object::_MakeRef() { return dabc::Reference(false, this); } bool dabc::Object::AddChild(Object* child, bool withmutex, bool setparent) throw() { if (child==0) return false; if (child->GetParent() == 0) { if (setparent) child->fObjectParent.SetObject(this, false, withmutex); } else if (child->GetParent() != this) { EOUT("Cannot move child from other parent"); throw dabc::Exception(ex_Object, "Cannot move child from other parent", GetName()); return false; } Reference ref(child); LockGuard guard(withmutex ? fObjectMutex : 0); // one can set owner flag under mutex - reference itself does not uses mutexes ref.SetOwner(GetFlag(flIsOwner)); if (fObjectChilds==0) fObjectChilds = new ReferencesVector; fObjectChilds->Add(ref); return true; } bool dabc::Object::AddChildAt(Object* child, unsigned pos, bool withmutex, bool setparent) { if (child==0) return false; if (child->GetParent() == 0) { if (setparent) child->fObjectParent.SetObject(this, false, withmutex); } else if (child->GetParent() != this) { EOUT("Cannot move child from other parent"); throw dabc::Exception(ex_Object, "Cannot move child from other parent", GetName()); return false; } Reference ref(child); LockGuard guard(withmutex ? fObjectMutex : 0); // one can set owner flag under mutex - reference itself does not uses mutexes ref.SetOwner(GetFlag(flIsOwner)); if (fObjectChilds==0) fObjectChilds = new ReferencesVector; fObjectChilds->AddAt(ref, pos); return true; } void dabc::Object::RemoveChild(Object* child) throw() { if (child==0) return; int cnt = 1000000; while (--cnt>0) { LockGuard guard(fObjectMutex); if (fObjectChilds==0) return; if (fObjectBlock>0) continue; fObjectChilds->Remove(child); break; } #ifdef DABC_EXTRA_CHECKS if (cnt < 999990) DOUT0("Object %s Retry %d time before childs were unblocked", GetName(), 1000000-cnt); #endif if (cnt==0) { EOUT("HARD error!!!! - For a long time fObjectBlock=%d is blocked in object %p %s", fObjectBlock, this, GetName()); exit(055); } } unsigned dabc::Object::NumChilds() const { LockGuard guard(fObjectMutex); return fObjectChilds ? fObjectChilds->GetSize() : 0; } dabc::Object* dabc::Object::GetChild(unsigned n) const { LockGuard guard(fObjectMutex); return fObjectChilds==0 ? 0 : fObjectChilds->GetObject(n); } dabc::Reference dabc::Object::GetChildRef(unsigned n) const { LockGuard guard(fObjectMutex); Object* obj = (fObjectChilds==0) ? 0 : fObjectChilds->GetObject(n); if (obj==0) return Reference(); IntGuard block(fObjectBlock); UnlockGuard unlock(fObjectMutex); return dabc::Reference(obj); // here will be mutex locked, fObjectBlock counter decremented and finally mutex unlocked again } bool dabc::Object::GetAllChildRef(ReferencesVector* vect) const { if (vect==0) return false; LockGuard guard(fObjectMutex); if (fObjectChilds==0) return true; PointersVector ptrs; for (unsigned n=0; nGetSize(); n++) ptrs.push_back(fObjectChilds->GetObject(n)); IntGuard block(fObjectBlock); UnlockGuard unlock(fObjectMutex); for (unsigned n=0;nAdd(Reference((Object*)ptrs[n])); return true; // here will be mutex locked, fObjectBlock counter decremented and finally mutex unlocked again } dabc::Object* dabc::Object::FindChild(const char* name) const { return FindChildRef(name, false)(); } dabc::Reference dabc::Object::FindChildRef(const char* name, bool force) const throw() { Reference ref(const_cast (this)); return SearchForChild(ref, name, true, force); } dabc::Reference dabc::Object::SearchForChild(Reference& ref, const char* name, bool firsttime, bool force) throw() { if (ref.null()) return ref; if ((name==0) || (strlen(name)==0)) return ref; if (*name=='/') { if (firsttime) { while (ref()->GetParent()!=0) ref = Reference(ref()->GetParent()); return SearchForChild(ref, name+1, false, force); } else { // skip all slashes in the beginning while (*name=='/') name++; if (*name==0) return ref; } } int len = strlen(name); if ((len>=2) && (name[0]=='.') && (name[1] == '.')) { if (len==2) return Reference(ref()->GetParent()); if (name[2]=='/') { ref = Reference(ref()->GetParent()); return SearchForChild(ref, name + 3, false, force); } } if ((len>=1) && (name[0]=='.')) { if (len==1) return ref; if (name[1]=='/') return SearchForChild(ref, name + 2, false, force); } const char* ptok = name; while (*ptok!=0) { if (*ptok == '/') break; ptok++; } Reference newref; { LockGuard guard(ref()->fObjectMutex); dabc::Object* obj = (ref()->fObjectChilds==0) ? 0 : ref()->fObjectChilds->FindObject(name, ptok-name); // create new object under parent mutex - no one can create double entries simultaneously if ((obj==0) && force) { obj = new Object(0, std::string(name, ptok-name)); ref()->AddChild(obj, false); } if (obj!=0) { IntGuard block(ref()->fObjectBlock); UnlockGuard unlock(ref()->fObjectMutex); // make reference outside parent mutex newref = dabc::Reference(obj); } } ref = newref; if (*ptok==0) return ref; return SearchForChild(ref, ptok+1, false, force); } dabc::Reference dabc::Object::GetFolder(const char* name, bool force) throw() { Reference ref(this); return SearchForChild(ref, name, true, force); } dabc::Reference dabc::Object::PutChild(Object* obj, bool delduplicate) { Reference newref; if (obj==0) return newref; Object* oldobj = 0; { LockGuard guard(ObjectMutex()); if (fObjectChilds) oldobj = fObjectChilds->FindObject(obj->GetName()); // if object was not exists, add child create new object under parent mutex - no one can create double entries simultaneously if (oldobj==0) { oldobj = obj; AddChild(obj, false); } IntGuard block(fObjectBlock); UnlockGuard unlock(fObjectMutex); // make reference outside parent mutex newref << dabc::Reference(oldobj); } // instance will be automatically deleted when object with givven name already exists if ((oldobj!=obj) && delduplicate) dabc::Object::Destroy(obj); return newref; } void dabc::Object::DeleteChilds(const std::string& exclude_mask) { ReferencesVector del_vect; int cnt = 1000000; while (--cnt>0) { LockGuard guard(fObjectMutex); // nothing to do if (fObjectChilds==0) return; if (fObjectBlock>0) continue; unsigned n = fObjectChilds->GetSize(); while (n-->0) { Object* obj = fObjectChilds->GetObject(n); if (!exclude_mask.empty() && obj->IsNameMatch(exclude_mask)) continue; del_vect.Add(fObjectChilds->TakeRef(n)); } if (fObjectChilds->GetSize()==0) { delete fObjectChilds; fObjectChilds = 0; } break; } #ifdef DABC_EXTRA_CHECKS if (cnt < 999990) DOUT0("Object %s Retry %d times before childs were unblocked", 1000000-cnt); #endif if (cnt==0) { EOUT("HARD error!!!! - For a long time fObjectBlock=%d is blocked in object %p %s", fObjectBlock, this, GetName()); exit(055); } if (IsLogging()) DOUT1("Obj:%s Deleting childs:%u", GetName(), del_vect.GetSize()); if (IsLogging()) while (del_vect.GetSize()>0) { Reference ref = del_vect.TakeLast(); DOUT1(" Del Child %s owner %s", ref.GetName(), DBOOL(ref.IsOwner())); } del_vect.Clear(); /* if (recursive) { GetAllChildRef(&del_vect); for(unsigned n=0; nDeleteChilds(); del_vect.Clear(); } */ } void dabc::Object::SetName(const char* name) { LockGuard guard(fObjectMutex); if (fObjectRefCnt>0) { EOUT("Cannot change object name when reference counter %d is not zero!!!", fObjectRefCnt); throw dabc::Exception(ex_Object, "Cannot change object name when refcounter is not zero", GetName()); } fObjectName = name; } /** \page object_destroy Destroyment of object * Object live may be complex but way to destroy it much more complex. * Problems are: * - multiple references on object from different threads * - need to cleanup of object from the thread where it is used * - object does not want to be destroyed * All this complexity handled by the dabc::Object::Destroy(obj) call. * * First, object is checked that if it is assigned with a thread (flHasThread). * It means that object can be actively used in the thread and any cleanup * should happen from this thread. In this case AskToDestroyByThread() method will be called. * Method can return true only when objects allowed to be destroyed immediately * * If object does not have thread, ObjectCleanup() will be called immediately, * otherwise it will be called in thread context. * Main motivation for ObjectCleanup() is to release references on * all other objects which may be used inside. One also can free buffers, * close files and do any other relevant job here. * * At the end will be checked existing references on the object. * If any exists, object will be moved to manager cleanup queue. * Manager will try to check if reference can be cleared and finally delete object. * * TODO: dabc::Object::Destroy() should not be used very often, rather reference on the object must be used * Therefore one should try to avoid dabc::Object::Destroy() as much as possible */ void dabc::Object::Destroy(Object* obj) throw() { if (obj==0) return; if (obj->DecReference(true, false)) delete obj; } bool dabc::Object::Find(ConfigIO &cfg) { return GetParent()==0 ? false : cfg.FindItem(GetName()); } dabc::Object::ConstructorPair dabc::Object::MakePair(Reference prnt, const std::string& fullnamearg, bool withmanager) { if (fullnamearg.empty() && prnt.null()) return ConstructorPair(); const char* fullname = fullnamearg.empty() ? "---" : fullnamearg.c_str(); bool isrootfolder(false), isskipparent(false); while (*fullname=='/') { isrootfolder = true; fullname++; } if (!isrootfolder && (*fullname=='#')) { fullname++; isskipparent = true; } if (prnt.null() && withmanager && !isskipparent) { if (!isrootfolder) prnt = dabc::mgr.GetAppFolder(true); if (prnt.null()) prnt = dabc::mgr; } const char* slash = strrchr(fullname, '/'); ConstructorPair pair; if (slash!=0) { pair.name = slash+1; std::string path = std::string(fullname, slash - fullname); if (!prnt.null()) prnt = prnt()->FindChildRef(path.c_str(), true); } else { pair.name = fullname; } if (!isskipparent) pair.parent = prnt; return pair; } dabc::Object::ConstructorPair dabc::Object::MakePair(Object* prnt, const std::string& fullname, bool withmanager) { return dabc::Object::MakePair(Reference(prnt), fullname, withmanager); } dabc::Object::ConstructorPair dabc::Object::MakePair(const std::string& fullname, bool withmanager) { return dabc::Object::MakePair(Reference(), fullname, withmanager); } bool dabc::Object::IsParent(Object* obj) const { if (obj==0) return false; Object* prnt = GetParent(); while (prnt!=0) { if (prnt==obj) return true; prnt = prnt->GetParent(); } return false; } bool dabc::Object::IsNameMatch(const std::string& mask) const { return NameMatch(fObjectName, mask); } bool dabc::Object::NameMatch(const std::string& name, const std::string& mask) { if ((mask.length()==0) || (name.length()==0)) return name.length()==mask.length(); size_t lastsepar(0), separ = mask.find_first_of(';',lastsepar); if (separ!=std::string::npos) { do { } while (lastsepar!=std::string::npos); } if (mask.find_first_of("*?")==mask.npos) return name == mask; if (mask=="*") return true; return fnmatch(mask.c_str(), name.c_str(), FNM_NOESCAPE)==0; } bool dabc::Object::NameMatchM(const std::string& name, const std::string& mask) { size_t separ = mask.find_first_of(':'); if (separ==std::string::npos) return NameMatch(name, mask); size_t lastsepar = 0; do { std::string submask; if (separ==std::string::npos) submask = mask.substr(lastsepar); else if (separ>lastsepar) submask = mask.substr(lastsepar, separ-lastsepar); if (!submask.empty()) if (NameMatch(name, submask)) return true; lastsepar = separ; if (separ!=std::string::npos) { lastsepar++; separ = mask.find_first_of(':', lastsepar); } } while (lastsepar!=std::string::npos); return false; } void dabc::Object::FillFullName(std::string &fullname, Object* upto, bool exclude_top_parent) const { if ((GetParent()!=0) && (GetParent() != upto)) { GetParent()->FillFullName(fullname, upto, exclude_top_parent); fullname.append("/"); } if (GetParent()==0) { if (exclude_top_parent) return; fullname.append("/"); } fullname.append(GetName()); } std::string dabc::Object::ItemName(bool compact) const { std::string res; if (IsParent(dabc::mgr())) dabc::mgr()->FillItemName(this, res, compact); else FillFullName(res, 0); return res; } // ============================================================================ // FIXME: old code, should be adjusted void dabc::Object::Print(int lvl) { LockGuard guard(ObjectMutex()); dabc::lgr()->Debug(lvl, "file", 1, "func", dabc::format("Object: %s Class:%s", GetName(), ClassName()).c_str()); } void dabc::Object::InspectGarbageCollector() { #ifdef DABC_EXTRA_CHECKS LockGuard lock(gObjectGarbageMutex); DOUT0("GarbageCollector: there are %u objects in collector now", gObjectGarbageCollector.size()); for (unsigned n=0;nGetName(), obj->ClassName(), obj->fObjectRefCnt); } #endif }