Named lock pattern now for real

This commit is contained in:
Gasper Jansa
2011-01-04 18:43:56 +01:00
parent 547a648e76
commit 435164bd60
5 changed files with 522 additions and 0 deletions

View File

@@ -0,0 +1,7 @@
/*
* namedLockPattern.cpp
*/
#include "namedLockPattern.h"
//NOTE NamedLockPattern is template so implementation is in header file

View 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 */

View 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);
}
}}

View 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 */

View 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;
}