new locking section macros for RWLocks, old acquirer bites the dust

This commit is contained in:
Christian Thaeter 2008-08-09 13:44:34 +02:00
parent d0b6919eea
commit c11915a4c4
4 changed files with 125 additions and 169 deletions

View file

@ -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:
*/

View file

@ -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:
*/

View file

@ -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

View file

@ -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