From 435164bd60de1435d14e64e4f06d7bc0f6a38f38 Mon Sep 17 00:00:00 2001 From: Gasper Jansa Date: Tue, 4 Jan 2011 18:43:56 +0100 Subject: [PATCH] Named lock pattern now for real --- pvAccessApp/utils/namedLockPattern.cpp | 7 + pvAccessApp/utils/namedLockPattern.h | 144 +++++++++++++ pvAccessApp/utils/referenceCountingLock.cpp | 81 ++++++++ pvAccessApp/utils/referenceCountingLock.h | 74 +++++++ testApp/utils/namedLockPatternTest.cpp | 216 ++++++++++++++++++++ 5 files changed, 522 insertions(+) create mode 100644 pvAccessApp/utils/namedLockPattern.cpp create mode 100644 pvAccessApp/utils/namedLockPattern.h create mode 100644 pvAccessApp/utils/referenceCountingLock.cpp create mode 100644 pvAccessApp/utils/referenceCountingLock.h create mode 100644 testApp/utils/namedLockPatternTest.cpp diff --git a/pvAccessApp/utils/namedLockPattern.cpp b/pvAccessApp/utils/namedLockPattern.cpp new file mode 100644 index 0000000..538d502 --- /dev/null +++ b/pvAccessApp/utils/namedLockPattern.cpp @@ -0,0 +1,7 @@ +/* + * namedLockPattern.cpp + */ + +#include "namedLockPattern.h" + +//NOTE NamedLockPattern is template so implementation is in header file diff --git a/pvAccessApp/utils/namedLockPattern.h b/pvAccessApp/utils/namedLockPattern.h new file mode 100644 index 0000000..d57455a --- /dev/null +++ b/pvAccessApp/utils/namedLockPattern.h @@ -0,0 +1,144 @@ +/* + * namedLockPattern.h + */ + +#ifndef NAMEDLOCKPATTERN_H +#define NAMEDLOCKPATTERN_H + +#include +#include + +#include +#include + +#include "referenceCountingLock.h" + +using namespace std; +using namespace epics::pvData; + +namespace epics { namespace pvAccess { +/** + * NamedLockPattern + */ +template > +class NamedLockPattern +{ +public: + /** + * Constructor. + */ + NamedLockPattern() {}; + /** + * Destructor. + */ + virtual ~NamedLockPattern() {}; + /** + * Acquire synchronization lock for named object. + * @param name name of the object whose lock to acquire. + * @param msec the number of milleseconds to wait. + * An argument less than or equal to zero means not to wait at all. + * @return true if acquired, false othwerwise. + */ + bool acquireSynchronizationObject(const Key name, const int64 msec); + /** + * Release synchronization lock for named object. + * @param name name of the object whose lock to release. + */ + void releaseSynchronizationObject(const Key name); +private: + Mutex _mutex; + std::map _namedLocks; + typename std::map::iterator _namedLocksIter; + + /** + * Release synchronization lock for named object. + * @param name name of the object whose lock to release. + * @param release set to false if there is no need to call release + * on synchronization lock. + */ + void releaseSynchronizationObject(const Key name,const bool release); +}; + +template +bool NamedLockPattern::acquireSynchronizationObject(const Key name, const int64 msec) +{ + ReferenceCountingLock* lock; + { //due to guard + Lock guard(&_mutex); + + _namedLocksIter = _namedLocks.find(name); + // get synchronization object + + // none is found, create and return new one + // increment references + if(_namedLocksIter == _namedLocks.end()) + { + lock = new ReferenceCountingLock(); + _namedLocks[name] = lock; + } + else + { + lock = _namedLocksIter->second; + lock->increment(); + } + } // end of guarded area + + bool success = lock->acquire(msec); + + if(!success) + { + releaseSynchronizationObject(name, false); + } + + return success; +} + +template +void NamedLockPattern::releaseSynchronizationObject(const Key name) +{ + releaseSynchronizationObject(name, true); +} + +template +void NamedLockPattern::releaseSynchronizationObject(const Key name,const bool release) +{ + Lock guard(&_mutex); + ReferenceCountingLock* lock; + _namedLocksIter = _namedLocks.find(name); + + // release lock + if (_namedLocksIter != _namedLocks.end()) + { + lock = _namedLocksIter->second; + + // release the lock + if (release) + { + lock->release(); + } + + // if there only one current lock exists + // remove it from the map + if (lock->decrement() <= 0) + { + _namedLocks.erase(_namedLocksIter); + delete lock; + } + } +} + +template +class NamedLock : private NoDefaultMethods +{ +public: + NamedLock(NamedLockPattern* namedLockPattern): _namedLockPattern(namedLockPattern) {} + bool acquireSynchronizationObject(const Key name, const int64 msec) {_name = name; return _namedLockPattern->acquireSynchronizationObject(name,msec);} + ~NamedLock(){_namedLockPattern->releaseSynchronizationObject(_name);} +private: + Key _name; + NamedLockPattern* _namedLockPattern; +}; + +}} + +#endif /* NAMEDLOCKPATTERN_H */ diff --git a/pvAccessApp/utils/referenceCountingLock.cpp b/pvAccessApp/utils/referenceCountingLock.cpp new file mode 100644 index 0000000..4a8b3fb --- /dev/null +++ b/pvAccessApp/utils/referenceCountingLock.cpp @@ -0,0 +1,81 @@ +/* + * namedLockPattern.cpp + */ + +#include "referenceCountingLock.h" + +namespace epics { namespace pvAccess { + +ReferenceCountingLock::ReferenceCountingLock(): _references(1) +{ + pthread_mutexattr_t mutexAttribute; + int32 retval = pthread_mutexattr_init(&mutexAttribute); + if(retval != 0) + { + //string errMsg = "Error: pthread_mutexattr_init failed: " + string(strerror(retval)); + assert(true); + } + retval = pthread_mutexattr_settype(&mutexAttribute, PTHREAD_MUTEX_RECURSIVE); + if(retval == 0) + { + retval = pthread_mutex_init(&_mutex, &mutexAttribute); + if(retval != 0) + { + //string errMsg = "Error: pthread_mutex_init failed: " + string(strerror(retval)); + assert(true); + } + } + else + { + //string errMsg = "Error: pthread_mutexattr_settype failed: " + string(strerror(retval)); + assert(true); + } + + pthread_mutexattr_destroy(&mutexAttribute); +} + +ReferenceCountingLock::~ReferenceCountingLock() +{ + pthread_mutex_destroy(&_mutex); +} + +bool ReferenceCountingLock::acquire(int64 msecs) +{ + struct timespec deltatime; + deltatime.tv_sec = msecs / 1000; + deltatime.tv_nsec = (msecs % 1000) * 1000; + + int32 retval = pthread_mutex_timedlock(&_mutex, &deltatime); + if(retval == 0) + { + return true; + } + return false; +} + +void ReferenceCountingLock::release() +{ + int retval = pthread_mutex_unlock(&_mutex); + if(retval != 0) + { + string errMsg = "Error: pthread_mutex_unlock failed: " + string(strerror(retval)); + //TODO do something? + } +} + +int ReferenceCountingLock::increment() +{ + //TODO does it really has to be atomic? + //return ++_references; + return __sync_add_and_fetch(&_references,1); +} + +int ReferenceCountingLock::decrement() +{ + //TODO does it really has to be atomic? + //return --_references; + return __sync_sub_and_fetch(&_references,1); +} + +}} + diff --git a/pvAccessApp/utils/referenceCountingLock.h b/pvAccessApp/utils/referenceCountingLock.h new file mode 100644 index 0000000..2825cae --- /dev/null +++ b/pvAccessApp/utils/referenceCountingLock.h @@ -0,0 +1,74 @@ +/* + * referenceCountingLock.h + */ + +#ifndef REFERENCECOUNTINGLOCK_H +#define REFERENCECOUNTINGLOCK_H + +#include +#include +#include +#include +#include + +#include +#include + +using namespace std; +using namespace epics::pvData; + +namespace epics { namespace pvAccess { + +/** + * Reference counting mutex implementation w/ deadlock detection. + * Synchronization helper class used (intended for use) for activation/deactivation synchronization. + * This class enforces attempt method of acquiring the locks to prevent deadlocks. + * Class also offers reference counting. + * (NOTE: automatic lock counting was not implemented due to imperfect usage.) + * + */ +class ReferenceCountingLock +{ +public: + /** + * Constructor of ReferenceCountingLock. + * After construction lock is free and reference count equals 1. + */ + ReferenceCountingLock(); + /** + * Destructor of ReferenceCountingLock. + */ + virtual ~ReferenceCountingLock(); + /** + * Attempt to acquire lock. + * + * @param msecs the number of milleseconds to wait. + * An argument less than or equal to zero means not to wait at all. + * @return true if acquired, false otherwise. + */ + bool acquire(int64 msecs); + /** + * Release previously acquired lock. + */ + void release(); + /** + * Increment number of references. + * + * @return number of references. + */ + int increment(); + /** + * Decrement number of references. + * + * @return number of references. + */ + int decrement(); +private: + int _references; + pthread_mutex_t _mutex; + +}; + +}} + +#endif /* NAMEDLOCKPATTERN_H */ diff --git a/testApp/utils/namedLockPatternTest.cpp b/testApp/utils/namedLockPatternTest.cpp new file mode 100644 index 0000000..9224800 --- /dev/null +++ b/testApp/utils/namedLockPatternTest.cpp @@ -0,0 +1,216 @@ +/* + * namedLockPatternTest.cpp + * + */ + +#include "namedLockPattern.h" +#include "showConstructDestruct.h" + +#include +#include + +#include + +using namespace epics::pvAccess; +using namespace std; + +void testIntLockPattern() +{ + int64 timeout = 100; + NamedLockPattern namedLockPattern; + int name1 = 1; + assert(namedLockPattern.acquireSynchronizationObject(name1,timeout)); + assert(namedLockPattern.acquireSynchronizationObject(name1,timeout)); + namedLockPattern.releaseSynchronizationObject(name1); + namedLockPattern.releaseSynchronizationObject(name1); + int name2 = 2; + assert(namedLockPattern.acquireSynchronizationObject(name2,timeout)); + namedLockPattern.releaseSynchronizationObject(name2); +} + +void testIntPtrLockPattern() +{ + int64 timeout = 100; + NamedLockPattern namedLockPattern; + int name1 = 1; + assert(namedLockPattern.acquireSynchronizationObject(&name1,timeout)); + assert(namedLockPattern.acquireSynchronizationObject(&name1,timeout)); + namedLockPattern.releaseSynchronizationObject(&name1); + namedLockPattern.releaseSynchronizationObject(&name1); + int name2 = 2; + assert(namedLockPattern.acquireSynchronizationObject(&name2,timeout)); + namedLockPattern.releaseSynchronizationObject(&name2); +} + +struct cmp_str +{ + bool operator()(char const *a, char const *b) + { + return strcmp(a, b) < 0; + } +}; + +void testCharPtrLockPattern() +{ + int64 timeout = 100; + NamedLockPattern namedLockPattern; + string name1 = "lojze"; + assert(namedLockPattern.acquireSynchronizationObject(name1.c_str(),timeout)); + assert(namedLockPattern.acquireSynchronizationObject(name1.c_str(),timeout)); + namedLockPattern.releaseSynchronizationObject(name1.c_str()); + namedLockPattern.releaseSynchronizationObject(name1.c_str()); + string name2 = "francka"; + assert(namedLockPattern.acquireSynchronizationObject(name2.c_str(),timeout)); + namedLockPattern.releaseSynchronizationObject(name2.c_str()); +} + +struct comp_osiSockAddrPtr +{ + bool operator()(osiSockAddr const *a, osiSockAddr const *b) + { + if (a->sa.sa_family < b->sa.sa_family) return true; + if ((a->sa.sa_family == b->sa.sa_family) && (a->ia.sin_addr.s_addr < b->ia.sin_addr.s_addr )) return true; + if ((a->sa.sa_family == b->sa.sa_family) && (a->ia.sin_addr.s_addr == b->ia.sin_addr.s_addr ) && ( a->ia.sin_port < b->ia.sin_port )) return true; + return false; + } +}; + +void testOsiSockAddrLockPattern() +{ + int64 timeout = 10000; + NamedLockPattern namedLockPattern; + osiSockAddr name1; + name1.ia.sin_addr.s_addr = 1; + name1.ia.sin_port = 1; + name1.ia.sin_family = AF_INET; + + assert(namedLockPattern.acquireSynchronizationObject(&name1,timeout)); + assert(namedLockPattern.acquireSynchronizationObject(&name1,timeout)); + namedLockPattern.releaseSynchronizationObject(&name1); + namedLockPattern.releaseSynchronizationObject(&name1); + + osiSockAddr name2; + name2.ia.sin_addr.s_addr = 1; + name2.ia.sin_port = 1; + name2.ia.sin_family = AF_INET; + NamedLock namedGuard(&namedLockPattern); + assert(namedGuard.acquireSynchronizationObject(&name1,timeout)); +} + +struct comp_osiSockAddr +{ + bool operator()(osiSockAddr const a, osiSockAddr const b) + { + if (a.sa.sa_family < b.sa.sa_family) return true; + if ((a.sa.sa_family == b.sa.sa_family) && (a.ia.sin_addr.s_addr < b.ia.sin_addr.s_addr )) return true; + if ((a.sa.sa_family == b.sa.sa_family) && (a.ia.sin_addr.s_addr == b.ia.sin_addr.s_addr ) && ( a.ia.sin_port < b.ia.sin_port )) return true; + return false; + } +}; + +void* testWorker1(void* p) +{ + int32 timeout = 1000; + const int32 max = 1000; + NamedLockPattern* namedLockPattern = (NamedLockPattern*)p; + + for(int32 i = 0 ; i < max; i = i +2) + { + osiSockAddr addr; + addr.ia.sin_addr.s_addr = i; + addr.ia.sin_port = i; + addr.ia.sin_family = AF_INET; + NamedLock namedGuard(namedLockPattern); + assert(namedGuard.acquireSynchronizationObject(addr,timeout)); + usleep(1); + } + + //this one takes a lock, thread 2 will be slower and will get timeout + { //due to namedGuard + osiSockAddr addr; + addr.ia.sin_addr.s_addr = 1; + addr.ia.sin_port = 1; + addr.ia.sin_family = AF_INET; + NamedLock namedGuard(namedLockPattern); + assert(namedGuard.acquireSynchronizationObject(addr,timeout)); + sleep(5); + } + + return NULL; +} + + +void* testWorker2(void* p) +{ + int32 timeout = 1000; + const int32 max = 1000; + NamedLockPattern* namedLockPattern = (NamedLockPattern*)p; + + for(int32 i = 1 ; i < max; i = i + 2) + { + osiSockAddr addr; + addr.ia.sin_addr.s_addr = i; + addr.ia.sin_port = i; + addr.ia.sin_family = AF_INET; + NamedLock namedGuard(namedLockPattern); + assert(namedGuard.acquireSynchronizationObject(addr,timeout)); + usleep(1); + } + + //this thread sleeps a while and gets timeout on lock + { + sleep(1); + osiSockAddr addr; + addr.ia.sin_addr.s_addr = 1; + addr.ia.sin_port = 1; + addr.ia.sin_family = AF_INET; + NamedLock namedGuard(namedLockPattern); + assert(!namedGuard.acquireSynchronizationObject(addr,timeout)); + } + + return NULL; +} + +int main(int argc, char *argv[]) +{ + testIntLockPattern(); + testIntPtrLockPattern(); + testCharPtrLockPattern(); + testOsiSockAddrLockPattern(); + + pthread_t _worker1Id; + pthread_t _worker2Id; + + NamedLockPattern namedLockPattern; + + //create two threads + int32 retval = pthread_create(&_worker1Id, NULL, testWorker1, &namedLockPattern); + if(retval != 0) + { + assert(true); + } + + retval = pthread_create(&_worker2Id, NULL, testWorker2, &namedLockPattern); + if(retval != 0) + { + assert(true); + } + + //wait for threads + retval = pthread_join(_worker1Id, NULL); + if(retval != 0) + { + assert(true); + } + + retval = pthread_join(_worker2Id, NULL); + if(retval != 0) + { + assert(true); + } + + getShowConstructDestruct()->constuctDestructTotals(stdout); + return 0; +} + +