diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index 42524b322..46ae62363 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -27,6 +27,7 @@ libcin3_a_SOURCES = \ $(libcin3_a_srcdir)/time.c \ $(libcin3_a_srcdir)/framerate.c \ $(libcin3_a_srcdir)/mutex.c \ + $(libcin3_a_srcdir)/rwlock.c \ $(libcin3_a_srcdir)/condition.c @@ -37,5 +38,6 @@ noinst_HEADERS += \ $(libcin3_a_srcdir)/framerate.h \ $(libcin3_a_srcdir)/locking.h \ $(libcin3_a_srcdir)/mutex.h \ + $(libcin3_a_srcdir)/rwlock.h \ $(libcin3_a_srcdir)/condition.h diff --git a/src/lib/locking.h b/src/lib/locking.h index aa722a568..5c6549f31 100644 --- a/src/lib/locking.h +++ b/src/lib/locking.h @@ -36,8 +36,8 @@ enum cinelerra_lockstate { CINELERRA_UNLOCKED, CINELERRA_LOCKED, - CINELERRA_RLOCKED, - CINELERRA_WLOCKED + CINELERRA_RDLOCKED, + CINELERRA_WRLOCKED }; #endif diff --git a/src/lib/rwlock.c b/src/lib/rwlock.c new file mode 100644 index 000000000..018c69f63 --- /dev/null +++ b/src/lib/rwlock.c @@ -0,0 +1,142 @@ +/* + rwlock.c - read/write locks + + Copyright (C) CinelerraCV + 2007, Christian Thaeter + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#define _GNU_SOURCE + +#include +#include "lib/rwlock.h" + +CINELERRA_ERROR_DEFINE(RWLOCK_AGAIN, "maximum number of readlocks exceed"); +CINELERRA_ERROR_DEFINE(RWLOCK_DEADLOCK, "deadlock detected"); + + +CinelerraRWLock +cinelerra_rwlock_init (CinelerraRWLock self) +{ + if (self) + { + pthread_rwlock_init (&self->rwlock, NULL); + } + return self; +} + +CinelerraRWLock +cinelerra_rwlock_destroy (CinelerraRWLock self) +{ + if (self) + { + if (pthread_rwlock_destroy (&self->rwlock)) + CINELERRA_DIE; + } + return self; +} + + + + +CinelerraRWLockacquirer +cinelerra_rwlockacquirer_init (CinelerraRWLockacquirer self, CinelerraRWLock rwlock, enum cinelerra_lockstate state) +{ + REQUIRE (self); + REQUIRE (rwlock); + REQUIRE (state != CINELERRA_LOCKED, "illegal state for rwlock"); + self->rwlock = rwlock; + self->state = state; + + switch (state) + { + case CINELERRA_RDLOCKED: + switch (pthread_rwlock_rdlock (&rwlock->rwlock)) + { + case 0: + break; + case EAGAIN: + cinelerra_error_set (CINELERRA_ERROR_RWLOCK_AGAIN); + return NULL; + case EDEADLK: + cinelerra_error_set (CINELERRA_ERROR_RWLOCK_DEADLOCK); + return NULL; + default: + CINELERRA_DIE; + } + case CINELERRA_WRLOCKED: + switch (pthread_rwlock_wrlock (&rwlock->rwlock)) + { + case 0: + break; + case EDEADLK: + cinelerra_error_set (CINELERRA_ERROR_RWLOCK_DEADLOCK); + return NULL; + default: + CINELERRA_DIE; + } + default: + break; + } + return self; +} + + +CinelerraRWLockacquirer +cinelerra_rwlockacquirer_rdlock (CinelerraRWLockacquirer self) +{ + REQUIRE (self); + REQUIRE (self->state == CINELERRA_UNLOCKED, "rwlock already locked"); + + switch (pthread_rwlock_rdlock (&self->rwlock->rwlock)) + { + case 0: + break; + case EAGAIN: + cinelerra_error_set (CINELERRA_ERROR_RWLOCK_AGAIN); + return NULL; + case EDEADLK: + cinelerra_error_set (CINELERRA_ERROR_RWLOCK_DEADLOCK); + return NULL; + default: + CINELERRA_DIE; + } + + self->state = CINELERRA_RDLOCKED; + return self; +} + + +CinelerraRWLockacquirer +cinelerra_rwlockacquirer_wrlock (CinelerraRWLockacquirer self) +{ + REQUIRE (self); + REQUIRE (self->state == CINELERRA_UNLOCKED, "rwlock already locked"); + + switch (pthread_rwlock_wrlock (&self->rwlock->rwlock)) + { + case 0: + break; + case EDEADLK: + cinelerra_error_set (CINELERRA_ERROR_RWLOCK_DEADLOCK); + return NULL; + default: + CINELERRA_DIE; + } + + self->state = CINELERRA_WRLOCKED; + return self; +} + diff --git a/src/lib/rwlock.h b/src/lib/rwlock.h new file mode 100644 index 000000000..9bc07f9f3 --- /dev/null +++ b/src/lib/rwlock.h @@ -0,0 +1,139 @@ +/* + rwlock.h - read/write locks + + Copyright (C) CinelerraCV + 2007, Christian Thaeter + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef CINELERRA_RWLOCK_H +#define CINELERRA_RWLOCK_H + +#ifndef _GNU_SOURCE +#error "This header must be included with _GNU_SOURCE or _POSIX_C_SOURCE >= 200112L defined" +#endif + +#include +#include + +#include "lib/locking.h" + +CINELERRA_ERROR_DECLARE(RWLOCK_AGAIN); +CINELERRA_ERROR_DECLARE(RWLOCK_DEADLOCK); + + +/** + * RWLock. + * + */ +struct cinelerra_rwlock_struct +{ + pthread_rwlock_t rwlock; +}; +typedef struct cinelerra_rwlock_struct cinelerra_rwlock; +typedef cinelerra_rwlock* CinelerraRWLock; + + +/** + * Initialize a rwlock + * @param self is a pointer to the rwlock to be initialized + * @return self as given + */ +CinelerraRWLock +cinelerra_rwlock_init (CinelerraRWLock self); + + +/** + * destroy a rwlock + * @param self is a pointer to the rwlock to be initialized + * @return self on success or NULL at error + */ +CinelerraRWLock +cinelerra_rwlock_destroy (CinelerraRWLock self); + + + + +/** + * rwlockacquirer used to manage the state of a rwlock variable. + */ +struct cinelerra_rwlockacquirer_struct +{ + CinelerraRWLock rwlock; + enum cinelerra_lockstate state; +}; +typedef struct cinelerra_rwlockacquirer_struct cinelerra_rwlockacquirer; +typedef struct cinelerra_rwlockacquirer_struct* CinelerraRWLockacquirer; + +/* helper function for nobug */ +static inline void +cinelerra_rwlockacquirer_ensureunlocked (CinelerraRWLockacquirer self) +{ + ENSURE (self->state == CINELERRA_UNLOCKED, "forgot to unlock the rwlock mutex"); +} + +/* override with a macro to use the cleanup checker */ +#define cinelerra_rwlockacquirer \ +cinelerra_rwlockacquirer NOBUG_CLEANUP(cinelerra_rwlockacquirer_ensureunlocked) + + +/** + * initialize a rwlockacquirer state + * @param self rwlockacquirer to be initialized, must be an automatic variable + * @param cond associated rwlock + * @param state initial state of the mutex, either CINELERRA_RDLOCKED, CINELERRA_WRLOCKED or CINELERRA_UNLOCKED + * @return self as given or NULL on error + */ +CinelerraRWLockacquirer +cinelerra_rwlockacquirer_init (CinelerraRWLockacquirer self, CinelerraRWLock rwlock, enum cinelerra_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 + */ +CinelerraRWLockacquirer +cinelerra_rwlockacquirer_rdlock (CinelerraRWLockacquirer self); + + +/** + * writelock the rwlock. + * must not already be locked + * @param self rwlockacquirer associated with a rwlock + * @return self as given or NULL on error + */ +CinelerraRWLockacquirer +cinelerra_rwlockacquirer_wrlock (CinelerraRWLockacquirer self); + + +/** + * release rwlock. + * a rwlockacquirer must be unlocked before leaving scope + * @param self rwlockacquirer associated with a rwlock variable + */ +static inline void +cinelerra_rwlockacquirer_unlock (CinelerraRWLockacquirer self) +{ + REQUIRE (self); + REQUIRE (self->state != CINELERRA_UNLOCKED, "rwlock was not locked"); + if (pthread_rwlock_unlock (&self->rwlock->rwlock)) + CINELERRA_DIE; + self->state = CINELERRA_UNLOCKED; +} + + +#endif