new locking section macros for RWLocks, old acquirer bites the dust
This commit is contained in:
parent
d0b6919eea
commit
c11915a4c4
4 changed files with 125 additions and 169 deletions
112
src/lib/rwlock.c
112
src/lib/rwlock.c
|
|
@ -18,28 +18,30 @@
|
|||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#include <errno.h>
|
||||
|
||||
#include "lib/error.h"
|
||||
#include "lib/rwlock.h"
|
||||
|
||||
|
||||
LUMIERA_ERROR_DEFINE(RWLOCK_AGAIN, "maximum number of readlocks exceed");
|
||||
LUMIERA_ERROR_DEFINE(RWLOCK_DEADLOCK, "deadlock detected");
|
||||
LUMIERA_ERROR_DEFINE(RWLOCK_DESTROY, "destroy rwlock");
|
||||
LUMIERA_ERROR_DEFINE(RWLOCK_UNLOCK, "unlock");
|
||||
LUMIERA_ERROR_DEFINE(RWLOCK_RLOCK, "rlock");
|
||||
LUMIERA_ERROR_DEFINE(RWLOCK_WLOCK, "wlock");
|
||||
LUMIERA_ERROR_DEFINE(RWLOCK_DESTROY, "destroying rwlock");
|
||||
LUMIERA_ERROR_DEFINE(RWLOCK_UNLOCK, "unlock rwlock failed");
|
||||
LUMIERA_ERROR_DEFINE(RWLOCK_RDLOCK, "locking rwlock for reading failed");
|
||||
LUMIERA_ERROR_DEFINE(RWLOCK_WRLOCK, "locking rwlock for writing failed");
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Read/write locks.
|
||||
*/
|
||||
|
||||
|
||||
LumieraRWLock
|
||||
lumiera_rwlock_init (LumieraRWLock self)
|
||||
{
|
||||
if (self)
|
||||
{
|
||||
pthread_rwlock_init (&self->rwlock, NULL);
|
||||
NOBUG_RESOURCE_HANDLE_INIT (self->rh);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
|
@ -56,94 +58,10 @@ lumiera_rwlock_destroy (LumieraRWLock self)
|
|||
return self;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
LumieraRWLockacquirer
|
||||
lumiera_rwlockacquirer_init (LumieraRWLockacquirer self, LumieraRWLock rwlock, enum lumiera_lockstate state)
|
||||
{
|
||||
REQUIRE (self);
|
||||
REQUIRE (rwlock);
|
||||
REQUIRE (state != LUMIERA_LOCKED, "illegal state for rwlock");
|
||||
self->rwlock = rwlock;
|
||||
self->state = state;
|
||||
|
||||
if (state == LUMIERA_RDLOCKED)
|
||||
switch (pthread_rwlock_rdlock (&rwlock->rwlock))
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
case EAGAIN:
|
||||
lumiera_error_set (LUMIERA_ERROR_RWLOCK_AGAIN);
|
||||
return NULL;
|
||||
case EDEADLK:
|
||||
lumiera_error_set (LUMIERA_ERROR_RWLOCK_DEADLOCK);
|
||||
return NULL;
|
||||
default:
|
||||
LUMIERA_DIE (RWLOCK_RLOCK);
|
||||
}
|
||||
else
|
||||
switch (pthread_rwlock_wrlock (&rwlock->rwlock))
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
case EDEADLK:
|
||||
lumiera_error_set (LUMIERA_ERROR_RWLOCK_DEADLOCK);
|
||||
return NULL;
|
||||
default:
|
||||
LUMIERA_DIE (RWLOCK_WLOCK);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
|
||||
LumieraRWLockacquirer
|
||||
lumiera_rwlockacquirer_rdlock (LumieraRWLockacquirer self)
|
||||
{
|
||||
REQUIRE (self);
|
||||
REQUIRE (self->state == LUMIERA_UNLOCKED, "rwlock already locked");
|
||||
|
||||
switch (pthread_rwlock_rdlock (&self->rwlock->rwlock))
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
case EAGAIN:
|
||||
lumiera_error_set (LUMIERA_ERROR_RWLOCK_AGAIN);
|
||||
return NULL;
|
||||
case EDEADLK:
|
||||
lumiera_error_set (LUMIERA_ERROR_RWLOCK_DEADLOCK);
|
||||
return NULL;
|
||||
default:
|
||||
LUMIERA_DIE (RWLOCK_RLOCK);
|
||||
}
|
||||
|
||||
self->state = LUMIERA_RDLOCKED;
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
|
||||
LumieraRWLockacquirer
|
||||
lumiera_rwlockacquirer_wrlock (LumieraRWLockacquirer self)
|
||||
{
|
||||
REQUIRE (self);
|
||||
REQUIRE (self->state == LUMIERA_UNLOCKED, "rwlock already locked");
|
||||
|
||||
switch (pthread_rwlock_wrlock (&self->rwlock->rwlock))
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
case EDEADLK:
|
||||
lumiera_error_set (LUMIERA_ERROR_RWLOCK_DEADLOCK);
|
||||
return NULL;
|
||||
default:
|
||||
LUMIERA_DIE (RWLOCK_WLOCK);
|
||||
}
|
||||
|
||||
self->state = LUMIERA_WRLOCKED;
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
// Local Variables:
|
||||
// mode: C
|
||||
// c-file-style: "gnu"
|
||||
// indent-tabs-mode: nil
|
||||
// End:
|
||||
*/
|
||||
|
|
|
|||
134
src/lib/rwlock.h
134
src/lib/rwlock.h
|
|
@ -27,41 +27,71 @@
|
|||
#endif
|
||||
|
||||
#include <pthread.h>
|
||||
//#include <errno.h>
|
||||
#include <nobug.h>
|
||||
|
||||
#include "lib/locking.h"
|
||||
|
||||
LUMIERA_ERROR_DECLARE(RWLOCK_AGAIN);
|
||||
LUMIERA_ERROR_DECLARE(RWLOCK_DEADLOCK);
|
||||
LUMIERA_ERROR_DECLARE(RWLOCK_DESTROY);
|
||||
LUMIERA_ERROR_DECLARE(RWLOCK_UNLOCK);
|
||||
LUMIERA_ERROR_DECLARE(RWLOCK_RLOCK);
|
||||
LUMIERA_ERROR_DECLARE(RWLOCK_WLOCK);
|
||||
LUMIERA_ERROR_DECLARE(RWLOCK_RDLOCK);
|
||||
LUMIERA_ERROR_DECLARE(RWLOCK_WRLOCK);
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Read/write locks, header.
|
||||
*/
|
||||
|
||||
#define LUMIERA_RDLOCK_SECTION(flag, handle, rwlock) \
|
||||
RESOURCE_HANDLE (rh_##__LINE__##_); \
|
||||
lumiera_rwlockacquirer lock_##__LINE__##_; \
|
||||
RESOURCE_ENTER (flag, handle, "acquire rwlock (read)", &lock_##__LINE__##_, \
|
||||
NOBUG_RESOURCE_EXCLUSIVE, rh_##__LINE__##_); \
|
||||
for (lumiera_rwlockacquirer_init (&lock_##__LINE__##_, rwlock, LUMIERA_RDLOCKED); \
|
||||
lock_##__LINE__##_.state == LUMIERA_RDLOCKED; \
|
||||
lumiera_rwlockacquirer_unlock (&lock_##__LINE__##_), \
|
||||
({RESOURCE_LEAVE(flag, rh_##__LINE__##_);}))
|
||||
|
||||
#define LUMIERA_WRLOCK_SECTION(flag, handle, rwlock) \
|
||||
RESOURCE_HANDLE (rh_##__LINE__##_); \
|
||||
lumiera_rwlockacquirer lock_##__LINE__##_; \
|
||||
RESOURCE_ENTER (flag, handle, "acquire rwlock (write)", &lock_##__LINE__##_, \
|
||||
NOBUG_RESOURCE_EXCLUSIVE, rh_##__LINE__##_); \
|
||||
for (lumiera_rwlockacquirer_init (&lock_##__LINE__##_, rwlock, LUMIERA_WRLOCKED); \
|
||||
lock_##__LINE__##_.state == LUMIERA_WRLOCKED; \
|
||||
lumiera_rwlockacquirer_unlock (&lock_##__LINE__##_), \
|
||||
({RESOURCE_LEAVE(flag, rh_##__LINE__##_);}))
|
||||
/**
|
||||
* Read locked section.
|
||||
*/
|
||||
#define LUMIERA_RDLOCK_SECTION(nobugflag, rwlck) \
|
||||
for (lumiera_rwlockacquirer NOBUG_CLEANUP(lumiera_rwlockacquirer_ensureunlocked) lumiera_rwlock_section_ = {(LumieraRWLock)1}; \
|
||||
lumiera_rwlock_section_.rwlock;) \
|
||||
for ( \
|
||||
({ \
|
||||
lumiera_rwlock_section_.rwlock = (rwlck); \
|
||||
NOBUG_RESOURCE_HANDLE_INIT (lumiera_rwlock_section_.rh); \
|
||||
RESOURCE_ENTER (nobugflag, (rwlck)->rh, "acquire rwlock for reading", &lumiera_rwlock_section_, \
|
||||
NOBUG_RESOURCE_EXCLUSIVE, lumiera_rwlock_section_.rh); \
|
||||
if (pthread_rwlock_rdlock (&(rwlck)->rwlock)) LUMIERA_DIE (RWLOCK_RDLOCK); \
|
||||
}); \
|
||||
lumiera_rwlock_section_.rwlock; \
|
||||
({ \
|
||||
if (lumiera_rwlock_section_.rwlock) \
|
||||
{ \
|
||||
pthread_rwlock_unlock (&lumiera_rwlock_section_.rwlock->rwlock); \
|
||||
lumiera_rwlock_section_.rwlock = NULL; \
|
||||
RESOURCE_LEAVE(nobugflag, lumiera_rwlock_section_.rh); \
|
||||
} \
|
||||
}))
|
||||
|
||||
|
||||
/**
|
||||
* Write locked section.
|
||||
*/
|
||||
#define LUMIERA_WRLOCK_SECTION(nobugflag, rwlck) \
|
||||
for (lumiera_rwlockacquirer NOBUG_CLEANUP(lumiera_rwlockacquirer_ensureunlocked) lumiera_rwlock_section_ = {(LumieraRWLock)1}; \
|
||||
lumiera_rwlock_section_.rwlock;) \
|
||||
for ( \
|
||||
({ \
|
||||
lumiera_rwlock_section_.rwlock = (rwlck); \
|
||||
NOBUG_RESOURCE_HANDLE_INIT (lumiera_rwlock_section_.rh); \
|
||||
RESOURCE_ENTER (nobugflag, (rwlck)->rh, "acquire rwlock for reading", &lumiera_rwlock_section_, \
|
||||
NOBUG_RESOURCE_EXCLUSIVE, lumiera_rwlock_section_.rh); \
|
||||
if (pthread_rwlock_wrlock (&(rwlck)->rwlock)) LUMIERA_DIE (RWLOCK_WRLOCK); \
|
||||
}); \
|
||||
lumiera_rwlock_section_.rwlock; \
|
||||
({ \
|
||||
if (lumiera_rwlock_section_.rwlock) \
|
||||
{ \
|
||||
pthread_rwlock_unlock (&lumiera_rwlock_section_.rwlock->rwlock); \
|
||||
lumiera_rwlock_section_.rwlock = NULL; \
|
||||
RESOURCE_LEAVE(nobugflag, lumiera_rwlock_section_.rh); \
|
||||
} \
|
||||
}))
|
||||
|
||||
|
||||
/**
|
||||
* RWLock.
|
||||
|
|
@ -70,6 +100,7 @@ for (lumiera_rwlockacquirer_init (&lock_##__LINE__##_, rwlock, LUMIERA_WRLOCKED)
|
|||
struct lumiera_rwlock_struct
|
||||
{
|
||||
pthread_rwlock_t rwlock;
|
||||
RESOURCE_HANDLE (rh);
|
||||
};
|
||||
typedef struct lumiera_rwlock_struct lumiera_rwlock;
|
||||
typedef lumiera_rwlock* LumieraRWLock;
|
||||
|
|
@ -99,7 +130,7 @@ lumiera_rwlock_destroy (LumieraRWLock self);
|
|||
struct lumiera_rwlockacquirer_struct
|
||||
{
|
||||
LumieraRWLock rwlock;
|
||||
enum lumiera_lockstate state;
|
||||
RESOURCE_HANDLE (rh);
|
||||
};
|
||||
typedef struct lumiera_rwlockacquirer_struct lumiera_rwlockacquirer;
|
||||
typedef struct lumiera_rwlockacquirer_struct* LumieraRWLockacquirer;
|
||||
|
|
@ -108,56 +139,15 @@ typedef struct lumiera_rwlockacquirer_struct* LumieraRWLockacquirer;
|
|||
static inline void
|
||||
lumiera_rwlockacquirer_ensureunlocked (LumieraRWLockacquirer self)
|
||||
{
|
||||
ENSURE (self->state == LUMIERA_UNLOCKED, "forgot to unlock the rwlock");
|
||||
}
|
||||
|
||||
/* override with a macro to use the cleanup checker */
|
||||
#define lumiera_rwlockacquirer \
|
||||
lumiera_rwlockacquirer NOBUG_CLEANUP(lumiera_rwlockacquirer_ensureunlocked)
|
||||
|
||||
/**
|
||||
* initialize a rwlockacquirer state
|
||||
* @param self rwlockacquirer to be initialized, must be an automatic variable
|
||||
* @param rwlock associated rwlock
|
||||
* @param state initial state of the mutex, either LUMIERA_RDLOCKED, LUMIERA_WRLOCKED or LUMIERA_UNLOCKED
|
||||
* @return self as given or NULL on error
|
||||
*/
|
||||
LumieraRWLockacquirer
|
||||
lumiera_rwlockacquirer_init (LumieraRWLockacquirer self, LumieraRWLock rwlock, enum lumiera_lockstate state);
|
||||
|
||||
/**
|
||||
* readlock the rwlock.
|
||||
* must not already be locked
|
||||
* @param self rwlockacquirer associated with a rwlock
|
||||
* @return self as given or NULL on error
|
||||
*/
|
||||
LumieraRWLockacquirer
|
||||
lumiera_rwlockacquirer_rdlock (LumieraRWLockacquirer self);
|
||||
|
||||
/**
|
||||
* writelock the rwlock.
|
||||
* must not already be locked
|
||||
* @param self rwlockacquirer associated with a rwlock
|
||||
* @return self as given or NULL on error
|
||||
*/
|
||||
LumieraRWLockacquirer
|
||||
lumiera_rwlockacquirer_wrlock (LumieraRWLockacquirer self);
|
||||
|
||||
|
||||
/**
|
||||
* release rwlock.
|
||||
* a rwlockacquirer must be unlocked before leaving scope
|
||||
* @param self rwlockacquirer associated with a rwlock variable
|
||||
*/
|
||||
static inline void
|
||||
lumiera_rwlockacquirer_unlock (LumieraRWLockacquirer self)
|
||||
{
|
||||
REQUIRE (self);
|
||||
REQUIRE (self->state != LUMIERA_UNLOCKED, "rwlock was not locked");
|
||||
if (pthread_rwlock_unlock (&self->rwlock->rwlock))
|
||||
LUMIERA_DIE (RWLOCK_UNLOCK);
|
||||
self->state = LUMIERA_UNLOCKED;
|
||||
ENSURE (!self->rwlock, "forgot to unlock rwlock");
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
/*
|
||||
// Local Variables:
|
||||
// mode: C
|
||||
// c-file-style: "gnu"
|
||||
// indent-tabs-mode: nil
|
||||
// End:
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -24,3 +24,14 @@ TEST "nested mutex section" nestedmutexsection <<END
|
|||
out: outer mutex locked section
|
||||
out: inner mutex locked section
|
||||
END
|
||||
|
||||
|
||||
TEST "rwlock section" rwlocksection <<END
|
||||
out: write locked section 1
|
||||
out: read locked section 2
|
||||
END
|
||||
|
||||
|
||||
TEST "rwlock not unlocked asserts" rwlockforgotunlock <<END
|
||||
return: 134
|
||||
END
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include "tests/test.h"
|
||||
#include "lib/mutex.h"
|
||||
#include "lib/condition.h"
|
||||
#include "lib/rwlock.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
|
@ -105,5 +106,41 @@ TEST ("nestedmutexsection")
|
|||
|
||||
|
||||
|
||||
TEST ("rwlocksection")
|
||||
{
|
||||
lumiera_rwlock rwlock;
|
||||
lumiera_rwlock_init (&rwlock);
|
||||
RESOURCE_ANNOUNCE (NOBUG_ON, "rwlock", "rwlocksection", &rwlock, rwlock.rh);
|
||||
|
||||
LUMIERA_WRLOCK_SECTION (NOBUG_ON, &rwlock)
|
||||
{
|
||||
printf ("write locked section 1\n");
|
||||
}
|
||||
|
||||
LUMIERA_RDLOCK_SECTION (NOBUG_ON, &rwlock)
|
||||
{
|
||||
printf ("read locked section 2\n");
|
||||
}
|
||||
|
||||
RESOURCE_FORGET (NOBUG_ON, rwlock.rh);
|
||||
lumiera_rwlock_destroy (&rwlock);
|
||||
}
|
||||
|
||||
|
||||
TEST ("rwlockforgotunlock")
|
||||
{
|
||||
lumiera_rwlock rwlock;
|
||||
lumiera_rwlock_init (&rwlock);
|
||||
RESOURCE_ANNOUNCE (NOBUG_ON, "rwlock", "rwlockforgotunlock", &rwlock, rwlock.rh);
|
||||
|
||||
LUMIERA_RDLOCK_SECTION (NOBUG_ON, &rwlock)
|
||||
{
|
||||
break; // LOCK_SECTIONS must not be left by a jump
|
||||
}
|
||||
|
||||
RESOURCE_FORGET (NOBUG_ON, rwlock.rh);
|
||||
lumiera_rwlock_destroy (&rwlock);
|
||||
}
|
||||
|
||||
|
||||
TESTS_END
|
||||
|
|
|
|||
Loading…
Reference in a new issue