diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index 91327cdf5..305f06ee9 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -25,11 +25,14 @@ libcin3_a_SOURCES = \ $(libcin3_a_srcdir)/plugin.c \ $(libcin3_a_srcdir)/error.c \ $(libcin3_a_srcdir)/time.c \ - $(libcin3_a_srcdir)/framerate.c + $(libcin3_a_srcdir)/framerate.c \ + $(libcin3_a_srcdir)/locking.c + noinst_HEADERS += \ $(libcin3_a_srcdir)/plugin.h \ $(libcin3_a_srcdir)/error.h \ $(libcin3_a_srcdir)/time.h \ - $(libcin3_a_srcdir)/framerate.h + $(libcin3_a_srcdir)/framerate.h \ + $(libcin3_a_srcdir)/locking.h diff --git a/src/lib/locking.c b/src/lib/locking.c new file mode 100644 index 000000000..744a083e8 --- /dev/null +++ b/src/lib/locking.c @@ -0,0 +1,52 @@ +/* + locking.c - locking primitives + + 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. +*/ + +#include "lib/locking.h" + +CinelerraCondition +cinelerra_condition_init (CinelerraCondition self) +{ + if (self) + { + pthread_cond_init (&self->cond, NULL); + pthread_mutex_init (&self->mutex, NULL); + } + return self; +} + +CinelerraCondition +cinelerra_condition_destroy (CinelerraCondition self) +{ + if (self) + { + if (pthread_mutex_destroy (&self->mutex)) + CINELERRA_DIE; + else if (pthread_cond_destroy (&self->cond)) + CINELERRA_DIE; + else + return self; + } + return NULL; +} + + + + diff --git a/src/lib/locking.h b/src/lib/locking.h new file mode 100644 index 000000000..067dee822 --- /dev/null +++ b/src/lib/locking.h @@ -0,0 +1,226 @@ +/* + locking.h - locking primitives + + 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_LOCKING_H +#define CINELERRA_LOCKING_H + +#include +#include + +#include "lib/error.h" + +/** + * used to store the current lock state. + * + * + */ +enum cinelerra_lockstate + { + CINELERRA_UNLOCKED, + CINELERRA_LOCKED + }; + + +/** + * Condition variables. + * + */ +struct cinelerra_condition_struct +{ + pthread_cond_t cond; + pthread_mutex_t mutex; +}; +typedef struct cinelerra_condition_struct cinelerra_condition; +typedef cinelerra_condition* CinelerraCondition; + + +/** + * Initialize a condition variable + * @param self is a pointer to the condition variable to be initialized + * @return self as given + */ +CinelerraCondition +cinelerra_condition_init (CinelerraCondition self); + + +/** + * destroy a condition variable + * @param self is a pointer to the condition variable to be initialized + * @return self on success or NULL at error + */ +CinelerraCondition +cinelerra_condition_destroy (CinelerraCondition self); + + +/** + * signal a single waiting thread. + * @param self condition variable to be signaled, must be given, all errors are fatal + */ +static inline void +cinelerra_condition_signal (CinelerraCondition self) +{ + REQUIRE (self); + if (pthread_mutex_lock (&self->mutex)) + CINELERRA_DIE; + pthread_cond_signal (&self->cond); + if (pthread_mutex_unlock (&self->mutex)) + CINELERRA_DIE; +} + +/** + * signal all waiting threads + * @param self condition variable to be signaled, must be given, all errors are fatal + */ +static inline void +cinelerra_condition_broadcast (CinelerraCondition self) +{ + REQUIRE (self); + if (pthread_mutex_lock (&self->mutex)) + CINELERRA_DIE; + pthread_cond_broadcast (&self->cond); + if (pthread_mutex_unlock (&self->mutex)) + CINELERRA_DIE; +} + + + + + +/** + * conditionlock used to manage the state of a condition variable. + */ +struct cinelerra_conditionlock_struct +{ + CinelerraCondition cond; + enum cinelerra_lockstate state; +}; +typedef struct cinelerra_conditionlock_struct cinelerra_conditionlock; +typedef struct cinelerra_conditionlock_struct* CinelerraConditionlock; + +/* helper function for nobug */ +static inline void +cinelerra_conditionlock_ensureunlocked (CinelerraConditionlock self) +{ + ENSURE (self->state == CINELERRA_UNLOCKED, "forgot to unlock the condition mutex"); +} + +/* override with a macro to use the cleanup checker */ +#define cinelerra_conditionlock \ +cinelerra_conditionlock NOBUG_CLEANUP(cinelerra_conditionlock_ensureunlocked) + + +/** + * initialize a conditionlock state + * @param self conditionlock to be initialized, must be an automatic variable + * @param cond associated condition variable + * @param state initial state of the mutex, either CINELERRA_LOCKED or CINELERRA_UNLOCKED + * @return self as given + * errors are fatal + */ +static inline CinelerraConditionlock +cinelerra_conditionlock_init (CinelerraConditionlock self, CinelerraCondition cond, enum cinelerra_lockstate state) +{ + REQUIRE (self); + REQUIRE (cond); + self->cond = cond; + self->state = state; + if (state == CINELERRA_LOCKED) + if (pthread_mutex_lock (&cond->mutex)) + CINELERRA_DIE; + + return self; +} + +/** + * lock the mutex. + * must not already be locked + * @param self conditionlock associated with a condition variable + */ +static inline void +cinelerra_conditionlock_lock (CinelerraConditionlock self) +{ + REQUIRE (self); + REQUIRE (self->state == CINELERRA_UNLOCKED, "mutex already locked"); + + if (pthread_mutex_lock (&self->cond->mutex)) + CINELERRA_DIE; + + self->state = CINELERRA_LOCKED; +} + + +/** + * wait on a locked condition. + * Waits until the condition variable gets signaled from another thread. Must already be locked. + * @param self conditionlock associated with a condition variable + */ +static inline void +cinelerra_conditionlock_wait (CinelerraConditionlock self) +{ + REQUIRE (self); + REQUIRE (self->state == CINELERRA_LOCKED, "mutex must be locked"); + pthread_cond_wait (&self->cond->cond, &self->cond->mutex); +} + + +/** + * release mutex. + * a conditionlock must be unlocked before leaving scope + * @param self conditionlock associated with a condition variable + */ +static inline int +cinelerra_conditionlock_unlock (CinelerraConditionlock self) +{ + REQUIRE (self); + REQUIRE (self->state == CINELERRA_LOCKED, "mutex was not locked"); + if (pthread_mutex_unlock (&self->cond->mutex)) + CINELERRA_DIE; + self->state = CINELERRA_UNLOCKED; +} + + +/** + * signal a single waiting thread + * @param self conditionlock associated with the condition variable to be signaled + */ +static inline void +cinelerra_conditionlock_signal (CinelerraConditionlock self) +{ + REQUIRE (self); + REQUIRE (self->state == CINELERRA_LOCKED, "mutex was not locked"); + pthread_cond_signal (&self->cond->cond); +} + + +/** + * signal all waiting threads + * @param self conditionlock associated with the condition variable to be signaled + */ +static inline int +cinelerra_conditionlock_broadcast (CinelerraConditionlock self) +{ + REQUIRE (self); + REQUIRE (self->state == CINELERRA_LOCKED, "mutex was not locked"); + pthread_cond_broadcast (&self->cond->cond); +} + + +#endif diff --git a/tests/15locking.tests b/tests/15locking.tests new file mode 100644 index 000000000..1ef4938c5 --- /dev/null +++ b/tests/15locking.tests @@ -0,0 +1,8 @@ + +TESTING "Locking" ./test-locking + +TEST "condition not unlocked asserts" conditionforgotunlock <