Named lock pattern now for real
This commit is contained in:
7
pvAccessApp/utils/namedLockPattern.cpp
Normal file
7
pvAccessApp/utils/namedLockPattern.cpp
Normal file
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
* namedLockPattern.cpp
|
||||
*/
|
||||
|
||||
#include "namedLockPattern.h"
|
||||
|
||||
//NOTE NamedLockPattern is template so implementation is in header file
|
||||
144
pvAccessApp/utils/namedLockPattern.h
Normal file
144
pvAccessApp/utils/namedLockPattern.h
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* namedLockPattern.h
|
||||
*/
|
||||
|
||||
#ifndef NAMEDLOCKPATTERN_H
|
||||
#define NAMEDLOCKPATTERN_H
|
||||
|
||||
#include <map>
|
||||
#include <iostream>
|
||||
|
||||
#include <lock.h>
|
||||
#include <pvType.h>
|
||||
|
||||
#include "referenceCountingLock.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace epics::pvData;
|
||||
|
||||
namespace epics { namespace pvAccess {
|
||||
/**
|
||||
* NamedLockPattern
|
||||
*/
|
||||
template <class Key, class Compare = less<Key> >
|
||||
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 <code>true</code> if acquired, <code>false</code> 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<const Key,ReferenceCountingLock*,Compare> _namedLocks;
|
||||
typename std::map<const Key,ReferenceCountingLock*,Compare>::iterator _namedLocksIter;
|
||||
|
||||
/**
|
||||
* Release synchronization lock for named object.
|
||||
* @param name name of the object whose lock to release.
|
||||
* @param release set to <code>false</code> if there is no need to call release
|
||||
* on synchronization lock.
|
||||
*/
|
||||
void releaseSynchronizationObject(const Key name,const bool release);
|
||||
};
|
||||
|
||||
template <class Key, class Compare>
|
||||
bool NamedLockPattern<Key,Compare>::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 <class Key, class Compare>
|
||||
void NamedLockPattern<Key,Compare>::releaseSynchronizationObject(const Key name)
|
||||
{
|
||||
releaseSynchronizationObject(name, true);
|
||||
}
|
||||
|
||||
template <class Key, class Compare>
|
||||
void NamedLockPattern<Key,Compare>::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 Key, class Compare>
|
||||
class NamedLock : private NoDefaultMethods
|
||||
{
|
||||
public:
|
||||
NamedLock(NamedLockPattern<Key,Compare>* 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<Key,Compare>* _namedLockPattern;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* NAMEDLOCKPATTERN_H */
|
||||
81
pvAccessApp/utils/referenceCountingLock.cpp
Normal file
81
pvAccessApp/utils/referenceCountingLock.cpp
Normal file
@@ -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);
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
74
pvAccessApp/utils/referenceCountingLock.h
Normal file
74
pvAccessApp/utils/referenceCountingLock.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* referenceCountingLock.h
|
||||
*/
|
||||
|
||||
#ifndef REFERENCECOUNTINGLOCK_H
|
||||
#define REFERENCECOUNTINGLOCK_H
|
||||
|
||||
#include <map>
|
||||
#include <iostream>
|
||||
#include <pthread.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <pvType.h>
|
||||
#include <epicsAssert.h>
|
||||
|
||||
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 <code>attempt</code> 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 <code>ReferenceCountingLock</code>.
|
||||
* After construction lock is free and reference count equals <code>1</code>.
|
||||
*/
|
||||
ReferenceCountingLock();
|
||||
/**
|
||||
* Destructor of <code>ReferenceCountingLock</code>.
|
||||
*/
|
||||
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 <code>true</code> if acquired, <code>false</code> 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 */
|
||||
216
testApp/utils/namedLockPatternTest.cpp
Normal file
216
testApp/utils/namedLockPatternTest.cpp
Normal file
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* namedLockPatternTest.cpp
|
||||
*
|
||||
*/
|
||||
|
||||
#include "namedLockPattern.h"
|
||||
#include "showConstructDestruct.h"
|
||||
|
||||
#include <epicsAssert.h>
|
||||
#include <iostream>
|
||||
|
||||
#include <osiSock.h>
|
||||
|
||||
using namespace epics::pvAccess;
|
||||
using namespace std;
|
||||
|
||||
void testIntLockPattern()
|
||||
{
|
||||
int64 timeout = 100;
|
||||
NamedLockPattern<int> 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<int*> 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<const char*,cmp_str> 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<const osiSockAddr*,comp_osiSockAddrPtr> 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<const osiSockAddr*,comp_osiSockAddrPtr> 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<osiSockAddr,comp_osiSockAddr>* namedLockPattern = (NamedLockPattern<osiSockAddr,comp_osiSockAddr>*)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<osiSockAddr,comp_osiSockAddr> 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<osiSockAddr,comp_osiSockAddr> namedGuard(namedLockPattern);
|
||||
assert(namedGuard.acquireSynchronizationObject(addr,timeout));
|
||||
sleep(5);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void* testWorker2(void* p)
|
||||
{
|
||||
int32 timeout = 1000;
|
||||
const int32 max = 1000;
|
||||
NamedLockPattern<osiSockAddr,comp_osiSockAddr>* namedLockPattern = (NamedLockPattern<osiSockAddr,comp_osiSockAddr>*)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<osiSockAddr,comp_osiSockAddr> 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<osiSockAddr,comp_osiSockAddr> 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<osiSockAddr,comp_osiSockAddr> 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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user