📄 safecoll.cxx
字号:
/* * safecoll.cxx * * Thread safe collection classes. * * Portable Windows Library * * Copyright (c) 2002 Equivalence Pty. Ltd. * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Portable Windows Library. * * The Initial Developer of the Original Code is Equivalence Pty. Ltd. * * Contributor(s): ______________________________________. * * $Log: safecoll.cxx,v $ * Revision 1.14 2004/10/14 23:01:31 csoutheren * Fiuxed problem with usage of Sleep * * Revision 1.13 2004/10/14 12:31:47 rjongbloed * Added synchronous mode for safe collection RemoveAll() to wait until all objects * have actually been deleted before returning. * * Revision 1.12 2004/10/04 12:54:33 rjongbloed * Added functions for locking an unlocking to "auto-unlock" classes. * * Revision 1.11 2004/08/14 07:42:31 rjongbloed * Added trace log at level 6 for helping find PSafeObject reference/dereference errors. * * Revision 1.10 2004/08/12 12:37:41 rjongbloed * Fixed bug recently introduced so removes deleted object from deletion list. * Also changed removal list to be correct type. * * Revision 1.9 2004/08/05 12:15:56 rjongbloed * Added classes for auto unlocking read only and read write mutex on * PSafeObject - similar to PWaitAndSIgnal. * Utilised mutable keyword for mutex and improved the constness of functions. * Added DisallowDeleteObjects to safe collections so can have a PSafeObject in * multiple collections. * Added a tempalte function to do casting of PSafePtr to a PSafePtr of a derived * class. * Assured that a PSafeObject present on a collection always increments its * reference count so while in collection it is not deleted. * * Revision 1.8 2004/04/03 08:22:22 csoutheren * Remove pseudo-RTTI and replaced with real RTTI * * Revision 1.7 2002/12/10 07:37:34 robertj * optimised SetLockMode() so if doesn't change mode it doesn't do anything. * * Revision 1.6 2002/10/29 00:06:24 robertj * Changed template classes so things like PSafeList actually creates the * base collection class as well. * Allowed for the PSafeList::Append() to return a locked pointer to the * object just appended. * * Revision 1.5 2002/10/04 08:22:50 robertj * Changed read/write mutex so can be called by same thread without deadlock * removing the need to a lock count in safe pointer. * Added asserts if try and dereference a NULL safe pointer. * Added more documentation on behaviour. * * Revision 1.4 2002/08/29 06:53:28 robertj * Added optimisiation, separate mutex for toBeRemoved list. * Added assert for reference count going below zero. * Fixed incorrect usage of lockCount if target of an assignment from another * safe pointer. Would not unlock the safe object which could cause deadlock. * * Revision 1.3 2002/05/06 00:44:45 robertj * Made the lock/unlock read only const so can be used in const functions. * * Revision 1.2 2002/05/01 04:48:05 robertj * GNU compatibility. * * Revision 1.1 2002/05/01 04:16:44 robertj * Added thread safe collection classes. * */#ifdef __GNUC__#pragma implementation "safecoll.h"#endif#include <ptlib.h>#include <ptlib/safecoll.h>#define new PNEW/////////////////////////////////////////////////////////////////////////////PSafeObject::PSafeObject(){ safeReferenceCount = 0; safelyBeingRemoved = FALSE;}BOOL PSafeObject::SafeReference(){ PWaitAndSignal mutex(safetyMutex); if (safelyBeingRemoved) return FALSE; safeReferenceCount++; PTRACE(6, "SafeColl\tIncrement reference count to " << safeReferenceCount << " for " << GetClass() << ' ' << (void *)this); return TRUE;}void PSafeObject::SafeDereference(){ safetyMutex.Wait(); if (PAssert(safeReferenceCount > 0, PLogicError)) { safeReferenceCount--; PTRACE(6, "SafeColl\tDecrement reference count to " << safeReferenceCount << " for " << GetClass() << ' ' << (void *)this); } safetyMutex.Signal();}BOOL PSafeObject::LockReadOnly() const{ safetyMutex.Wait(); if (safelyBeingRemoved) { safetyMutex.Signal(); return FALSE; } safetyMutex.Signal(); safeInUseFlag.StartRead(); return TRUE;}void PSafeObject::UnlockReadOnly() const{ safeInUseFlag.EndRead();}BOOL PSafeObject::LockReadWrite(){ safetyMutex.Wait(); if (safelyBeingRemoved) { safetyMutex.Signal(); return FALSE; } safetyMutex.Signal(); safeInUseFlag.StartWrite(); return TRUE;}void PSafeObject::UnlockReadWrite(){ safeInUseFlag.EndWrite();}void PSafeObject::SafeRemove(){ safetyMutex.Wait(); safelyBeingRemoved = TRUE; safetyMutex.Signal();}BOOL PSafeObject::SafelyCanBeDeleted() const{ PWaitAndSignal mutex(safetyMutex); return safelyBeingRemoved && safeReferenceCount == 0;}/////////////////////////////////////////////////////////////////////////////PSafeLockReadOnly::PSafeLockReadOnly(const PSafeObject & object) : safeObject((PSafeObject &)object){ locked = safeObject.LockReadOnly();}PSafeLockReadOnly::~PSafeLockReadOnly(){ if (locked) safeObject.UnlockReadOnly();}BOOL PSafeLockReadOnly::Lock(){ locked = safeObject.LockReadOnly(); return locked;}void PSafeLockReadOnly::Unlock(){ if (locked) { safeObject.UnlockReadOnly(); locked = FALSE; }}/////////////////////////////////////////////////////////////////////////////PSafeLockReadWrite::PSafeLockReadWrite(const PSafeObject & object) : safeObject((PSafeObject &)object){ locked = safeObject.LockReadWrite();}PSafeLockReadWrite::~PSafeLockReadWrite(){ if (locked) safeObject.UnlockReadWrite();}BOOL PSafeLockReadWrite::Lock(){ locked = safeObject.LockReadWrite(); return locked;}void PSafeLockReadWrite::Unlock(){ if (locked) { safeObject.UnlockReadWrite(); locked = FALSE; }}/////////////////////////////////////////////////////////////////////////////PSafeCollection::PSafeCollection(PCollection * coll){ collection = coll; collection->DisallowDeleteObjects(); toBeRemoved.DisallowDeleteObjects(); deleteObjects = TRUE;}PSafeCollection::~PSafeCollection(){ deleteObjectsTimer.Stop(); toBeRemoved.AllowDeleteObjects(); toBeRemoved.RemoveAll(); collection->AllowDeleteObjects(); delete collection;}BOOL PSafeCollection::SafeRemove(PSafeObject * obj){ if (obj == NULL) return FALSE; PWaitAndSignal mutex(collectionMutex); if (!collection->Remove(obj)) return FALSE; SafeRemoveObject(obj); return TRUE;}BOOL PSafeCollection::SafeRemoveAt(PINDEX idx){ PWaitAndSignal mutex(collectionMutex); PSafeObject * obj = PDownCast(PSafeObject, collection->RemoveAt(idx)); if (obj == NULL) return FALSE; SafeRemoveObject(obj); return TRUE;}void PSafeCollection::RemoveAll(BOOL synchronous){ collectionMutex.Wait(); while (collection->GetSize() > 0) SafeRemoveObject(PDownCast(PSafeObject, collection->RemoveAt(0))); collectionMutex.Signal(); if (synchronous) { // Have unfortunate busy loop here, but it should be very // rare that it will be here for long while (!DeleteObjectsToBeRemoved()) PThread::Sleep(100); }}void PSafeCollection::SafeRemoveObject(PSafeObject * obj){ if (obj == NULL) return; obj->SafeDereference(); if (!deleteObjects) return; obj->SafeRemove(); removalMutex.Wait(); toBeRemoved.Append(obj); removalMutex.Signal();}BOOL PSafeCollection::DeleteObjectsToBeRemoved(){ PWaitAndSignal lock(removalMutex); PINDEX i = 0; while (i < toBeRemoved.GetSize()) { if (toBeRemoved[i].SafelyCanBeDeleted()) { PObject * obj = toBeRemoved.RemoveAt(i); removalMutex.Signal(); DeleteObject(obj); removalMutex.Wait(); i = 0; // Restart looking through list } else i++; } return toBeRemoved.IsEmpty() && collection->IsEmpty();}void PSafeCollection::DeleteObject(PObject * object) const{ delete object;}void PSafeCollection::SetAutoDeleteObjects(){ if (deleteObjectsTimer.IsRunning()) return; deleteObjectsTimer.SetNotifier(PCREATE_NOTIFIER(DeleteObjectsTimeout)); deleteObjectsTimer.RunContinuous(1000); // EVery second}void PSafeCollection::DeleteObjectsTimeout(PTimer &, INT){ DeleteObjectsToBeRemoved();}PINDEX PSafeCollection::GetSize() const{ PWaitAndSignal lock(collectionMutex); return collection->GetSize();}/////////////////////////////////////////////////////////////////////////////PSafePtrBase::PSafePtrBase(PSafeObject * obj, PSafetyMode mode){ collection = NULL; currentObject = obj; lockMode = mode; EnterSafetyMode(WithReference);}PSafePtrBase::PSafePtrBase(const PSafeCollection & safeCollection, PSafetyMode mode, PINDEX idx){ collection = &safeCollection; currentObject = NULL; lockMode = mode; Assign(idx);}PSafePtrBase::PSafePtrBase(const PSafeCollection & safeCollection, PSafetyMode mode, PSafeObject * obj){ collection = &safeCollection; currentObject = NULL; lockMode = mode; Assign(obj);}PSafePtrBase::PSafePtrBase(const PSafePtrBase & enumerator){ collection = enumerator.collection; currentObject = enumerator.currentObject; lockMode = enumerator.lockMode; EnterSafetyMode(WithReference);}PSafePtrBase::~PSafePtrBase(){ ExitSafetyMode(WithDereference);}PObject::Comparison PSafePtrBase::Compare(const PObject & obj) const{ const PSafePtrBase * other = PDownCast(const PSafePtrBase, &obj); if (other == NULL) return GreaterThan; if (currentObject < other->currentObject) return LessThan; if (currentObject > other->currentObject) return GreaterThan; return EqualTo;}void PSafePtrBase::Assign(const PSafePtrBase & enumerator){ if (this == &enumerator) return; // lockCount ends up zero after this ExitSafetyMode(WithDereference); collection = enumerator.collection; currentObject = enumerator.currentObject; lockMode = enumerator.lockMode; EnterSafetyMode(WithReference);}void PSafePtrBase::Assign(const PSafeCollection & safeCollection){ // lockCount ends up zero after this ExitSafetyMode(WithDereference); collection = &safeCollection; lockMode = PSafeReadWrite; Assign((PINDEX)0);}void PSafePtrBase::Assign(PSafeObject * newObj){ ExitSafetyMode(WithDereference); currentObject = newObj; if (newObj == NULL) return; if (collection == NULL) { lockMode = PSafeReference; if (!EnterSafetyMode(WithReference)) currentObject = NULL; return; } collection->collectionMutex.Wait(); if (collection->collection->GetObjectsIndex(newObj) == P_MAX_INDEX) { collection->collectionMutex.Signal(); collection = NULL; lockMode = PSafeReference; if (!EnterSafetyMode(WithReference)) currentObject = NULL; } else { if (!newObj->SafeReference()) currentObject = NULL; collection->collectionMutex.Signal(); EnterSafetyMode(AlreadyReferenced); }}void PSafePtrBase::Assign(PINDEX idx){ ExitSafetyMode(WithDereference); currentObject = NULL; if (collection == NULL) return; collection->collectionMutex.Wait(); while (idx < collection->collection->GetSize()) { currentObject = (PSafeObject *)collection->collection->GetAt(idx); if (currentObject != NULL) { if (currentObject->SafeReference()) break; currentObject = NULL; } idx++; } collection->collectionMutex.Signal(); EnterSafetyMode(AlreadyReferenced);}void PSafePtrBase::Next(){ if (collection == NULL || currentObject == NULL) return; ExitSafetyMode(NoDereference); collection->collectionMutex.Wait(); PINDEX idx = collection->collection->GetObjectsIndex(currentObject); currentObject->SafeDereference(); currentObject = NULL; if (idx != P_MAX_INDEX) { while (++idx < collection->collection->GetSize()) { currentObject = (PSafeObject *)collection->collection->GetAt(idx); if (currentObject != NULL) { if (currentObject->SafeReference()) break; currentObject = NULL; } } } collection->collectionMutex.Signal(); EnterSafetyMode(AlreadyReferenced);}void PSafePtrBase::Previous(){ if (collection == NULL || currentObject == NULL) return; ExitSafetyMode(NoDereference); collection->collectionMutex.Wait(); PINDEX idx = collection->collection->GetObjectsIndex(currentObject); currentObject->SafeDereference(); currentObject = NULL; if (idx != P_MAX_INDEX) { while (idx-- > 0) { currentObject = (PSafeObject *)collection->collection->GetAt(idx); if (currentObject != NULL) { if (currentObject->SafeReference()) break; currentObject = NULL; } } } collection->collectionMutex.Signal(); EnterSafetyMode(AlreadyReferenced);}BOOL PSafePtrBase::SetSafetyMode(PSafetyMode mode){ if (lockMode == mode) return TRUE; ExitSafetyMode(NoDereference); lockMode = mode; return EnterSafetyMode(AlreadyReferenced);}BOOL PSafePtrBase::EnterSafetyMode(EnterSafetyModeOption ref){ if (currentObject == NULL) return FALSE; if (ref == WithReference && !currentObject->SafeReference()) { currentObject = NULL; return FALSE; } switch (lockMode) { case PSafeReadOnly : if (currentObject->LockReadOnly()) return TRUE; break; case PSafeReadWrite : if (currentObject->LockReadWrite()) return TRUE; break; case PSafeReference : return TRUE; } currentObject->SafeDereference(); currentObject = NULL; return FALSE;}void PSafePtrBase::ExitSafetyMode(ExitSafetyModeOption ref){ if (currentObject == NULL) return; switch (lockMode) { case PSafeReadOnly : currentObject->UnlockReadOnly(); break; case PSafeReadWrite : currentObject->UnlockReadWrite(); break; case PSafeReference : break; } if (ref == WithDereference) currentObject->SafeDereference();}// End of File ///////////////////////////////////////////////////////////////
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -