diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am index c2161cc82..5729d910b 100644 --- a/src/backend/Makefile.am +++ b/src/backend/Makefile.am @@ -19,7 +19,7 @@ liblumierabackend_la_srcdir = $(top_srcdir)/src/backend lib_LTLIBRARIES += liblumierabackend.la liblumierabackend_la_CPPFLAGS = $(AM_CPPFLAGS) -liblumierabackend_la_CFLAGS = $(AM_CFLAGS) -std=gnu99 -Wextra -Wall -Werror +liblumierabackend_la_CFLAGS = $(AM_CFLAGS) -std=gnu99 -Wextra -Wall -Werror liblumierabackend_la_LIBADD = liblumiera.la liblumierabackend_la_SOURCES = \ @@ -29,10 +29,10 @@ liblumierabackend_la_SOURCES = \ $(liblumierabackend_la_srcdir)/file.c \ $(liblumierabackend_la_srcdir)/filehandle.c \ $(liblumierabackend_la_srcdir)/filedescriptor.c \ + $(liblumierabackend_la_srcdir)/filehandlecache.c \ $(liblumierabackend_la_srcdir)/mmap.c \ $(liblumierabackend_la_srcdir)/mmapings.c \ $(liblumierabackend_la_srcdir)/mmapcache.c \ - $(liblumierabackend_la_srcdir)/filehandlecache.c \ $(liblumierabackend_la_srcdir)/enginefacade.cpp \ $(liblumierabackend_la_srcdir)/scriptrunnerfacade.cpp \ $(liblumierabackend_la_srcdir)/netnodefacade.cpp diff --git a/src/backend/thread-wrapper.hpp b/src/backend/thread-wrapper.hpp index e7e83ca57..8a76cb5cf 100644 --- a/src/backend/thread-wrapper.hpp +++ b/src/backend/thread-wrapper.hpp @@ -42,6 +42,7 @@ namespace backend { using std::tr1::function; using lumiera::Literal; using lib::Sync; + using lib::RecursiveLock_Waitable; using lib::NonrecursiveLock_Waitable; typedef struct nobug_flag* NoBugFlag; @@ -65,17 +66,17 @@ namespace backend { * details of the thread handling and didn't implement the waiting feature. */ class JoinHandle - : public Sync - , Sync::Lock + : public Sync + , Sync::Lock { - typedef Sync SyncBase; + typedef Sync SyncBase; bool isWaiting_; volatile bool armed_; friend class Thread; - LumieraCondition + LumieraReccondition accessLockedCondition() { ASSERT (!armed_, "Lifecycle error, JoinHandle used for several threads."); @@ -171,7 +172,7 @@ namespace backend { void - start_thread (lumiera_thread_class kind, Literal& purpose, NoBugFlag logging_flag, LumieraCondition joinCond=0) + start_thread (lumiera_thread_class kind, Literal& purpose, NoBugFlag logging_flag, LumieraReccondition joinCond=0) { Lock sync(this); LumieraThread res = diff --git a/src/backend/threads.c b/src/backend/threads.c index 794d13b79..67f2d4aa4 100644 --- a/src/backend/threads.c +++ b/src/backend/threads.c @@ -21,6 +21,7 @@ //TODO: Support library includes// +#include "include/logging.h" //TODO: Lumiera header includes// #include "threads.h" @@ -41,8 +42,33 @@ //code goes here// -/* really dumb mutex, to make the mockup little less brittle */ -static pthread_mutex_t threads_mutex = PTHREAD_MUTEX_INITIALIZER; + +struct lumiera_thread_mockup +{ + void (*fn)(void*); + void* arg; + LumieraReccondition finished; +}; + + +static void* pthread_runner (void* thread) +{ + pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, NULL); + + struct lumiera_thread_mockup* starter = (struct lumiera_thread_mockup*) thread; + LumieraReccondition thread_end_notification = starter->finished; + + starter->fn (starter->arg); + + if (!thread_end_notification) + return NULL; // no signalling of thread termination desired + + LUMIERA_RECCONDITION_SECTION(cond_sync, thread_end_notification) + LUMIERA_RECCONDITION_BROADCAST; + + return NULL; +} + static pthread_once_t attr_once = PTHREAD_ONCE_INIT; static pthread_attr_t attrs; @@ -51,42 +77,22 @@ static void thread_attr_init (void) { pthread_attr_init (&attrs); pthread_attr_setdetachstate (&attrs, PTHREAD_CREATE_DETACHED); - pthread_attr_setdetachstate (&attrs, PTHREAD_CREATE_DETACHED); + //cancel ... } -struct lumiera_thread_mockup -{ - void (*fn)(void*); - void* arg; -}; - - -static void* pthread_runner (void* thread) -{ - struct lumiera_thread_mockup* starter = (struct lumiera_thread_mockup*) thread; - (void) starter; - pthread_mutex_lock (&threads_mutex); - pthread_mutex_unlock (&threads_mutex); - starter->fn (starter->arg); - return NULL; -} - - + LumieraThread lumiera_thread_run (enum lumiera_thread_class kind, void (*start_routine)(void *), void * arg, - LumieraCondition finished, + LumieraReccondition finished, const char* purpose, struct nobug_flag* flag) { (void) kind; - (void) finished; (void) purpose; (void) flag; - REQUIRE (!finished, "Condition variable for finish notification not yet implemented"); - if (attr_once == PTHREAD_ONCE_INIT) pthread_once (&attr_once, thread_attr_init); @@ -94,14 +100,11 @@ lumiera_thread_run (enum lumiera_thread_class kind, thread.fn = start_routine; thread.arg = arg; - - pthread_mutex_lock (&threads_mutex); + thread.finished = finished; pthread_t dummy; int error = pthread_create (&dummy, &attrs, pthread_runner, &thread); - pthread_mutex_unlock (&threads_mutex); - if (error) return 0; /////TODO temporary addition by Ichthyo; probably we'll set lumiera_error? return (LumieraThread) 1; } diff --git a/src/backend/threads.h b/src/backend/threads.h index 50786c729..9d420fa2f 100644 --- a/src/backend/threads.h +++ b/src/backend/threads.h @@ -23,7 +23,7 @@ #define LUMIERA_THREADS_H //TODO: Support library includes// -#include "lib/condition.h" +#include "lib/reccondition.h" //TODO: Forward declarations// @@ -96,7 +96,7 @@ LumieraThread lumiera_thread_run (enum lumiera_thread_class kind, void (*start_routine)(void *), void * arg, - LumieraCondition finished, + LumieraReccondition finished, const char* purpose, struct nobug_flag* flag); diff --git a/src/common/interface.c b/src/common/interface.c index 1853c7af3..a8d91c40f 100644 --- a/src/common/interface.c +++ b/src/common/interface.c @@ -21,7 +21,7 @@ #include "include/logging.h" -#include "lib/mutex.h" +#include "lib/recmutex.h" #include "lib/safeclib.h" #include "common/plugin.h" diff --git a/src/common/interfaceregistry.c b/src/common/interfaceregistry.c index ad8bd8224..3b67e77e1 100644 --- a/src/common/interfaceregistry.c +++ b/src/common/interfaceregistry.c @@ -20,7 +20,6 @@ */ #include "include/logging.h" -#include "lib/mutex.h" #include "lib/error.h" #include "lib/psplay.h" #include "lib/safeclib.h" @@ -47,7 +46,7 @@ PSplay lumiera_interfaceregistry; PSplay lumiera_pluginregistry; -lumiera_mutex lumiera_interface_mutex; +lumiera_recmutex lumiera_interface_mutex; static int lumiera_interface_cmp_fn (const void* keya, const void* keyb); @@ -126,7 +125,7 @@ lumiera_interfaceregistry_destroy (void) psplay_delete (lumiera_pluginregistry); lumiera_pluginregistry = NULL; - lumiera_mutex_destroy (&lumiera_interface_mutex, &NOBUG_FLAG(mutex_dbg)); + lumiera_recmutex_destroy (&lumiera_interface_mutex, &NOBUG_FLAG(mutex_dbg)); REQUIRE (!psplay_nelements (lumiera_interfaceregistry), "some interfaces still registered at shutdown"); diff --git a/src/common/interfaceregistry.h b/src/common/interfaceregistry.h index 8adc46313..bc4f11440 100644 --- a/src/common/interfaceregistry.h +++ b/src/common/interfaceregistry.h @@ -22,6 +22,7 @@ #define LUMIERA_INTERFACEREGISTRY_H #include "lib/mutex.h" +#include "lib/recmutex.h" #include "lib/psplay.h" #include "common/interface.h" @@ -42,7 +43,7 @@ //NOBUG_DECLARE_FLAG (interface); extern PSplay lumiera_interfaceregistry; -extern lumiera_mutex lumiera_interface_mutex; +extern lumiera_recmutex lumiera_interface_mutex; /** @@ -75,8 +76,6 @@ struct lumiera_interfacenode_struct LumieraInterfacenode* deps; }; -extern lumiera_mutex lumiera_interface_mutex; - /** * Initialize the interface registry diff --git a/src/common/plugin.c b/src/common/plugin.c index 1d24f9c5e..2ddd58998 100644 --- a/src/common/plugin.c +++ b/src/common/plugin.c @@ -22,7 +22,7 @@ #include "include/logging.h" #include "lib/safeclib.h" #include "lib/psplay.h" -#include "lib/mutex.h" +#include "lib/recmutex.h" #include "lib/error.h" #include "common/interfaceregistry.h" diff --git a/src/gui/workspace/workspace-window.cpp b/src/gui/workspace/workspace-window.cpp index 12d6506bc..b9db9b5d5 100644 --- a/src/gui/workspace/workspace-window.cpp +++ b/src/gui/workspace/workspace-window.cpp @@ -56,6 +56,7 @@ WorkspaceWindow::WorkspaceWindow(Project &source_project, WorkspaceWindow::~WorkspaceWindow() { + INFO (gui_dbg, "closing workspace window..."); } Project& diff --git a/src/include/logging.h b/src/include/logging.h index 202836c08..4d674bc03 100644 --- a/src/include/logging.h +++ b/src/include/logging.h @@ -89,6 +89,7 @@ NOBUG_CPP_DEFINE_FLAG_PARENT ( proc_dbg, debugging); NOBUG_CPP_DEFINE_FLAG_PARENT ( gui_dbg, debugging); /** base if debug logging for the support library */ NOBUG_CPP_DEFINE_FLAG_PARENT ( library_dbg, debugging); +NOBUG_CPP_DEFINE_FLAG_PARENT ( mpool_dbg, library_dbg); NOBUG_CPP_DEFINE_FLAG_PARENT ( psplay_dbg, library_dbg); NOBUG_CPP_DEFINE_FLAG_PARENT ( resourcecollector_dbg, library_dbg); NOBUG_CPP_DEFINE_FLAG_PARENT ( mutex_dbg, library_dbg); diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index 8e5b1b8ae..4fa99136d 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -1,5 +1,5 @@ # Copyright (C) Lumiera.org -# 2007, Christian Thaeter +# 2007, 2008, 2009 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 @@ -23,8 +23,10 @@ liblumiera_la_CXXFLAGS = $(AM_CXXFLAGS) -Wall -Wextra liblumiera_la_SOURCES = \ $(liblumiera_la_srcdir)/error.c \ + $(liblumiera_la_srcdir)/mpool.c \ $(liblumiera_la_srcdir)/exception.cpp \ $(liblumiera_la_srcdir)/mutex.c \ + $(liblumiera_la_srcdir)/recmutex.c \ $(liblumiera_la_srcdir)/rwlock.c \ $(liblumiera_la_srcdir)/condition.c \ $(liblumiera_la_srcdir)/reccondition.c \ @@ -51,7 +53,9 @@ liblumiera_la_SOURCES = \ noinst_HEADERS += \ $(liblumiera_la_srcdir)/error.h \ $(liblumiera_la_srcdir)/error.hpp \ + $(liblumiera_la_srcdir)/mpool.h \ $(liblumiera_la_srcdir)/mutex.h \ + $(liblumiera_la_srcdir)/recmutex.h \ $(liblumiera_la_srcdir)/rwlock.h \ $(liblumiera_la_srcdir)/condition.h \ $(liblumiera_la_srcdir)/reccondition.h \ diff --git a/src/lib/condition.c b/src/lib/condition.c index a7f17e57b..9db79c54f 100644 --- a/src/lib/condition.c +++ b/src/lib/condition.c @@ -2,7 +2,7 @@ condition.c - condition variable Copyright (C) Lumiera.org - 2008, Christian Thaeter + 2008, 2009 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 @@ -26,8 +26,6 @@ * Condition variables */ -LUMIERA_ERROR_DEFINE (CONDITION_DESTROY, "condition destroy failed"); - LumieraCondition lumiera_condition_init (LumieraCondition self, const char* purpose, struct nobug_flag* flag) @@ -35,7 +33,7 @@ lumiera_condition_init (LumieraCondition self, const char* purpose, struct nobug if (self) { pthread_cond_init (&self->cond, NULL); - pthread_mutex_init (&self->mutex, NULL); + pthread_mutex_init (&self->cndmutex, NULL); NOBUG_RESOURCE_HANDLE_INIT (self->rh); NOBUG_RESOURCE_ANNOUNCE_RAW (flag, "cond_var", purpose, self, self->rh); } @@ -50,15 +48,21 @@ lumiera_condition_destroy (LumieraCondition self, struct nobug_flag* flag) { NOBUG_RESOURCE_FORGET_RAW (flag, self->rh); - if (pthread_mutex_destroy (&self->mutex)) - LUMIERA_DIE (MUTEX_DESTROY); + if (pthread_mutex_destroy (&self->cndmutex)) + LUMIERA_DIE (LOCK_DESTROY); if (pthread_cond_destroy (&self->cond)) - LUMIERA_DIE (CONDITION_DESTROY); + LUMIERA_DIE (LOCK_DESTROY); } return self; } +int lumiera_condition_unlock_cb (void* cond) +{ + return pthread_mutex_unlock (&((LumieraCondition)cond)->cndmutex); +} + + diff --git a/src/lib/condition.h b/src/lib/condition.h index 328d07c49..a2e02cebd 100644 --- a/src/lib/condition.h +++ b/src/lib/condition.h @@ -23,58 +23,73 @@ #define LUMIERA_CONDITION_H #include "lib/error.h" -#include "lib/mutex.h" +#include "lib/sectionlock.h" +#include +#include /** * @file * Condition variables, header */ -LUMIERA_ERROR_DECLARE (CONDITION_DESTROY); - - /** * Condition section. * Locks the condition mutex, one can use LUMIERA_CONDITION_WAIT to wait for signals or * LUMIERA_CONDITION_SIGNAL or LUMIERA_CONDITION_BROADCAST to wake waiting threads + * Condition variables must be at the end of locking chains, they can not be used at + * intermediate position. * @param nobugflag NoBug flag used to log actions on the condition * @param cnd Condition variable to be locked */ -#define LUMIERA_CONDITION_SECTION(nobugflag, cnd) \ - for (lumiera_conditionacquirer NOBUG_CLEANUP(lumiera_conditionacquirer_ensureunlocked) \ - lumiera_condition_section_ = {(LumieraCondition)1}; \ - lumiera_condition_section_.condition;) \ - for ( \ - ({ \ - lumiera_condition_section_.condition = (cnd); \ - NOBUG_IF(NOBUG_MODE_ALPHA, lumiera_condition_section_.flag = &NOBUG_FLAG(nobugflag)); \ - NOBUG_RESOURCE_HANDLE_INIT (lumiera_condition_section_.rh); \ - RESOURCE_ENTER (nobugflag, (cnd)->rh, "acquire condition", &lumiera_condition_section_, \ - NOBUG_RESOURCE_EXCLUSIVE, lumiera_condition_section_.rh); \ - if (pthread_mutex_lock (&(cnd)->mutex)) LUMIERA_DIE (MUTEX_LOCK); \ - }); \ - lumiera_condition_section_.condition; \ - ({ \ - LUMIERA_CONDITION_UNLOCK \ +#define LUMIERA_CONDITION_SECTION(nobugflag, cnd) \ + for (lumiera_sectionlock NOBUG_CLEANUP(lumiera_sectionlock_ensureunlocked) \ + lumiera_cond_section_ = { \ + (void*)1, lumiera_condition_unlock_cb NOBUG_ALPHA_COMMA_NULL NOBUG_ALPHA_COMMA_NULL}; \ + lumiera_cond_section_.lock;) \ + for ( \ + ({ \ + lumiera_cond_section_.lock = (cnd); \ + NOBUG_IF_ALPHA(lumiera_cond_section_.flag = &NOBUG_FLAG(nobugflag);) \ + RESOURCE_ENTER (nobugflag, (cnd)->rh, "acquire condmutex", &lumiera_cond_section_, \ + NOBUG_RESOURCE_WAITING, lumiera_cond_section_.rh); \ + if (pthread_mutex_lock (&(cnd)->cndmutex)) \ + LUMIERA_DIE (LOCK_ACQUIRE); \ + RESOURCE_STATE (nobugflag, NOBUG_RESOURCE_EXCLUSIVE, lumiera_cond_section_.rh); \ + }); \ + lumiera_cond_section_.lock; \ + ({ \ + LUMIERA_CONDITION_SECTION_UNLOCK; \ })) -/** - * Explicit mutex unlock for a condition variable - * One can early unlock the mutex of a condition variable prior leaving a CONDITION_SECTION. - * The CONDITION_WAIT, CONDITION_SIGNAL and CONDITION_BROADCAST macros must not be used after the mutex - * got unlocked. - * @param nobugflag NoBug flag used to log actions on the condition - */ -#define LUMIERA_CONDITION_UNLOCK \ - if (lumiera_condition_section_.condition) \ - { \ - pthread_mutex_unlock (&lumiera_condition_section_.condition->mutex); \ - lumiera_condition_section_.condition = NULL; \ - NOBUG_RESOURCE_LEAVE_RAW(lumiera_condition_section_.flag, lumiera_condition_section_.rh); \ - } +#define LUMIERA_CONDITION_SECTION_CHAIN(nobugflag, cnd) \ + for (lumiera_sectionlock *lumiera_lock_section_old_ = &lumiera_lock_section_, \ + NOBUG_CLEANUP(lumiera_sectionlock_ensureunlocked) lumiera_cond_section_ = { \ + (void*)1, lumiera_condition_unlock_cb NOBUG_ALPHA_COMMA_NULL NOBUG_ALPHA_COMMA_NULL}; \ + lumiera_cond_section_.lock;) \ + for ( \ + ({ \ + REQUIRE (lumiera_lock_section_old_->lock, "section prematurely unlocked"); \ + lumiera_cond_section_.lock = cnd; \ + NOBUG_IF_ALPHA(lumiera_cond_section_.flag = &NOBUG_FLAG(nobugflag);) \ + RESOURCE_ENTER (nobugflag, (cnd)->rh, "acquire condmutex", &lumiera_cond_section_, \ + NOBUG_RESOURCE_WAITING, lumiera_cond_section_.rh); \ + if (pthread_mutex_lock (&(cnd)->cndmutex)) \ + LUMIERA_DIE (LOCK_ACQUIRE); \ + RESOURCE_STATE (nobugflag, NOBUG_RESOURCE_EXCLUSIVE, lumiera_cond_section_.rh); \ + LUMIERA_SECTION_UNLOCK_(lumiera_lock_section_old_); \ + }); \ + lumiera_cond_section_.lock; \ + ({ \ + LUMIERA_CONDITION_SECTION_UNLOCK; \ + })) + + + +#define LUMIERA_CONDITION_SECTION_UNLOCK \ + LUMIERA_SECTION_UNLOCK_(&lumiera_cond_section_) /** @@ -82,12 +97,13 @@ LUMIERA_ERROR_DECLARE (CONDITION_DESTROY); * Must be used inside a CONDITION_SECTION. * @param expr Conditon which must become true, else the condition variable goes back into sleep */ -#define LUMIERA_CONDITION_WAIT(expr) \ - do { \ - ENSURE (lumiera_condition_section_.condition, "Condition mutex not locked"); \ - NOBUG_RESOURCE_STATE_RAW (lumiera_condition_section_.flag, lumiera_condition_section_.rh, NOBUG_RESOURCE_WAITING); \ - pthread_cond_wait (&lumiera_condition_section_.condition->cond, &lumiera_condition_section_.condition->mutex); \ - NOBUG_RESOURCE_STATE_RAW (lumiera_condition_section_.flag, lumiera_condition_section_.rh, NOBUG_RESOURCE_EXCLUSIVE);\ +#define LUMIERA_CONDITION_WAIT(expr) \ + do { \ + REQUIRE (lumiera_cond_section_.lock, "Condition mutex not locked"); \ + NOBUG_RESOURCE_STATE_RAW (lumiera_cond_section_.flag, NOBUG_RESOURCE_WAITING, lumiera_cond_section_.rh); \ + pthread_cond_wait (&((LumieraCondition)lumiera_cond_section_.lock)->cond, \ + &((LumieraCondition)lumiera_cond_section_.lock)->cndmutex); \ + NOBUG_RESOURCE_STATE_RAW (lumiera_cond_section_.flag, NOBUG_RESOURCE_EXCLUSIVE, lumiera_cond_section_.rh); \ } while (!(expr)) @@ -96,12 +112,12 @@ LUMIERA_ERROR_DECLARE (CONDITION_DESTROY); * Must be used inside a CONDITION_SECTION. * Wakes one thread waiting on the condition variable */ -#define LUMIERA_CONDITION_SIGNAL \ -do { \ - ENSURE (lumiera_condition_section_.condition, "Condition mutex not locked"); \ - NOBUG_IF(NOBUG_MODE_ALPHA, TRACE(lumiera_condition_section_.flag, "Signaling")); \ - pthread_cond_signal (&lumiera_condition_section_.condition->cond); \ -} while (0) +#define LUMIERA_CONDITION_SIGNAL \ + do { \ + REQUIRE (lumiera_cond_section_.lock, "Condition mutex not locked"); \ + TRACE(NOBUG_FLAG_RAW(lumiera_cond_section_.flag), "Signal %p", &lumiera_cond_section_); \ + pthread_cond_signal (&((LumieraCondition)lumiera_cond_section_.lock)->cond); \ + } while (0) /** @@ -109,13 +125,12 @@ do { * Must be used inside a CONDITION_SECTION. * Wakes all threads waiting on the condition variable */ -#define LUMIERA_CONDITION_BROADCAST \ -do { \ - ENSURE (lumiera_condition_section_.condition, "Condition mutex not locked"); \ - NOBUG_IF(NOBUG_MODE_ALPHA, TRACE(lumiera_condition_section_.flag, "Broadcasting")); \ - pthread_cond_broadcast (&lumiera_condition_section_.condition->cond); \ -} while (0) - +#define LUMIERA_CONDITION_BROADCAST \ + do { \ + REQUIRE (lumiera_cond_section_.lock, "Condition mutex not locked"); \ + TRACE(NOBUG_FLAG_RAW(lumiera_cond_section_.flag), "Broadcast %p", &lumiera_cond_section_); \ + pthread_cond_broadcast (&((LumieraCondition)lumiera_cond_section_.lock)->cond); \ + } while (0) /** @@ -125,7 +140,7 @@ do { struct lumiera_condition_struct { pthread_cond_t cond; - pthread_mutex_t mutex; + pthread_mutex_t cndmutex; RESOURCE_HANDLE (rh); }; typedef struct lumiera_condition_struct lumiera_condition; @@ -149,26 +164,8 @@ lumiera_condition_init (LumieraCondition self, const char* purpose, struct nobug LumieraCondition lumiera_condition_destroy (LumieraCondition self, struct nobug_flag* flag); - -/** - * conditionacquirer used to manage the state of a condition variable. - */ -struct lumiera_conditionacquirer_struct -{ - LumieraCondition condition; - NOBUG_IF(NOBUG_MODE_ALPHA, struct nobug_flag* flag); - RESOURCE_HANDLE (rh); -}; -typedef struct lumiera_conditionacquirer_struct lumiera_conditionacquirer; -typedef struct lumiera_conditionacquirer_struct* LumieraConditionacquirer; - - -/* helper function for nobug */ -static inline void -lumiera_conditionacquirer_ensureunlocked (LumieraConditionacquirer self) -{ - ENSURE (!self->condition, "forgot to unlock condition variable"); -} +int +lumiera_condition_unlock_cb (void* cond); #endif diff --git a/src/lib/llist.h b/src/lib/llist.h index 25a9dca39..d6511ebaf 100644 --- a/src/lib/llist.h +++ b/src/lib/llist.h @@ -193,8 +193,8 @@ typedef llist ** LList_ref; * before any other operation on them is called. * @param self node to be initialized */ -LLIST_FUNC (void llist_init (LList self), - self->next = self->prev = self; +LLIST_FUNC (LList llist_init (LList self), + return self->next = self->prev = self; ); /** @@ -533,15 +533,26 @@ LLIST_FUNC (LList llist_get_nth_stop (LList self, int n, const_LList stop), ); +/** + * The comparsion function function type. + * certain sort and find functions depend on a user supplied coparsion function + * @param a first operand for the comparsion + * @param b second operand for the comparsion + * @param extra user supplied data which passed through + * @return shall return a value less than zero, zero, biggier than zero when + * a is less than, equal to, biggier than b + */ +typedef int (*llist_cmpfn)(const_LList a, const_LList b, void* extra); + + /** * Sort a list. * recursive mergesort, needs much extra stackspace (crappy implementation yet) but it reasonable fast * @param self list to be sorted - * @param cmp function takeing 2 LLists + * @param cmp function for comparing 2 nodes + * @param extra generic data passed to the cmp function */ -typedef int (*llist_cmpfn)(const_LList a, const_LList b); - -LLIST_FUNC (LList llist_sort (LList self, llist_cmpfn cmp), +LLIST_FUNC (LList llist_sort (LList self, llist_cmpfn cmp, void* extra), llist left; llist right; llist_init (&left); @@ -552,11 +563,11 @@ LLIST_FUNC (LList llist_sort (LList self, llist_cmpfn cmp), LLIST_WHILE_HEAD (self, head) llist_insert_prev (++n & 1 ? &left : &right, head); - llist_sort (&left, cmp); - llist_sort (&right, cmp); + llist_sort (&left, cmp, extra); + llist_sort (&right, cmp, extra); while (!llist_is_empty (&left) && !llist_is_empty (&right)) - llist_insert_prev (self, cmp (left.next, right.next) < 0 ? left.next : right.next); + llist_insert_prev (self, cmp (left.next, right.next, extra) < 0 ? left.next : right.next); if (!llist_is_empty (&left)) llist_insertlist_prev (self, &left); @@ -573,13 +584,13 @@ LLIST_FUNC (LList llist_sort (LList self, llist_cmpfn cmp), * Does not change the list order. * @param self list to be searched * @param templ template for the element being searched - * @param cmp function taking 2 LLists - * @return pointer to the found LList element or NULL if nothing foound + * @param cmp function for comparing 2 nodes + * @return pointer to the found LList element or NULL if nothing found */ -LLIST_FUNC (LList llist_find (const_LList self, const_LList templ, llist_cmpfn cmp), +LLIST_FUNC (LList llist_find (const_LList self, const_LList templ, llist_cmpfn cmp, void* extra), LLIST_FOREACH(self, node) { - if (!cmp (node, templ)) + if (!cmp (node, templ, extra)) return node; } return NULL; @@ -592,15 +603,16 @@ LLIST_FUNC (LList llist_find (const_LList self, const_LList templ, llist_cmpfn c * Useful if the order of the list is not required and few elements are frequently searched. * @param self list to be searched * @param templ template for the element being searched - * @param cmp function taking 2 LLists - * @return pointer to the found LList element (head) or NULL if nothing foound + * @param cmp function for comparing 2 nodes + * @return pointer to the found LList element (head) or NULL if nothing found */ -LLIST_FUNC (LList llist_ufind (LList self, const_LList templ, llist_cmpfn cmp), +LLIST_FUNC (LList llist_ufind (LList self, const_LList templ, llist_cmpfn cmp, void* extra), LLIST_FOREACH(self, node) { - if (!cmp (node, templ)) + if (!cmp (node, templ, extra)) { - llist_insert_next (self, node); + if (llist_next(self) != node) + llist_insert_next (self, node); return node; } } @@ -614,13 +626,13 @@ LLIST_FUNC (LList llist_ufind (LList self, const_LList templ, llist_cmpfn cmp), * biggier than the searched one. * @param self list to be searched * @param templ template for the element being searched - * @param cmp function takeing 2 LLists + * @param cmp function for comparing 2 nodes * @return pointer to the found LList element or NULL if nothing foound */ -LLIST_FUNC (LList llist_sfind (const_LList self, const_LList templ, llist_cmpfn cmp), +LLIST_FUNC (LList llist_sfind (const_LList self, const_LList templ, llist_cmpfn cmp, void* extra), LLIST_FOREACH(self, node) { - int c = cmp (node, templ); + int c = cmp (node, templ, extra); if (!c) return node; else if (c>0) diff --git a/src/lib/mpool.c b/src/lib/mpool.c new file mode 100644 index 000000000..88a2a8be2 --- /dev/null +++ b/src/lib/mpool.c @@ -0,0 +1,518 @@ +/* + mpool.c - memory pool for constant sized objects + + Copyright (C) Lumiera.org + 2009, 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 +#include +#include +#include + +#include "mpool.h" + + + +#if UINTPTR_MAX > 4294967295U /* 64 bit */ +#define MPOOL_DIV_SHIFT 6 +#define MPOOL_C(c) c ## ULL +#else /* 32 bit */ +#define MPOOL_DIV_SHIFT 5 +#define MPOOL_C(c) c ## UL +#endif + +/* + Cluster and node structures are private +*/ + +typedef struct mpoolcluster_struct mpoolcluster; +typedef mpoolcluster* MPoolcluster; +typedef const mpoolcluster* const_MPoolcluster; + +struct mpoolcluster_struct +{ + llist node; /* all clusters */ + void* data[]; /* bitmap and elements */ +}; + + +typedef struct mpoolnode_struct mpoolnode; +typedef mpoolnode* MPoolnode; +typedef const mpoolnode* const_MPoolnode; + +struct mpoolnode_struct +{ + llist node; +}; + + +MPool +mpool_cluster_alloc_ (MPool self); + + +#define MPOOL_BITMAP_SIZE(elements_per_cluster) \ + (((elements_per_cluster) + sizeof(uintptr_t)*CHAR_BIT - 1) \ + / (sizeof(uintptr_t) * CHAR_BIT) * sizeof (uintptr_t)) + +MPool +mpool_init (MPool self, size_t elem_size, unsigned elements_per_cluster, mpool_destroy_fn dtor) +{ + TRACE (mpool_dbg, "%p: elem_size %zd: elem_per_cluster %u", self, elem_size, elements_per_cluster); + + if (self) + { + llist_init (&self->freelist); + llist_init (&self->clusters); + self->elem_size = (elem_size+sizeof(void*)-1) / sizeof(void*) * sizeof(void*); /* void* aligned */ + + /* minimum size is the size of a llist node */ + if (self->elem_size < sizeof(llist)) + self->elem_size = sizeof(llist); + + self->elements_per_cluster = elements_per_cluster; + + self->cluster_size = sizeof (mpoolcluster) + /* header */ + MPOOL_BITMAP_SIZE (self->elements_per_cluster) + /* bitmap */ + self->elem_size * self->elements_per_cluster; /* elements */ + + self->elements_free = 0; + self->destroy = dtor; + self->locality = NULL; + } + + return self; +} + + +static inline void* +cluster_element_get (MPoolcluster cluster, MPool self, unsigned n) +{ + return (void*)cluster + /* start address */ + sizeof (*cluster) + /* header */ + MPOOL_BITMAP_SIZE (self->elements_per_cluster) + /* bitmap */ + self->elem_size * n; /* offset*/ +} + + +static inline bool +bitmap_bit_get_nth (MPoolcluster cluster, unsigned index) +{ + TRACE (mpool_dbg, "cluster %p: index %u", cluster, index); + + uintptr_t quot = index>>MPOOL_DIV_SHIFT; + uintptr_t rem = index & ~((~MPOOL_C(0))<data; + + return bitmap[quot] & ((uintptr_t)1<clusters, cluster) + { + if (self->destroy) + for (unsigned i = 0; i < self->elements_per_cluster; ++i) + { + if (bitmap_bit_get_nth ((MPoolcluster)cluster, i)) + { + void* obj = cluster_element_get ((MPoolcluster)cluster, self, i); + TRACE (mpool_dbg, "dtor: cluster %p: obj %p: freelist %p", cluster, obj, self->freelist); + self->destroy (obj); + } + } + + llist_unlink_fast_ (cluster); + TRACE (mpool_dbg, "freeing cluster %p" , cluster); + free (cluster); + } + + llist_init (&self->freelist); + self->elements_free = 0; + self->locality = NULL; + } + + return self; +} + + + +MPool +mpool_cluster_alloc_ (MPool self) +{ + MPoolcluster cluster = malloc (self->cluster_size); + TRACE (mpool_dbg, "%p", cluster); + + if (!cluster) + return NULL; + + /* clear the bitmap */ + memset (&cluster->data, 0, MPOOL_BITMAP_SIZE (self->elements_per_cluster)); + + /* initialize freelist */ + for (unsigned i = 0; i < self->elements_per_cluster; ++i) + { + MPoolnode node = cluster_element_get (cluster, self, i); + TRACE (mpool_dbg, "node %p", node); + llist_insert_tail (&self->freelist, llist_init (&node->node)); + } + + /* we insert the cluster at head because its likely be used next */ + llist_insert_head (&self->clusters, llist_init (&cluster->node)); + self->elements_free += self->elements_per_cluster; + + return self; +} + + +static int +cmp_cluster_contains_element (const_LList cluster, const_LList element, void* cluster_size) +{ + if (element < cluster) + return -1; + + if ((void*)element > (void*)cluster + (uintptr_t)cluster_size) + return 1; + + return 0; +} + + +static inline MPoolcluster +element_cluster_get (MPool self, void* element) +{ + return (MPoolcluster) llist_ufind (&self->clusters, (const_LList) element, cmp_cluster_contains_element, (void*)self->cluster_size); +} + + +static inline unsigned +uintptr_nearestbit (uintptr_t v, unsigned n) +{ + unsigned r = 0; + uintptr_t mask = MPOOL_C(1)<>1)); + } +} + + +static inline void* +alloc_near (MPoolcluster cluster, MPool self, void* locality) +{ + TRACE (mpool_dbg, "locality %p", locality); + void* begin_of_elements = + (void*)cluster + + sizeof (*cluster) + /* header */ + MPOOL_BITMAP_SIZE (((MPool)self)->elements_per_cluster); /* bitmap */ + + uintptr_t index = (locality - begin_of_elements) / self->elem_size; + uintptr_t quot = index>>MPOOL_DIV_SHIFT; + uintptr_t rem = index & ~((~MPOOL_C(0))<data; + unsigned r = ~0U; + + /* the bitmap word at locality */ + if (bitmap[quot] < UINTPTR_MAX) + { + r = uintptr_nearestbit (~bitmap[quot], rem); + } + /* the bitmap word before locality, this gives a slight bias towards the begin, keeping the pool compact */ + else if (quot && bitmap[quot-1] < UINTPTR_MAX) + { + --quot; + r = uintptr_nearestbit (~bitmap[quot], sizeof(uintptr_t)*CHAR_BIT-1); + } + + if (r != ~0U && (quot*sizeof(uintptr_t)*CHAR_BIT+r) < self->elements_per_cluster) + { + void* ret = begin_of_elements + ((uintptr_t)(quot*sizeof(uintptr_t)*CHAR_BIT+r)*self->elem_size); + return ret; + } + return NULL; +} + + +static inline void +bitmap_set_element (MPoolcluster cluster, MPool self, void* element) +{ + void* begin_of_elements = + (void*)cluster + + sizeof (*cluster) + /* header */ + MPOOL_BITMAP_SIZE (((MPool)self)->elements_per_cluster); /* bitmap */ + + uintptr_t index = (element - begin_of_elements) / self->elem_size; + uintptr_t quot = index>>MPOOL_DIV_SHIFT; + uintptr_t rem = index & ~((~MPOOL_C(0))<data; + bitmap[quot] |= ((uintptr_t)1<elements_per_cluster); /* bitmap */ + + uintptr_t index = (element - begin_of_elements) / self->elem_size; + uintptr_t quot = index>>MPOOL_DIV_SHIFT; + uintptr_t rem = index & ~((~MPOOL_C(0))<data; + bitmap[quot] &= ~((uintptr_t)1<elements_free) + { + if (mpool_cluster_alloc_ (self)) + { + self->locality = NULL; /* supress alloc_near() */ + } + else + { + ERROR (mpool_dbg, "allocation failure"); + return NULL; + } + } + + void* ret = NULL; + + if (self->locality) + { + ret = alloc_near (element_cluster_get (self, self->locality), self, self->locality); + TRACE_IF (ret, mpool_dbg, "near allocation %p", ret); + } + + if (!ret) + { + ret = llist_head (&self->freelist); + TRACE_IF (ret, mpool_dbg, "far allocation %p", ret); + } + + if (ret) + { + bitmap_set_element (element_cluster_get (self, ret), self, ret); + llist_unlink_fast_ ((LList)ret); + } + + self->locality = ret; + --self->elements_free; + + return ret; +} + + +void* +mpool_alloc_near (MPool self, void* near) +{ + TRACE (mpool_dbg); + + if (!self->elements_free) + { + if (mpool_cluster_alloc_ (self)) + { + near = NULL; /* supress alloc_near() */ + } + else + { + ERROR (mpool_dbg, "allocation failure"); + return NULL; + } + } + + void* ret = NULL; + + if (near) + { + ret = alloc_near (element_cluster_get (self, near), self, near); + TRACE_IF (ret, mpool_dbg, "near allocation %p", ret); + } + + if (!ret) + { + ret = llist_head (&self->freelist); + TRACE_IF (ret, mpool_dbg, "far allocation %p", ret); + } + + if (ret) + { + bitmap_set_element (element_cluster_get (self, ret), self, ret); + llist_unlink_fast_ ((LList)ret); + } + + --self->elements_free; + + return ret; +} + + +static inline MPoolnode +find_near (MPoolcluster cluster, MPool self, void* element) +{ + void* begin_of_elements = + (void*)cluster + + sizeof (*cluster) + /* header */ + MPOOL_BITMAP_SIZE (((MPool)self)->elements_per_cluster); /* bitmap */ + + uintptr_t index = (element - begin_of_elements) / self->elem_size; + uintptr_t quot = index>>MPOOL_DIV_SHIFT; + uintptr_t rem = index & ~((~MPOOL_C(0))<data; + unsigned r = ~0U; + + /* the bitmap word at locality */ + if (bitmap[quot] < UINTPTR_MAX) + { + r = uintptr_nearestbit (~bitmap[quot], rem); + } + /* the bitmap word after element, we assume that elements after the searched element are more likely be free */ + else if (index < self->elements_per_cluster && bitmap[quot+1] < UINTPTR_MAX) + { + ++quot; + r = uintptr_nearestbit (~bitmap[quot], 0); + } + /* finally the bitmap word before element */ + else if (index > 0 && bitmap[quot-1] < UINTPTR_MAX) + { + --quot; + r = uintptr_nearestbit (~bitmap[quot], sizeof(uintptr_t)*CHAR_BIT-1); + } + + if (r != ~0U && (quot*sizeof(uintptr_t)*CHAR_BIT+r) < self->elements_per_cluster) + return begin_of_elements + ((uintptr_t)(quot*sizeof(uintptr_t)*CHAR_BIT+r)*self->elem_size); + + return NULL; +} + + +void +mpool_free (MPool self, void* element) +{ + if (self && element) + { + TRACE (mpool_dbg, "mpool %p: element %p", self, element); + + MPoolcluster cluster = element_cluster_get (self,element); + MPoolnode near = find_near (cluster, self, element); + + bitmap_clear_element (cluster, self, element); + llist_init (&((MPoolnode)element)->node); + + if (near) + { + TRACE (mpool_dbg, "found near %p", near); + if (near < (MPoolnode)element) + llist_insert_next (&near->node, &((MPoolnode)element)->node); + else + llist_insert_prev (&near->node, &((MPoolnode)element)->node); + } + else + llist_insert_tail (&self->freelist, &((MPoolnode)element)->node); + + ++self->elements_free; + } +} + + + +MPool +mpool_reserve (MPool self, unsigned nelements) +{ + if (self) + while (self->elements_free < nelements) + if (!mpool_cluster_alloc_ (self)) + return NULL; + + return self; +} + + +void +nobug_mpool_dump (const_MPool self, + const int depth, + const char* file, + const int line, + const char* func) +{ + if (self && depth) + { + DUMP_LOG ("mpool %p: ", self); + + if (depth > 1) + { + DUMP_LOG (" elements_per_cluster %u: ", self->elements_per_cluster); + DUMP_LOG (" elements_free %u: ", self->elements_free); + } + + if (depth > 2) + { + DUMP_LOG (" clusters %p: ", self->clusters); + int i = 0; + LLIST_FOREACH (&self->clusters, cluster) + DUMP_LOG (" %p: %u", cluster, ++i); + } + + if (depth > 3) + { + DUMP_LOG (" freelist %p: ", self->freelist); + int i = 0; + LLIST_FOREACH (&self->freelist, node) + DUMP_LOG (" %p: %u", node, ++i); + } + } +} + + +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/lib/mpool.h b/src/lib/mpool.h new file mode 100644 index 000000000..18c34dca8 --- /dev/null +++ b/src/lib/mpool.h @@ -0,0 +1,247 @@ +/* + mpool.h - memory pool for constant sized objects + + Copyright (C) Lumiera.org + 2009, 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 +#include +#include "lib/llist.h" +#include "include/logging.h" + +/* +//mpool Memory Pools +//mpool ------------ +//mpool +//mpool This memory pools are implemented as clusters of fixed sized elements. New clusters +//mpool are allocated on demand or manually preallocated with a `reserve()` operation. +//mpool Some efforts are taken to ensure (cache) locality of the provided memory. +//mpool All functions are reentrant but not threadsafe, if this is desired it is advised to +//mpool care for proper locking elsewhere. +//mpool +*/ + + +/* +//index.mpool_destroy_fn xref:mpool_destroy_fn[mpool_destroy_fn]:: function prototype for destroying elements +//mpool [[mpool_destroy_fn]] +//mpool .mpool_destroy_fn +//mpool When a memory pool gets destroyed it can call a destructor for any element which is still in the pool. +//mpool This destructor is optional. +//mpool +//mpool typedef void (*mpool_destroy_fn)(void* self) +//mpool +//mpool `self`:: +//mpool element to be destroyed +//mpool +*/ +typedef void (*mpool_destroy_fn)(void* self); + + +/* +//index.struct_mpool xref:struct_mpool[mpool (struct)]:: the memory pool management structure +//mpool [[struct_mpool]] +//mpool .mpool +//mpool typedef struct mpool_struct mpool +//mpool typedef mpool* MPool +//mpool typedef const mpool* const_MPool +//mpool +//mpool This structure should be considered opaque. +*/ +typedef struct mpool_struct mpool; +typedef mpool* MPool; +typedef const mpool* const_MPool; + +struct mpool_struct +{ + llist freelist; + llist clusters; + size_t elem_size; + unsigned elements_per_cluster; + uintptr_t cluster_size; + unsigned elements_free; /* a counter of free elements is the price we pay to support a reserve() operation */ + void* locality; + mpool_destroy_fn destroy; +}; + + +/* +//index.mpool_init xref:mpool_init[mpool_init()]:: initialize a new memory pool +//mpool [[mpool_init]] +//mpool .mpool_init +//mpool Initialize a memory pool, memory pools must be initialized before being used. One can supply +//mpool an optional destructor function for elements, this will be used to destroy elements which are still +//mpool in the pool when it gets destroyed itself. The destructor is _NOT_ called when elemented are freed. +//mpool +//mpool MPool mpool_init (MPool self, size_t elem_size, unsigned elements_per_cluster, mpool_move_fn mv, mpool_destroy_fn dtor) +//mpool +//mpool `self`:: +//mpool pointer to the memory pool structure to be initialized +//mpool `elem_size`:: +//mpool size for a single element +//mpool `elements_per_cluster`:: +//mpool how many elements to put into a cluster +//mpool `dtor`:: +//mpool pointer to an optional destructor function or NULL +//mpool return:: +//mpool self +//mpool +*/ +MPool +mpool_init (MPool self, size_t elem_size, unsigned elements_per_cluster, mpool_destroy_fn dtor); + + +/* +//index.mpool_destroy xref:mpool_destroy[mpool_destroy()]:: destroy a memory pool +//mpool [[mpool_destroy]] +//mpool .mpool_destroy +//mpool A memory pool is not used anymore it should be destroyed. This frees all memory allocated with it. +//mpool When a destructor was provided at construction time, then this destructor is used on all non free elements +//mpool before before the clusters are freed. If no destructor was given then the clusters are just freed. +//mpool The destroyed memory pool behaves as if it was freshly initialized and can be used again, this is some kindof +//mpool exceptional behaviour. +//mpool +//mpool MPool mpool_destroy (MPool self) +//mpool +//mpool `self`:: +//mpool pointer to an initialized memory pool to be destroyed. +//mpool return:: +//mpool self +//mpool +//mpool +*/ +MPool +mpool_destroy (MPool self); + + +/* +//index.mpool_available xref:mpool_available[mpool_available()]:: query number of free elements +//mpool [[mpool_available]] +//mpool .mpool_available +//mpool One can check how much elements are available without a new cluster allocation in a memory pool. +//mpool +//mpool unsigned mpool_available (MPool self) +//mpool +//mpool `self`:: +//mpool pointer to the memory pool to be queried +//mpool return:: +//mpool number of available elements +//mpool +*/ +static inline unsigned +mpool_available (MPool self) +{ + return self->elements_free; +} + + +/* +//index.mpool_reserve xref:mpool_reserve[mpool_reserve()]:: preallocate elements +//mpool [[mpool_reserve]] +//mpool .mpool_reserve +//mpool Resize the pool that at least nelements become available without cluster reallocations +//mpool +//mpool unsigned mpool_reserve (MPool self, unsigned nelements) +//mpool +//mpool `self`:: +//mpool pointer to the memory pool +//mpool `nelements`:: +//mpool minimum number of elements to preallocate +//mpool return:: +//mpool self on success or NULL on error +//mpool +*/ +MPool +mpool_reserve (MPool self, unsigned nelements); + + +/* +//index.mpool_alloc xref:mpool_alloc[mpool_alloc()]:: allocate one element +//mpool [[mpool_alloc]] +//mpool .mpool_alloc +//mpool Allocates on element from a mpool. To improve cache locality allocations +//mpool are grouped close together to recent allocations. +//mpool +//mpool void* mpool_alloc (MPool self) +//mpool +//mpool `self`:: +//mpool pointer to the memory pool +//mpool return:: +//mpool pointer to the allocated memory on success or NULL on error +//mpool will never fail when enough space was preallocated +//mpool +*/ +void* +mpool_alloc (MPool self); + + +/* +//index.mpool_alloc_near xref:mpool_alloc_near[mpool_alloc_near()]:: allocate one element, w/ locality +//mpool [[mpool_alloc_near]] +//mpool .mpool_alloc_near +//mpool Allocates on element from a mpool. To improve cache locality the allocation +//mpool tries to get an element close to another. +//mpool +//mpool void* mpool_alloc_near (MPool self, void* near) +//mpool +//mpool `self`:: +//mpool pointer to the memory pool +//mpool `near`:: +//mpool reference to another element which should be close to the returned element (hint only) +//mpool return:: +//mpool pointer to the allocated memory on success or NULL on error +//mpool will never fail when enough space was preallocated +//mpool +*/ +void* +mpool_alloc_near (MPool self, void* near); + + +/* +//index.mpool_free xref:mpool_free[mpool_free()]:: free one element +//mpool [[mpool_free]] +//mpool .mpool_free +//mpool Frees the given element and puts it back into the pool for furhter allocations. +//mpool +//mpool void mpool_free (MPool self, void* element) +//mpool +//mpool `self`:: +//mpool pointer to the memory pool +//mpool `element`:: +//mpool element to be freed +//mpool +*/ +void +mpool_free (MPool self, void* element); + + + +void +nobug_mpool_dump (const_MPool self, + const int depth, + const char* file, + const int line, + const char* func); + +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/lib/mutex.c b/src/lib/mutex.c index 60078671d..25b56679c 100644 --- a/src/lib/mutex.c +++ b/src/lib/mutex.c @@ -2,7 +2,7 @@ mutex.c - mutex Copyright (C) Lumiera.org - 2008, Christian Thaeter + 2008, 2009, 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 @@ -26,9 +26,9 @@ * Mutual exclusion locking. */ -LUMIERA_ERROR_DEFINE (MUTEX_LOCK, "Mutex locking failed"); -LUMIERA_ERROR_DEFINE (MUTEX_UNLOCK, "Mutex unlocking failed"); -LUMIERA_ERROR_DEFINE (MUTEX_DESTROY, "Mutex destroy failed"); +LUMIERA_ERROR_DEFINE (LOCK_ACQUIRE, "locking failed"); +LUMIERA_ERROR_DEFINE (LOCK_RELEASE, "unlocking failed"); +LUMIERA_ERROR_DEFINE (LOCK_DESTROY, "lock destroy failed"); LumieraMutex @@ -44,32 +44,6 @@ lumiera_mutex_init (LumieraMutex self, const char* purpose, struct nobug_flag* f } -static pthread_once_t recursive_mutexattr_once = PTHREAD_ONCE_INIT; -static pthread_mutexattr_t recursive_mutexattr; - -static void recursive_mutexattr_init() -{ - pthread_mutexattr_init (&recursive_mutexattr); - pthread_mutexattr_settype (&recursive_mutexattr, PTHREAD_MUTEX_RECURSIVE); -} - - -LumieraMutex -lumiera_recmutex_init (LumieraMutex self, const char* purpose, struct nobug_flag* flag) -{ - if (self) - { - if (recursive_mutexattr_once == PTHREAD_ONCE_INIT) - pthread_once (&recursive_mutexattr_once, recursive_mutexattr_init); - - pthread_mutex_init (&self->mutex, &recursive_mutexattr); - NOBUG_RESOURCE_HANDLE_INIT (self->rh); - NOBUG_RESOURCE_ANNOUNCE_RAW (flag, "recmutex", purpose, self, self->rh); - } - return self; -} - - LumieraMutex lumiera_mutex_destroy (LumieraMutex self, struct nobug_flag* flag) { @@ -77,11 +51,16 @@ lumiera_mutex_destroy (LumieraMutex self, struct nobug_flag* flag) { NOBUG_RESOURCE_FORGET_RAW (flag, self->rh); if (pthread_mutex_destroy (&self->mutex)) - LUMIERA_DIE (MUTEX_DESTROY); + LUMIERA_DIE (LOCK_DESTROY); } return self; } +int lumiera_mutex_unlock_cb (void* mutex) +{ + return pthread_mutex_unlock (&((LumieraMutex)mutex)->mutex); +} + /* // Local Variables: // mode: C diff --git a/src/lib/mutex.h b/src/lib/mutex.h index 0ab5b43c2..a76707933 100644 --- a/src/lib/mutex.h +++ b/src/lib/mutex.h @@ -2,7 +2,7 @@ mutex.h - mutal exclusion locking Copyright (C) Lumiera.org - 2008, Christian Thaeter + 2008, 2009, 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 @@ -23,6 +23,7 @@ #define LUMIERA_MUTEX_H #include "lib/error.h" +#include "lib/sectionlock.h" #include #include @@ -32,35 +33,31 @@ * Mutual exclusion locking, header. */ -LUMIERA_ERROR_DECLARE (MUTEX_LOCK); -LUMIERA_ERROR_DECLARE (MUTEX_UNLOCK); -LUMIERA_ERROR_DECLARE (MUTEX_DESTROY); /** * Mutual exclusive section. */ -#define LUMIERA_MUTEX_SECTION(nobugflag, mtx) \ - for (lumiera_mutexacquirer NOBUG_CLEANUP(lumiera_mutexacquirer_ensureunlocked) lumiera_mutex_section_ \ - = {(LumieraMutex)1 NOBUG_ALPHA_COMMA_NULL}; \ - lumiera_mutex_section_.mutex;) \ - for ( \ - ({ \ - lumiera_mutex_section_.mutex = (mtx); \ - NOBUG_RESOURCE_HANDLE_INIT (lumiera_mutex_section_.rh); \ - RESOURCE_ENTER (nobugflag, (mtx)->rh, "acquire mutex", &lumiera_mutex_section_, \ - NOBUG_RESOURCE_EXCLUSIVE, lumiera_mutex_section_.rh); \ - if (pthread_mutex_lock (&(mtx)->mutex)) LUMIERA_DIE (MUTEX_LOCK); \ - }); \ - lumiera_mutex_section_.mutex; \ - ({ \ - if (lumiera_mutex_section_.mutex) \ - { \ - pthread_mutex_unlock (&lumiera_mutex_section_.mutex->mutex); \ - lumiera_mutex_section_.mutex = NULL; \ - RESOURCE_LEAVE(nobugflag, lumiera_mutex_section_.rh); \ - } \ +#define LUMIERA_MUTEX_SECTION(nobugflag, mtx) \ + for (lumiera_sectionlock NOBUG_CLEANUP(lumiera_sectionlock_ensureunlocked) \ + lumiera_lock_section_ = { \ + (void*)1, lumiera_mutex_unlock_cb NOBUG_ALPHA_COMMA_NULL NOBUG_ALPHA_COMMA_NULL}; \ + lumiera_lock_section_.lock;) \ + for ( \ + ({ \ + lumiera_lock_section_.lock = (mtx); \ + NOBUG_IF_ALPHA(lumiera_lock_section_.flag = &NOBUG_FLAG(nobugflag);) \ + RESOURCE_ENTER (nobugflag, (mtx)->rh, "acquire mutex", &lumiera_lock_section_, \ + NOBUG_RESOURCE_WAITING, lumiera_lock_section_.rh); \ + if (pthread_mutex_lock (&(mtx)->mutex)) \ + LUMIERA_DIE (LOCK_ACQUIRE); \ + RESOURCE_STATE (nobugflag, NOBUG_RESOURCE_EXCLUSIVE, lumiera_lock_section_.rh); \ + }); \ + lumiera_lock_section_.lock; \ + ({ \ + LUMIERA_MUTEX_SECTION_UNLOCK; \ })) + /** * Mutual exclusion chainbuilder section. * Usage: LUMIERA_MUTEX_SECTION(a){LUMIERA_MUTEX_SECTION_CHAIN(b){run();}} @@ -68,97 +65,32 @@ LUMIERA_ERROR_DECLARE (MUTEX_DESTROY); * This macro should only be used inside LUMIERA_MUTEX_SECTION and should be * called on the correct mutexes, period. */ -#define LUMIERA_MUTEX_SECTION_CHAIN(nobugflag, mtx) \ - for (lumiera_mutexacquirer *lumiera_mutex_section_old_ = &lumiera_mutex_section_, \ - NOBUG_CLEANUP(lumiera_mutexacquirer_ensureunlocked) lumiera_mutex_section_ = {(LumieraMutex)1 \ - NOBUG_ALPHA_COMMA_NULL}; \ - lumiera_mutex_section_.mutex;) \ - for ( \ - ({ \ - lumiera_mutex_section_.mutex = (mtx); \ - NOBUG_RESOURCE_HANDLE_INIT (lumiera_mutex_section_.rh); \ - RESOURCE_ENTER (nobugflag, (mtx)->rh, "acquire mutex", &lumiera_mutex_section_, \ - NOBUG_RESOURCE_EXCLUSIVE, lumiera_mutex_section_.rh); \ - if (pthread_mutex_lock (&(mtx)->mutex)) LUMIERA_DIE (MUTEX_LOCK); \ - if (lumiera_mutex_section_old_->mutex) \ - { \ - pthread_mutex_unlock (&lumiera_mutex_section_old_->mutex->mutex); \ - lumiera_mutex_section_old_->mutex = NULL; \ - RESOURCE_LEAVE(nobugflag, lumiera_mutex_section_old_->rh); \ - } \ - }); \ - lumiera_mutex_section_.mutex; \ - ({ \ - if (lumiera_mutex_section_.mutex) \ - { \ - pthread_mutex_unlock (&lumiera_mutex_section_.mutex->mutex); \ - lumiera_mutex_section_.mutex = NULL; \ - RESOURCE_LEAVE(nobugflag, lumiera_mutex_section_.rh); \ - } \ + +#define LUMIERA_MUTEX_SECTION_CHAIN(nobugflag, mtx) \ + for (lumiera_sectionlock *lumiera_lock_section_old_ = &lumiera_lock_section_, \ + NOBUG_CLEANUP(lumiera_sectionlock_ensureunlocked) lumiera_lock_section_ = { \ + (void*)1, lumiera_mutex_unlock_cb NOBUG_ALPHA_COMMA_NULL NOBUG_ALPHA_COMMA_NULL}; \ + lumiera_lock_section_.lock;) \ + for ( \ + ({ \ + REQUIRE (lumiera_lock_section_old_->lock, "section prematurely unlocked"); \ + lumiera_lock_section_.lock = mtx; \ + NOBUG_IF_ALPHA(lumiera_lock_section_.flag = &NOBUG_FLAG(nobugflag);) \ + RESOURCE_ENTER (nobugflag, (mtx)->rh, "acquire mutex", &lumiera_lock_section_, \ + NOBUG_RESOURCE_WAITING, lumiera_lock_section_.rh); \ + if (pthread_mutex_lock (&(mtx)->mutex)) \ + LUMIERA_DIE (LOCK_ACQUIRE); \ + RESOURCE_STATE (nobugflag, NOBUG_RESOURCE_EXCLUSIVE, lumiera_lock_section_.rh); \ + LUMIERA_SECTION_UNLOCK_(lumiera_lock_section_old_); \ + }); \ + lumiera_lock_section_.lock; \ + ({ \ + LUMIERA_MUTEX_SECTION_UNLOCK; \ })) -/** - * Recursive Mutual exclusive section. - */ -#define LUMIERA_RECMUTEX_SECTION(nobugflag, mtx) \ - for (lumiera_mutexacquirer NOBUG_CLEANUP(lumiera_mutexacquirer_ensureunlocked) lumiera_mutex_section_ \ - = {(LumieraMutex)1 NOBUG_ALPHA_COMMA_NULL}; \ - lumiera_mutex_section_.mutex;) \ - for ( \ - ({ \ - lumiera_mutex_section_.mutex = (mtx); \ - NOBUG_RESOURCE_HANDLE_INIT (lumiera_mutex_section_.rh); \ - RESOURCE_ENTER (nobugflag, (mtx)->rh, "acquire recmutex", &lumiera_mutex_section_, \ - NOBUG_RESOURCE_RECURSIVE, lumiera_mutex_section_.rh); \ - if (pthread_mutex_lock (&(mtx)->mutex)) LUMIERA_DIE (MUTEX_LOCK); \ - }); \ - lumiera_mutex_section_.mutex; \ - ({ \ - if (lumiera_mutex_section_.mutex) \ - { \ - pthread_mutex_unlock (&lumiera_mutex_section_.mutex->mutex); \ - lumiera_mutex_section_.mutex = NULL; \ - RESOURCE_LEAVE(nobugflag, lumiera_mutex_section_.rh); \ - } \ - })) - - -/** - * Mutual exclusion chainbuilder section. - * Usage: LUMIERA_MUTEX_SECTION(a){LUMIERA_RECMUTEX_SECTION_CHAIN(b){run();}} - * calls lock(a); lock(b); unlock(a); run(); unlock(b); - * This macro should only be used inside LUMIERA_MUTEX_SECTION and should be - * called on the correct mutexes, period. - */ -#define LUMIERA_RECMUTEX_SECTION_CHAIN(nobugflag, mtx) \ - for (lumiera_mutexacquirer *lumiera_mutex_section_old_ = &lumiera_mutex_section_, \ - NOBUG_CLEANUP(lumiera_mutexacquirer_ensureunlocked) lumiera_mutex_section_ = {(LumieraMutex)1 \ - NOBUG_ALPHA_COMMA_NULL}; \ - lumiera_mutex_section_.mutex;) \ - for ( \ - ({ \ - lumiera_mutex_section_.mutex = (mtx); \ - NOBUG_RESOURCE_HANDLE_INIT (lumiera_mutex_section_.rh); \ - RESOURCE_ENTER (nobugflag, (mtx)->rh, "acquire recmutex", &lumiera_mutex_section_, \ - NOBUG_RESOURCE_RECURSIVE, lumiera_mutex_section_.rh); \ - if (pthread_mutex_lock (&(mtx)->mutex)) LUMIERA_DIE (MUTEX_LOCK); \ - if (lumiera_mutex_section_old_->mutex) \ - { \ - pthread_mutex_unlock (&lumiera_mutex_section_old_->mutex->mutex); \ - lumiera_mutex_section_old_->mutex = NULL; \ - RESOURCE_LEAVE(nobugflag, lumiera_mutex_section_old_->rh); \ - } \ - }); \ - lumiera_mutex_section_.mutex; \ - ({ \ - if (lumiera_mutex_section_.mutex) \ - { \ - pthread_mutex_unlock (&lumiera_mutex_section_.mutex->mutex); \ - lumiera_mutex_section_.mutex = NULL; \ - RESOURCE_LEAVE(nobugflag, lumiera_mutex_section_.rh); \ - } \ - })) +#define LUMIERA_MUTEX_SECTION_UNLOCK \ + LUMIERA_SECTION_UNLOCK_(&lumiera_lock_section_) /** @@ -183,16 +115,6 @@ typedef lumiera_mutex* LumieraMutex; LumieraMutex lumiera_mutex_init (LumieraMutex self, const char* purpose, struct nobug_flag* flag); -/** - * Initialize a mutex variable - * Initializes a 'recursive' mutex which might be locked by the same thread multiple times. - * @param self is a pointer to the mutex to be initialized - * @return self as given - */ -LumieraMutex -lumiera_recmutex_init (LumieraMutex self, const char* purpose, struct nobug_flag* flag); - - /** * Destroy a mutex variable * @param self is a pointer to the mutex to be destroyed @@ -203,22 +125,12 @@ lumiera_mutex_destroy (LumieraMutex self, struct nobug_flag* flag); /** - * mutexacquirer used to manage the state of a mutex. + * Callback for unlocking mutexes. + * @internal */ -struct lumiera_mutexacquirer_struct -{ - LumieraMutex mutex; - RESOURCE_HANDLE (rh); -}; -typedef struct lumiera_mutexacquirer_struct lumiera_mutexacquirer; -typedef struct lumiera_mutexacquirer_struct* LumieraMutexacquirer; +int +lumiera_mutex_unlock_cb (void* mutex); -/* helper function for nobug */ -static inline void -lumiera_mutexacquirer_ensureunlocked (LumieraMutexacquirer self) -{ - ENSURE (!self->mutex, "forgot to unlock mutex"); -} #endif /* diff --git a/src/lib/reccondition.c b/src/lib/reccondition.c index 66000e7e1..59bc956aa 100644 --- a/src/lib/reccondition.c +++ b/src/lib/reccondition.c @@ -2,7 +2,7 @@ reccondition.c - condition variable, w/ recursive mutex Copyright (C) Lumiera.org - 2008, Christian Thaeter + 2008, 2009, 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 @@ -19,7 +19,6 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include "lib/condition.h" #include "lib/reccondition.h" /** @@ -48,7 +47,7 @@ lumiera_reccondition_init (LumieraReccondition self, const char* purpose, struct pthread_once (&recursive_mutexattr_once, recursive_mutexattr_init); pthread_cond_init (&self->cond, NULL); - pthread_mutex_init (&self->mutex, &recursive_mutexattr); + pthread_mutex_init (&self->reccndmutex, &recursive_mutexattr); NOBUG_RESOURCE_HANDLE_INIT (self->rh); NOBUG_RESOURCE_ANNOUNCE_RAW (flag, "reccond_var", purpose, self, self->rh); } @@ -63,15 +62,19 @@ lumiera_reccondition_destroy (LumieraReccondition self, struct nobug_flag* flag) { NOBUG_RESOURCE_FORGET_RAW (flag, self->rh); - if (pthread_mutex_destroy (&self->mutex)) - LUMIERA_DIE (MUTEX_DESTROY); + if (pthread_mutex_destroy (&self->reccndmutex)) + LUMIERA_DIE (LOCK_DESTROY); if (pthread_cond_destroy (&self->cond)) - LUMIERA_DIE (CONDITION_DESTROY); + LUMIERA_DIE (LOCK_DESTROY); } return self; } +int lumiera_reccondition_unlock_cb (void* cond) +{ + return pthread_mutex_unlock (&((LumieraReccondition)cond)->reccndmutex); +} diff --git a/src/lib/reccondition.h b/src/lib/reccondition.h index f3246c516..dcf4e33a4 100644 --- a/src/lib/reccondition.h +++ b/src/lib/reccondition.h @@ -1,8 +1,8 @@ /* - reccondition.h - condition variables, w/ recursive mutex + reccondition.h - recursive locked condition variables Copyright (C) Lumiera.org - 2008, Christian Thaeter + 2008, 2009, 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 @@ -23,106 +23,122 @@ #define LUMIERA_RECCONDITION_H #include "lib/error.h" -#include "lib/mutex.h" +#include "lib/sectionlock.h" +#include +#include /** * @file - * Reccondition variables. Same as Conditon variables but using a recursive mutex for locking. + * Condition variables, header */ /** - * Reccondition section. - * Locks the reccondition mutex, one can then use LUMIERA_RECCONDITION_WAIT to wait for signals or + * Recursive Condition section. + * Locks the condition mutex, one can use LUMIERA_RECCONDITION_WAIT to wait for signals or * LUMIERA_RECCONDITION_SIGNAL or LUMIERA_RECCONDITION_BROADCAST to wake waiting threads - * @param nobugflag NoBug flag used to log actions on the reccondition - * @param rcnd Reccondition variable to be locked + * @param nobugflag NoBug flag used to log actions on the condition + * @param cnd Condition variable to be locked */ -#define LUMIERA_RECCONDITION_SECTION(nobugflag, rcnd) \ - for (lumiera_recconditionacquirer NOBUG_CLEANUP(lumiera_recconditionacquirer_ensureunlocked) \ - lumiera_reccondition_section_ = {(LumieraReccondition)1}; \ - lumiera_reccondition_section_.reccondition;) \ - for ( \ - ({ \ - lumiera_reccondition_section_.reccondition = (rcnd); \ - NOBUG_IF(NOBUG_MODE_ALPHA, lumiera_reccondition_section_.flag = &NOBUG_FLAG(nobugflag)); \ - NOBUG_RESOURCE_HANDLE_INIT (lumiera_reccondition_section_.rh); \ - RESOURCE_ENTER (nobugflag, (rcnd)->rh, "acquire reccondition", &lumiera_reccondition_section_, \ - NOBUG_RESOURCE_RECURSIVE, lumiera_reccondition_section_.rh); \ - if (pthread_mutex_lock (&(rcnd)->mutex)) LUMIERA_DIE (MUTEX_LOCK); \ - }); \ - lumiera_reccondition_section_.reccondition; \ - ({ \ - LUMIERA_RECCONDITION_UNLOCK(nobugflag) \ +#define LUMIERA_RECCONDITION_SECTION(nobugflag, cnd) \ + for (lumiera_sectionlock NOBUG_CLEANUP(lumiera_sectionlock_ensureunlocked) \ + lumiera_reccond_section_ = { \ + (void*)1, lumiera_reccondition_unlock_cb NOBUG_ALPHA_COMMA_NULL NOBUG_ALPHA_COMMA_NULL}; \ + lumiera_reccond_section_.lock;) \ + for ( \ + ({ \ + lumiera_reccond_section_.lock = (cnd); \ + NOBUG_IF_ALPHA(lumiera_reccond_section_.flag = &NOBUG_FLAG(nobugflag);) \ + RESOURCE_ENTER (nobugflag, (cnd)->rh, "acquire reccondmutex", &lumiera_reccond_section_, \ + NOBUG_RESOURCE_WAITING, lumiera_reccond_section_.rh); \ + if (pthread_mutex_lock (&(cnd)->reccndmutex)) \ + LUMIERA_DIE (LOCK_ACQUIRE); \ + RESOURCE_STATE (nobugflag, NOBUG_RESOURCE_RECURSIVE, lumiera_reccond_section_.rh); \ + }); \ + lumiera_reccond_section_.lock; \ + ({ \ + LUMIERA_RECCONDITION_SECTION_UNLOCK; \ })) -/** - * Explicit mutex unlock for a reccondition variable - * One can early unlock the mutex of a condition variable prior leaving a RECCONDITION_SECTION. - * The RECCONDITION_WAIT, RECCONDITION_SIGNAL and RECCONDITION_BROADCAST macros must not be used after the mutex - * got unlocked. - * @param nobugflag NoBug flag used to log actions on the reccondition - */ -#define LUMIERA_RECCONDITION_UNLOCK \ - if (lumiera_reccondition_section_.reccondition) \ - { \ - pthread_mutex_unlock (&lumiera_reccondition_section_.reccondition->mutex); \ - lumiera_reccondition_section_.reccondition = NULL; \ - RESOURCE_LEAVE(nobugflag, lumiera_reccondition_section_.rh); \ - } + +#define LUMIERA_RECCONDITION_SECTION_CHAIN(nobugflag, cnd) \ + for (lumiera_sectionlock *lumiera_lock_section_old_ = &lumiera_lock_section_, \ + NOBUG_CLEANUP(lumiera_sectionlock_ensureunlocked) lumiera_reccond_section_ = { \ + (void*)1, lumiera_reccondition_unlock_cb NOBUG_ALPHA_COMMA_NULL NOBUG_ALPHA_COMMA_NULL}; \ + lumiera_reccond_section_.lock;) \ + for ( \ + ({ \ + REQUIRE (lumiera_lock_section_old_->lock, "section prematurely unlocked"); \ + lumiera_reccond_section_.lock = (cnd); \ + NOBUG_IF_ALPHA(lumiera_reccond_section_.flag = &NOBUG_FLAG(nobugflag);) \ + RESOURCE_ENTER (nobugflag, (cnd)->rh, "acquire reccondmutex", &lumiera_reccond_section_, \ + NOBUG_RESOURCE_WAITING, lumiera_reccond_section_.rh); \ + if (pthread_mutex_lock (&(cnd)->reccndmutex)) \ + LUMIERA_DIE (LOCK_ACQUIRE); \ + RESOURCE_STATE (nobugflag, NOBUG_RESOURCE_RECURSIVE, lumiera_reccond_section_.rh); \ + LUMIERA_SECTION_UNLOCK_(lumiera_lock_section_old_) \ + }); \ + lumiera_reccond_section_.lock; \ + ({ \ + LUMIERA_RECCONDITION_SECTION_UNLOCK; \ + })) + + +#define LUMIERA_RECCONDITION_SECTION_UNLOCK \ + LUMIERA_SECTION_UNLOCK_(&lumiera_reccond_section_) /** - * Wait for a reccondition. + * Wait for a condition. * Must be used inside a RECCONDITION_SECTION. - * @param expr Recconditon which must become true, else the reccondition variable goes back into sleep + * @param expr Condition which must become true, else the condition variable goes back into sleep */ -#define LUMIERA_RECCONDITION_WAIT(expr) \ - do { \ - ENSURE (lumiera_reccondition_section_.reccondition, "Reccondition mutex not locked"); \ - NOBUG_RESOURCE_STATE_RAW (lumiera_reccondition_section_.flag, lumiera_reccondition_section_.rh, NOBUG_RESOURCE_WAITING); \ - pthread_cond_wait (&lumiera_reccondition_section_.reccondition->cond, &lumiera_reccondition_section_.reccondition->mutex); \ - NOBUG_RESOURCE_STATE_RAW (lumiera_reccondition_section_.flag, lumiera_reccondition_section_.rh, NOBUG_RESOURCE_RECURSIVE); \ +#define LUMIERA_RECCONDITION_WAIT(expr) \ + do { \ + REQUIRE (lumiera_reccond_section_.lock, "Condition recmutex not locked"); \ + NOBUG_RESOURCE_STATE_RAW (lumiera_reccond_section_.flag, NOBUG_RESOURCE_WAITING, lumiera_reccond_section_.rh); \ + pthread_cond_wait (&((LumieraReccondition)lumiera_reccond_section_.lock)->cond, \ + &((LumieraReccondition)lumiera_reccond_section_.lock)->reccndmutex); \ + NOBUG_RESOURCE_STATE_RAW (lumiera_reccond_section_.flag, NOBUG_RESOURCE_RECURSIVE, lumiera_reccond_section_.rh); \ } while (!(expr)) /** - * Signal a reccondition variable + * Signal a condition variable * Must be used inside a RECCONDITION_SECTION. * Wakes one thread waiting on the condition variable */ -#define LUMIERA_RECCONDITION_SIGNAL \ -do { \ - ENSURE (lumiera_reccondition_section_.reccondition, "Reccondition mutex not locked"); \ - NOBUG_IF(NOBUG_MODE_ALPHA, TRACE(lumiera_reccondition_section_.flag, "Signaling")); \ - pthread_cond_signal (&lumiera_reccondition_section_.reccondition->cond); \ -} while (0) +#define LUMIERA_RECCONDITION_SIGNAL \ + do { \ + REQUIRE (lumiera_reccond_section_.lock, "Condition recmutex not locked"); \ + TRACE(NOBUG_FLAG_RAW(lumiera_reccond_section_.flag), "Signal %p", &lumiera_reccond_section_); \ + pthread_cond_signal (&((LumieraReccondition)lumiera_reccond_section_.lock)->cond); \ + } while (0) /** - * Broadcast a reccondition variable + * Broadcast a condition variable * Must be used inside a RECCONDITION_SECTION. - * Wakes all threads waiting on the reccondition variable + * Wakes all threads waiting on the condition variable */ -#define LUMIERA_RECCONDITION_BROADCAST \ -do { \ - ENSURE (lumiera_reccondition_section_.reccondition, "Reccondition mutex not locked"); \ - NOBUG_IF(NOBUG_MODE_ALPHA, TRACE(lumiera_reccondition_section_.flag, "Broadcasting"));\ - pthread_cond_broadcast (&lumiera_reccondition_section_.reccondition->cond); \ -} while (0) - +#define LUMIERA_RECCONDITION_BROADCAST \ + do { \ + REQUIRE (lumiera_reccond_section_.lock, "Condition recmutex not locked"); \ + TRACE(NOBUG_FLAG_RAW(lumiera_reccond_section_.flag), "Broadcast %p", &lumiera_reccond_section_); \ + pthread_cond_broadcast (&((LumieraReccondition)lumiera_reccond_section_.lock)->cond); \ + } while (0) /** - * Reccondition variables. + * Condition variables. Recursive mutex variant. * */ struct lumiera_reccondition_struct { pthread_cond_t cond; - pthread_mutex_t mutex; + pthread_mutex_t reccndmutex; RESOURCE_HANDLE (rh); }; typedef struct lumiera_reccondition_struct lumiera_reccondition; @@ -130,8 +146,8 @@ typedef lumiera_reccondition* LumieraReccondition; /** - * Initialize a reccondition variable - * @param self is a pointer to the reccondition variable to be initialized + * Initialize a condition variable + * @param self is a pointer to the condition variable to be initialized * @return self as given */ LumieraReccondition @@ -139,34 +155,16 @@ lumiera_reccondition_init (LumieraReccondition self, const char* purpose, struct /** - * Destroy a reccondition variable - * @param self is a pointer to the reccondition variable to be destroyed + * Destroy a condition variable + * @param self is a pointer to the condition variable to be destroyed * @return self as given */ LumieraReccondition lumiera_reccondition_destroy (LumieraReccondition self, struct nobug_flag* flag); -/** - * recconditionacquirer used to manage the state of a reccondition variable. - */ -struct lumiera_recconditionacquirer_struct -{ - LumieraReccondition reccondition; - NOBUG_IF(NOBUG_MODE_ALPHA, struct nobug_flag* flag); - RESOURCE_HANDLE (rh); -}; -typedef struct lumiera_recconditionacquirer_struct lumiera_recconditionacquirer; -typedef struct lumiera_recconditionacquirer_struct* LumieraRecconditionacquirer; - - -/* helper function for nobug */ -static inline void -lumiera_recconditionacquirer_ensureunlocked (LumieraRecconditionacquirer self) -{ - ENSURE (!self->reccondition, "forgot to unlock reccondition variable"); -} - +int +lumiera_reccondition_unlock_cb (void* cond); #endif /* diff --git a/src/lib/recmutex.c b/src/lib/recmutex.c new file mode 100644 index 000000000..447a12350 --- /dev/null +++ b/src/lib/recmutex.c @@ -0,0 +1,74 @@ +/* + recmutex.c - recursive mutex + + Copyright (C) Lumiera.org + 2008, 2009, 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/recmutex.h" + +/** + * @file + * Recursive Mutexes. + */ + + +static pthread_once_t recmutexattr_once = PTHREAD_ONCE_INIT; +static pthread_mutexattr_t recmutexattr; + +static void recmutexattr_init() +{ + pthread_mutexattr_init (&recmutexattr); + pthread_mutexattr_settype (&recmutexattr, PTHREAD_MUTEX_RECURSIVE); +} + + +LumieraRecmutex +lumiera_recmutex_init (LumieraRecmutex self, const char* purpose, struct nobug_flag* flag) +{ + if (self) + { + if (recmutexattr_once == PTHREAD_ONCE_INIT) + pthread_once (&recmutexattr_once, recmutexattr_init); + + pthread_mutex_init (&self->recmutex, &recmutexattr); + NOBUG_RESOURCE_HANDLE_INIT (self->rh); + NOBUG_RESOURCE_ANNOUNCE_RAW (flag, "recmutex", purpose, self, self->rh); + } + return self; +} + +LumieraRecmutex +lumiera_recmutex_destroy (LumieraRecmutex self, struct nobug_flag* flag) +{ + if (self) + { + NOBUG_RESOURCE_FORGET_RAW (flag, self->rh); + if (pthread_mutex_destroy (&self->recmutex)) + LUMIERA_DIE (LOCK_DESTROY); + } + return self; +} + + +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/lib/recmutex.h b/src/lib/recmutex.h new file mode 100644 index 000000000..a893d3bed --- /dev/null +++ b/src/lib/recmutex.h @@ -0,0 +1,132 @@ +/* + recmutex.h - recursive mutal exclusion locking + + Copyright (C) Lumiera.org + 2008, 2009, 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 LUMIERA_RECMUTEX_H +#define LUMIERA_RECMUTEX_H + +#include "lib/error.h" +#include "lib/sectionlock.h" + +#include +#include + +/** + * @file + * Mutual exclusion locking, header. + */ + +/** + * Recursive Mutual exclusive section. + */ +#define LUMIERA_RECMUTEX_SECTION(nobugflag, mtx) \ + for (lumiera_sectionlock NOBUG_CLEANUP(lumiera_sectionlock_ensureunlocked) \ + lumiera_lock_section_ = { \ + (void*)1, lumiera_mutex_unlock_cb NOBUG_ALPHA_COMMA_NULL NOBUG_ALPHA_COMMA_NULL}; \ + lumiera_lock_section_.lock;) \ + for ( \ + ({ \ + lumiera_lock_section_.lock = (mtx); \ + NOBUG_IF_ALPHA(lumiera_lock_section_.flag = &NOBUG_FLAG(nobugflag);) \ + RESOURCE_ENTER (nobugflag, (mtx)->rh, "acquire recmutex", &lumiera_lock_section_, \ + NOBUG_RESOURCE_WAITING, lumiera_lock_section_.rh); \ + if (pthread_mutex_lock (&(mtx)->recmutex)) \ + LUMIERA_DIE (LOCK_ACQUIRE); \ + RESOURCE_STATE (nobugflag, NOBUG_RESOURCE_RECURSIVE, lumiera_lock_section_.rh); \ + }); \ + lumiera_lock_section_.lock; \ + ({ \ + LUMIERA_MUTEX_SECTION_UNLOCK; \ + })) + + +#define LUMIERA_RECMUTEX_SECTION_CHAIN(nobugflag, mtx) \ + for (lumiera_sectionlock *lumiera_lock_section_old_ = &lumiera_lock_section_, \ + NOBUG_CLEANUP(lumiera_sectionlock_ensureunlocked) lumiera_lock_section_ = { \ + (void*)1, lumiera_mutex_unlock_cb NOBUG_ALPHA_COMMA_NULL NOBUG_ALPHA_COMMA_NULL}; \ + lumiera_lock_section_.lock;) \ + for ( \ + ({ \ + REQUIRE (lumiera_lock_section_old_->lock, "section prematurely unlocked"); \ + lumiera_lock_section_.lock = (mtx); \ + NOBUG_IF_ALPHA(lumiera_lock_section_.flag = &NOBUG_FLAG(nobugflag);) \ + RESOURCE_ENTER (nobugflag, (mtx)->rh, "acquire recmutex", &lumiera_lock_section_, \ + NOBUG_RESOURCE_WAITING, lumiera_lock_section_.rh); \ + if (pthread_mutex_lock (&(mtx)->recmutex)) \ + LUMIERA_DIE (LOCK_ACQUIRE); \ + RESOURCE_STATE (nobugflag, NOBUG_RESOURCE_RECURSIVE, lumiera_lock_section_.rh); \ + LUMIERA_SECTION_UNLOCK_(lumiera_lock_section_old_) \ + }); \ + lumiera_lock_section_.lock; \ + ({ \ + LUMIERA_MUTEX_SECTION_UNLOCK; \ + })) + + +#define LUMIERA_RECMUTEX_SECTION_UNLOCK \ + LUMIERA_SECTION_UNLOCK_(&lumiera_lock_section_) + + + +/** + * Mutex. + * + */ +struct lumiera_recmutex_struct +{ + pthread_mutex_t recmutex; + RESOURCE_HANDLE (rh); +}; +typedef struct lumiera_recmutex_struct lumiera_recmutex; +typedef lumiera_recmutex* LumieraRecmutex; + +/** + * Initialize a recursive mutex variable + * Initializes a 'recursive' mutex which might be locked by the same thread multiple times. + * @param self is a pointer to the mutex to be initialized + * @return self as given + */ +LumieraRecmutex +lumiera_recmutex_init (LumieraRecmutex self, const char* purpose, struct nobug_flag* flag); + +/** + * Destroy a recursive mutex variable + * @param self is a pointer to the mutex to be destroyed + * @return self as given + */ +LumieraRecmutex +lumiera_recmutex_destroy (LumieraRecmutex self, struct nobug_flag* flag); + +/** + * Callback for unlocking mutexes. + * @internal + */ +int +lumiera_mutex_unlock_cb (void* mutex); + + +#endif +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/lib/rwlock.c b/src/lib/rwlock.c index c5287d535..60929a458 100644 --- a/src/lib/rwlock.c +++ b/src/lib/rwlock.c @@ -60,6 +60,12 @@ lumiera_rwlock_destroy (LumieraRWLock self, struct nobug_flag* flag) return self; } + +int lumiera_rwlock_unlock_cb (void* rwlock) +{ + return pthread_rwlock_unlock (&((LumieraRWLock)rwlock)->rwlock); +} + /* // Local Variables: // mode: C diff --git a/src/lib/rwlock.h b/src/lib/rwlock.h index 23376979b..aa0814b37 100644 --- a/src/lib/rwlock.h +++ b/src/lib/rwlock.h @@ -45,53 +45,106 @@ LUMIERA_ERROR_DECLARE(RWLOCK_WRLOCK); /** * 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); \ - } \ +#define LUMIERA_RDLOCK_SECTION(nobugflag, rwlck) \ + for (lumiera_sectionlock NOBUG_CLEANUP(lumiera_sectionlock_ensureunlocked) \ + lumiera_lock_section_ = { \ + (void*)1, lumiera_rwlock_unlock_cb NOBUG_ALPHA_COMMA_NULL NOBUG_ALPHA_COMMA_NULL}; \ + lumiera_lock_section_.lock;) \ + for ( \ + ({ \ + lumiera_lock_section_.lock = (rwlck); \ + NOBUG_IF_ALPHA(lumiera_lock_section_.flag = &NOBUG_FLAG(nobugflag);) \ + RESOURCE_ENTER (nobugflag, (rwlck)->rh, "acquire readlock", \ + &lumiera_lock_section_, NOBUG_RESOURCE_WAITING, \ + lumiera_lock_section_.rh); \ + if (pthread_rwlock_rdlock (&(rwlck)->rwlock)) \ + LUMIERA_DIE (LOCK_ACQUIRE); \ + TODO ("implement NOBUG_RESOURCE_SHARED"); \ + /*RESOURCE_STATE (nobugflag, NOBUG_RESOURCE_SHARED, lumiera_lock_section_.rh); */ \ + }); \ + lumiera_lock_section_.lock; \ + ({ \ + LUMIERA_RWLOCK_SECTION_UNLOCK; \ })) +#define LUMIERA_RDLOCK_SECTION_CHAIN(nobugflag, rwlck) \ + for (lumiera_sectionlock *lumiera_lock_section_old_ = &lumiera_lock_section_, \ + NOBUG_CLEANUP(lumiera_sectionlock_ensureunlocked) lumiera_lock_section_ = { \ + (void*)1, lumiera_rwlock_unlock_cb NOBUG_ALPHA_COMMA_NULL NOBUG_ALPHA_COMMA_NULL}; \ + lumiera_lock_section_.lock;) \ + for ( \ + ({ \ + REQUIRE (lumiera_lock_section_old_->lock, "section prematurely unlocked"); \ + lumiera_lock_section_.lock = (rwlck); \ + NOBUG_IF_ALPHA(lumiera_lock_section_.flag = &NOBUG_FLAG(nobugflag);) \ + RESOURCE_ENTER (nobugflag, (rwlck)->rh, "acquire readlock", &lumiera_lock_section_, \ + NOBUG_RESOURCE_WAITING, lumiera_lock_section_.rh); \ + if (pthread_rwlock_rdlock (&(rwlck)->rwlock)) \ + LUMIERA_DIE (LOCK_ACQUIRE); \ + /*RESOURCE_STATE (nobugflag, NOBUG_RESOURCE_SHARED, lumiera_lock_section_.rh); */ \ + LUMIERA_SECTION_UNLOCK_(lumiera_lock_section_old_); \ + }); \ + lumiera_lock_section_.lock; \ + ({ \ + LUMIERA_MUTEX_SECTION_UNLOCK; \ + })) + + + + /** * 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); \ - } \ +#define LUMIERA_WRLOCK_SECTION(nobugflag, rwlck) \ + for (lumiera_sectionlock NOBUG_CLEANUP(lumiera_sectionlock_ensureunlocked) \ + lumiera_lock_section_ = { \ + (void*)1, lumiera_rwlock_unlock_cb NOBUG_ALPHA_COMMA_NULL NOBUG_ALPHA_COMMA_NULL}; \ + lumiera_lock_section_.lock;) \ + for ( \ + ({ \ + lumiera_lock_section_.lock = (rwlck); \ + NOBUG_IF_ALPHA(lumiera_lock_section_.flag = &NOBUG_FLAG(nobugflag);) \ + RESOURCE_ENTER (nobugflag, (rwlck)->rh, "acquire writelock", \ + &lumiera_lock_section_, NOBUG_RESOURCE_WAITING, \ + lumiera_lock_section_.rh); \ + if (pthread_rwlock_wrlock (&(rwlck)->rwlock)) \ + LUMIERA_DIE (LOCK_ACQUIRE); \ + RESOURCE_STATE (nobugflag, NOBUG_RESOURCE_EXCLUSIVE, lumiera_lock_section_.rh); \ + }); \ + lumiera_lock_section_.lock; \ + ({ \ + LUMIERA_RWLOCK_SECTION_UNLOCK; \ })) +#define LUMIERA_WRLOCK_SECTION_CHAIN(nobugflag, rwlck) \ + for (lumiera_sectionlock *lumiera_lock_section_old_ = &lumiera_lock_section_, \ + NOBUG_CLEANUP(lumiera_sectionlock_ensureunlocked) lumiera_lock_section_ = { \ + (void*)1, lumiera_rwlock_unlock_cb NOBUG_ALPHA_COMMA_NULL NOBUG_ALPHA_COMMA_NULL}; \ + lumiera_lock_section_.lock;) \ + for ( \ + ({ \ + REQUIRE (lumiera_lock_section_old_->lock, "section prematurely unlocked"); \ + lumiera_lock_section_.lock = (rwlck); \ + NOBUG_IF_ALPHA(lumiera_lock_section_.flag = &NOBUG_FLAG(nobugflag);) \ + RESOURCE_ENTER (nobugflag, (rwlck)->rh, "acquire writelock", &lumiera_lock_section_, \ + NOBUG_RESOURCE_WAITING, lumiera_lock_section_.rh); \ + if (pthread_rwlock_wrlock (&(twlck)->rwlock)) \ + LUMIERA_DIE (LOCK_ACQUIRE); \ + RESOURCE_STATE (nobugflag, NOBUG_RESOURCE_EXCLUSIVE, lumiera_lock_section_.rh); \ + LUMIERA_SECTION_UNLOCK_(lumiera_lock_section_old_); \ + }); \ + lumiera_lock_section_.lock; \ + ({ \ + LUMIERA_MUTEX_SECTION_UNLOCK; \ + })) + + +#define LUMIERA_RWLOCK_SECTION_UNLOCK \ + LUMIERA_SECTION_UNLOCK_(&lumiera_lock_section_) + + /** * RWLock. * @@ -121,26 +174,8 @@ LumieraRWLock lumiera_rwlock_destroy (LumieraRWLock self, struct nobug_flag* flag); - - - -/** - * rwlockacquirer used to manage the state of a rwlock variable. - */ -struct lumiera_rwlockacquirer_struct -{ - LumieraRWLock rwlock; - RESOURCE_HANDLE (rh); -}; -typedef struct lumiera_rwlockacquirer_struct lumiera_rwlockacquirer; -typedef struct lumiera_rwlockacquirer_struct* LumieraRWLockacquirer; - -/* helper function for nobug */ -static inline void -lumiera_rwlockacquirer_ensureunlocked (LumieraRWLockacquirer self) -{ - ENSURE (!self->rwlock, "forgot to unlock rwlock"); -} +int +lumiera_rwlock_unlock_cb (void* rwlock); #endif diff --git a/src/lib/sectionlock.h b/src/lib/sectionlock.h new file mode 100644 index 000000000..c50ecfc8d --- /dev/null +++ b/src/lib/sectionlock.h @@ -0,0 +1,82 @@ +/* + sectionlock.h - mutex state handle + + Copyright (C) Lumiera.org + 2008, 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 LUMIERA_SECTIONLOCK_H +#define LUMIERA_SECTIONLOCK_H + +#include "lib/error.h" + +#include +#include + +LUMIERA_ERROR_DECLARE (LOCK_ACQUIRE); +LUMIERA_ERROR_DECLARE (LOCK_RELEASE); +LUMIERA_ERROR_DECLARE (LOCK_DESTROY); + + +typedef int (*lumiera_sectionlock_unlock_fn)(void*); + +/** + * sectionlock used to manage the state of mutexes. + */ +struct lumiera_sectionlock_struct +{ + void* lock; + lumiera_sectionlock_unlock_fn unlock; + NOBUG_IF_ALPHA(struct nobug_flag* flag); + RESOURCE_HANDLE (rh); +}; +typedef struct lumiera_sectionlock_struct lumiera_sectionlock; +typedef struct lumiera_sectionlock_struct* LumieraSectionlock; + +/* helper function for nobug */ +static inline void +lumiera_sectionlock_ensureunlocked (LumieraSectionlock self) +{ + ENSURE (!self->lock, "forgot to unlock"); +} + + + +/** + * Unlock the lock hold in a SECTION + * @internal + * @param sectionname name used for the sectionlock instance + * @param ... some extra code to execute + */ +#define LUMIERA_SECTION_UNLOCK_(section) \ + do if ((section)->lock) \ + { \ + if ((section)->unlock((section)->lock)) \ + LUMIERA_DIE (LOCK_RELEASE); \ + (section)->lock = NULL; \ + NOBUG_RESOURCE_LEAVE_RAW((section)->flag, (section)->rh); \ + } while (0) + + +#endif +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/lib/slist.h b/src/lib/slist.h new file mode 100644 index 000000000..61fd9e10d --- /dev/null +++ b/src/lib/slist.h @@ -0,0 +1,666 @@ +/* + * slist.h - simple intrusive cyclic single linked list + * + * Copyright (C) Lumiera.org + * 2009 Anton Yakovlev + * + * 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 SLIST_H +#define SLIST_H + +#include + +/** + * @file + * Intrusive cyclic single linked list. + * + * List node is a structure, which consists only of a forward pointer. This is + * much easier and makes code much cleaner, than to have forward pointer as is. + * In a empty initialized node, this pointer points to the node itself. Note + * that this pointer can never ever become NULL. + * + * This lists are used by using one node as 'root' node where it's pointer is + * the head pointer to the actual list. Care needs to be taken to ensure not to + * apply any operations meant to be applied to data nodes to the root node. + * This way is the prefered way to use this lists. + * + * Alternatively one can store only a chain of data nodes and use a SList + * pointer to point to the first item (which might be NULL in case no data is + * stored). When using such approach care must be taken since most functions + * below expect lists to have a root node. + * + * Due to nature of single linked list, there's no easy way to implement + * functions, which need reverse passing through a list. But some of L1-list + * interface functions need such ability (for example, when we need to find + * previous element for current element). Because search of previous element + * requires visiting of exactly N-1 nodes (where N is length of L1-list), we + * use root node as start point. This gives to us probability of visiting + * 1 <= C <= N-1 nodes, and, thus, speed up search. + * + * This header can be used in 2 different ways: + * 1) (prerefered) just including it provides all functions as static inlined + * functions. This is the default + * 2) #define LLIST_INTERFACE before including this header gives only the declarations + * #define LLIST_IMPLEMENTATION before including this header yields in definitions + * this can be used to generate a library. This is currently untested and not + * recommended. + * The rationale for using inlined functions is that most functions are very + * small and likely to be used in performance critical parts. Inlining can give + * a hughe performance and optimization improvement here. The few functions + * which are slightly larger are expected to be the less common used ones, so + * inlining them too shouldn't be a problem either. + */ + +/* TODO __STDC_VERSION__ 199901L +150) This macro was not specified in ISO/IEC 9899:1990 and was specified as 199409L in +ISO/IEC 9899/AMD1:1995. The intention is that this will remain an integer constant of type long +int that is increased with each revision of this International Standard. +*/ + +#ifdef HAVE_INLINE +# define SLIST_MACRO static inline +#else +# ifdef __GNUC__ +# define SLIST_MACRO static __inline__ +# else +# define SLIST_MACRO static +# endif +#endif + +#if defined(SLIST_INTERFACE) +/* only the interface is generated */ +#define SLIST_FUNC(proto, ...) proto +#elif defined(SLIST_IMPLEMENTATION) +/* generate a non inlined implementation */ +#define SLIST_FUNC(proto, ...) proto { __VA_ARGS__ } +#else +/* all functions are macro-like inlined */ +#define SLIST_FUNC(proto, ...) SLIST_MACRO proto { __VA_ARGS__ } +#endif + +/* + * Type of a slist node. + */ + +#ifndef SLIST_DEFINED +#define SLIST_DEFINED + +struct slist_struct { + struct slist_struct* next; +}; + +#endif + +typedef struct slist_struct slist; +typedef slist* SList; +typedef const slist* const_SList; +typedef slist** SList_ref; + +/** + * Macro to instantiate a local llist. + * + * @param name of the slist node + */ + +#define SLIST_AUTO( name ) slist name = { &name } + +/* + * some macros for convenience + */ + +#define slist_head slist_next +#define slist_insert_head( list, element ) slist_insert( list, element ) + +/** + * Сast back from a member of a structure to a pointer of the structure. + * + * Example: + * + * struct point { + * int x; + * int y; + * slist list; + * }; + * + * SList points = ...; // some initialization; must be the root of our list + * + * SLIST_FOREACH( points, current_node ) { + * struct point* current_point = SLIST_TO_STRUCTP( current_node, struct point, list ); + * printf( "point = ( %d, %d )\n", current_point -> x, current_point -> y ); + * } + * + * @param list is a pointer to the SList member of the linked structures + * @param type is type name of the linked structures + * @param member is a name of the SList member of the linked structures + */ + +#define SLIST_TO_STRUCTP( list, type, member ) \ + ( ( type* ) ( ( ( char* )( list ) ) - offsetof( type, member ) ) ) + +/** + * Iterate forward over a list. + * + * @param list the root node of the list to be iterated + * @param node pointer to the iterated node + */ + +#define SLIST_FOREACH( list, node ) \ + for ( SList node = slist_head( list ); ! slist_is_end( node, list ); slist_forward( &node ) ) + +/** + * Iterate forward over a range. + * + * @param start first node to be interated + * @param end node after the last node be iterated + * @param node pointer to the iterated node + */ + +#define SLIST_FORRANGE( start, end, node ) \ + for ( SList node = start; node != end; slist_forward( &node ) ) + +/** + * Consume a list from head. + * The body of this statement should remove the head from the list, else it would be a infinite loop + * + * @param list the root node of the list to be consumed + * @param head pointer to the head node + */ + +#define SLIST_WHILE_HEAD( list, head ) \ + for ( SList head = slist_head( list ); ! slist_is_empty( list ); head = slist_head( list ) ) + +/** + * Initialize a new llist. + * Must not be applied to a list node which is not empty! Lists need to be initialized + * before any other operation on them is called. + * + * @param list node to be initialized + */ + +SLIST_FUNC ( + void slist_init( SList list ), + list -> next = list; +); + +/** + * Check if a node is not linked with some other node. + */ + +SLIST_FUNC ( + int slist_is_empty( const_SList list ), + return list -> next == list; +); + +/** + * Check if self is the only node in a list or self is not in a list. + * + * Warning: + * There's no check for empty list, so if you have a list with no items, + * you'll get seg fault here. + * + * @param list is root node of the list to be checked + */ + +SLIST_FUNC ( + int slist_is_single( const_SList list ), + return list -> next -> next == list; +); + +/** + * Check for the head of a list. + * + * @param list is root node of the list + * @param head is expected head of the list + */ + +SLIST_FUNC ( + int slist_is_head( const_SList list, const_SList head ), + return list -> next == head; +); + +/** + * Check for the end of a list. + * The end is by definition one past the tail of a list, which is the root node itself. + * + * @param list is root node of the list + * @param end is expected end of the list + */ + +SLIST_FUNC ( + int slist_is_end( const_SList list, const_SList end ), + return list == end; +); + +/** + * Check if a node is a member of a list. + * + * @param list is root node of the list + * @param member is node to be searched + */ + +SLIST_FUNC ( + int slist_is_member( const_SList list, const_SList member ), + for ( const_SList i = member -> next; i != member; i = i -> next ) { + if ( i == list ) { + return 1; + } + } + return 0; +); + +/** + * Check the order of elements in a list. + * + * @param list is root node of the list + * @param before is expected to be before after + * @param after is expected to be after before + */ + +SLIST_FUNC ( + int slist_is_before_after( const_SList list, const_SList before, const_SList after ), + for ( const_SList i = before -> next; i != list; i = i -> next ) { + if ( i == after ) { + return 1; + } + } + return 0; +); + +/** + * Count the nodes of a list. + * + * @param list is root node of the list + * @return number of nodes in `list` + */ + +SLIST_FUNC ( + unsigned slist_count( const_SList list ), + unsigned cnt = 0; + for ( const_SList i = list; i -> next != list; ++cnt, i = i -> next ) { + ; + } + return cnt; +); + +/** + * Get next node. + * Will not stop at tail. + * + * @param node is current node + * @return node after current node + */ + +SLIST_FUNC ( + SList slist_next( const_SList node ), + return node -> next; +); + +/** + * Get previous node. + * + * @param list is root node of the list + * @param node is current node + * @return node before current node + */ + +SLIST_FUNC ( + SList slist_prev( SList list, SList node ), + while ( list -> next != node ) { + list = list -> next; + } + return list; +); + +/** + * Remove a node from a list. + * + * @param list is root node of the list + * @param node to be removed + * @return node + */ + +SLIST_FUNC ( + SList slist_unlink( SList list, SList node ), + SList prev_node = slist_prev( list, node ); + prev_node -> next = node -> next; + return node -> next = node; +); + +/** + * Insert a node after another. + * + * @param head is node after which we want to insert + * @param node is node which shall be inserted after `head`. Could already linked to a list from where it will be removed. + * @return head + */ + +SLIST_FUNC ( + SList slist_insert( SList head, SList node ), + if ( ! slist_is_empty( node ) ) { + slist_unlink( node, node ); + } + node -> next = head -> next; + head -> next = node; + return head; +); + +/** + * Move the content of a list after a node in another list. + * + * @param xnode is node after which we want to insert a list + * @param ylist is root node of the list which shall be inserted after self. This list will be empty after call. + * @return xnode + */ + +SLIST_FUNC ( + SList slist_insert_list( SList xnode, SList ylist ), + if ( ! slist_is_empty( ylist ) ) { + SList tail = slist_prev( ylist, ylist ); // search for the Y list tail + tail -> next = xnode -> next; + xnode -> next = ylist -> next; + + ylist -> next = ylist; // clear the Y list + } + return xnode; +); + +/** + * Move a range of nodes after a given node. + * + * @param node is node after which the range shall be inserted + * @param start first node in range to be moved + * @param end node after the last node of the range + * @return node + */ + +SLIST_FUNC ( + SList slist_insert_range( SList node, SList start, SList end ), + // insert range + SList tail = slist_prev( start, end ); // search for the end of range + tail -> next = node -> next; + node -> next = start -> next; + // fix list + start -> next = end; + return node; +); + +/** + * Swap a node with its next node. + * Advancing will not stop at tail, one has to check that if this is intended. + * + * @param list is root node of the list + * @param node is node to be advaced + * @return node + */ + +SLIST_FUNC ( + SList slist_advance( SList list, SList node ), + SList prev = slist_prev( list, node ); + prev -> next = node -> next; + node -> next = node -> next -> next; + prev -> next -> next = node; + return node; +); + +/** + * Advance a pointer to a node to its next node. + * + * @param node pointer-to-pointer to the current node. `node` will point to the next node after this call. + */ + +SLIST_FUNC ( + void slist_forward( SList_ref node ), + *node = ( *node ) -> next; +); + +/** + * Get the nth element of a list (this function does not stop at head/tail). + * + * @param list is root node of the list to be queried + * @param n is number of element to find + * @return |n|-th element of list + */ + +SLIST_FUNC ( + SList slist_get_nth( SList list, unsigned int n ), + while ( n-- > 0 ) { + list = slist_next( list ); + } + return list; +); + +/** + * Get the nth element of a list with a stop node. + * + * @param list is root node of the list to be queried + * @param n is number of element to find + * @param stop is node which will abort the iteration + * @return |n|-th element of list or NULL if `stop` node has been reached + */ + +SLIST_FUNC ( + SList slist_get_nth_stop( SList list, unsigned int n, const_SList stop ), + while ( n-- > 0 ) { + list = slist_next( list ); + if ( list == stop ) { + return NULL; + } + } + return list; +); + +/** + * Sort a list. + * + * This is iterative version of bottom-up merge sort for (L1/L2) linked-list: + * + there's no recursion + * + there's no extra stackspace allocation (only a few bytes for locals) + * Such implementation should be optimal and fast enough. + * + * Maybe this function is too big for inlining (though I don't think so), so + * maybe somebody can make it smaller without losing perfomance? ;) + * + * @param list is root node of a list to be sorted + * @param cmp is compare function of 2 SList items + * @return list + */ + +typedef int ( *slist_cmpfn )( const_SList a, const_SList b ); + +SLIST_FUNC ( + SList slist_sort( SList list, slist_cmpfn cmp ), + if ( ! slist_is_single( list ) ) { + + unsigned int length = slist_count( list ); + + // `max_size` is a half of minimum power of 2, greater of equal to `length` + // ( 2 * max_size = 2^k ) >= length + // We need `max_size` value for proper binary division of a list for sorting. + + unsigned long long max_size = 1; + while ( ( max_size << 1 ) < length ) { + max_size <<= 1; + } + + // The main idea of bottom-up merge sort is sequential merging of each pair + // of sequences of { 1, .. 2^k, .. max_size } length. That's all. :) + + for ( unsigned int size = 1; size <= max_size; size <<= 1 ) { + + // On each iteration: + // * `result` points to the current node of global (merged/sorted) list. + // thus, we can holds all nodes are linked. + // * `left` and `right` points to begin of (sub)lists for merging. + + SList result = list; + SList left = list -> next; + SList right; + + // Process each pairs of sequences of size=2^k length. + + for ( unsigned int position = 0; position < length; position += size + size ) { + + right = slist_get_nth_stop( left, size, list ); + + unsigned int size_left = size; + unsigned int size_right = right == NULL ? 0 : size; + + // Here we have 2 sublists of `size_left` and `size_right` sizes. + // Implementation of `merge` function is next three loops. + + while ( ( size_left > 0 ) && ( size_right > 0 ) ) { + + if ( cmp( left, right ) <= 0 ) { + + result -> next = left; + + left = left -> next; + if ( left == list ) { + size_left = 0; + } else { + size_left--; + } + + } else { + + result -> next = right; + + right = right -> next; + if ( right == list ) { + size_right = 0; + } else { + size_right--; + } + + } + + result = result -> next; + + } + + while ( size_left > 0 ) { + result -> next = left; + result = left; + + left = left -> next; + if ( left == list ) { + break; + } + size_left--; + } + + while ( size_right > 0 ) { + result -> next = right; + result = right; + + right = right -> next; + if ( right == list ) { + break; + } + size_right--; + } + + // go to begin of next pair of sequences + + left = right; + + } + + // here `result` points to the last node of a list. + // we wanna keep cyclic list. + + result -> next = list; + + } + + } + return list; +) + +/** + * Find the first occurence of an element in a list. + * Does not change the order of a list. + * + * @param list is root node of a list to be searched + * @param pattern is template for the element being searched + * @param cmp is compare function of 2 SList items + * @return pointer to the found SList element or NULL if nothing found + */ + +SLIST_FUNC ( + SList slist_find( const_SList list, const_SList pattern, slist_cmpfn cmp ), + SLIST_FOREACH( list, node ) { + if ( cmp( node, pattern ) == 0 ) { + return node; + } + } + return NULL; +) + +/** + * Find the first occurence of an element in an unsorted list. + * + * Searches the list until it finds the searched element and moves it then to + * the head. Useful if the order of the list is not required and few elements + * are frequently searched. + * + * @param list is root node of a list to be searched + * @param pattern is template for the element being searched + * @param cmp is compare function of 2 SList items + * @return pointer to the found SList element (head) or NULL if nothing found + */ + +SLIST_FUNC ( + SList slist_ufind( SList list, const_SList pattern, slist_cmpfn cmp ), + SLIST_FOREACH( list, node ) { + if ( cmp( node, pattern ) == 0 ) { + slist_insert_head( list, node ); + return node; + } + } + return NULL; +) + +/** + * Find the first occurence of an element in a sorted list. + * + * Searches the list until it finds the searched element, exits searching when + * found an element biggier than the searched one. + * + * @param list is root node of a list to be searched + * @param pattern is template for the element being searched + * @param cmp is compare function of 2 SList items + * @return pointer to the found SList element (head) or NULL if nothing found + */ + +SLIST_FUNC ( + SList slist_sfind( const_SList list, const_SList pattern, slist_cmpfn cmp ), + SLIST_FOREACH( list, node ) { + + int result = cmp( node, pattern ); + + if ( result == 0 ) { + return node; + } else if ( result > 0 ) { + break; + } + + } + return NULL; +) + +#endif /* SLIST_H */ diff --git a/src/lib/sync.hpp b/src/lib/sync.hpp index 6515f35b9..4d3fa0757 100644 --- a/src/lib/sync.hpp +++ b/src/lib/sync.hpp @@ -73,6 +73,7 @@ extern "C" { #include "lib/mutex.h" +#include "lib/recmutex.h" #include "lib/condition.h" #include "lib/reccondition.h" } @@ -98,25 +99,29 @@ namespace lib { protected: Wrapped_LumieraExcMutex() { lumiera_mutex_init (this, "Obj.Monitor ExclMutex", &NOBUG_FLAG(sync)); } ~Wrapped_LumieraExcMutex() { lumiera_mutex_destroy (this, &NOBUG_FLAG(sync)); } - + + pthread_mutex_t* mutex() { return &(lumiera_mutex::mutex); } + //------------------Resource-Tracking------ void __may_block() { TODO ("Record we may block on mutex"); } void __enter() { TODO ("Record we successfully acquired the mutex"); } - void __leave() { TODO ("Record we are releasing the mutex"); } + void __left() { TODO ("Record we released the mutex"); } }; struct Wrapped_LumieraRecMutex - : public lumiera_mutex + : public lumiera_recmutex { protected: Wrapped_LumieraRecMutex() { lumiera_recmutex_init (this, "Obj.Monitor RecMutex", &NOBUG_FLAG(sync)); } - ~Wrapped_LumieraRecMutex() { lumiera_mutex_destroy (this, &NOBUG_FLAG(sync)); } - + ~Wrapped_LumieraRecMutex() { lumiera_recmutex_destroy (this, &NOBUG_FLAG(sync)); } + + pthread_mutex_t* mutex() { return &recmutex; } + //------------------Resource-Tracking------ void __may_block() { TODO ("Record we may block on mutex"); } void __enter() { TODO ("Record we successfully acquired the mutex"); } - void __leave() { TODO ("Record we are releasing the mutex"); } + void __left() { TODO ("Record we are released the mutex"); } }; @@ -126,11 +131,14 @@ namespace lib { protected: Wrapped_LumieraExcCond() { lumiera_condition_init (this, "Obj.Monitor ExclCondition", &NOBUG_FLAG(sync) ); } ~Wrapped_LumieraExcCond() { lumiera_condition_destroy (this, &NOBUG_FLAG(sync) ); } - + + pthread_mutex_t* mutex() { return &cndmutex; } + pthread_cond_t* condv() { return &cond; } + //------------------Resource-Tracking------ void __may_block() { TODO ("Record we may block on mutex"); } void __enter() { TODO ("Record we successfully acquired the mutex"); } - void __leave() { TODO ("Record we are releasing the mutex"); } + void __left() { TODO ("Record we released the mutex"); } }; @@ -140,11 +148,14 @@ namespace lib { protected: Wrapped_LumieraRecCond() { lumiera_reccondition_init (this, "Obj.Monitor RecCondition", &NOBUG_FLAG(sync) ); } ////////TODO ~Wrapped_LumieraRecCond() { lumiera_reccondition_destroy (this, &NOBUG_FLAG(sync) ); } - + + pthread_mutex_t* mutex() { return &reccndmutex; } + pthread_cond_t* condv() { return &cond; } + //------------------Resource-Tracking------ void __may_block() { TODO ("Record we may block on mutex"); } void __enter() { TODO ("Record we successfully acquired the mutex"); } - void __leave() { TODO ("Record we are releasing the mutex"); } + void __left() { TODO ("Record we released the mutex"); } }; @@ -159,7 +170,7 @@ namespace lib { using MTX::mutex; using MTX::__may_block; using MTX::__enter; - using MTX::__leave; + using MTX::__left; ~Mutex () { } Mutex () { } @@ -172,7 +183,7 @@ namespace lib { { __may_block(); - if (pthread_mutex_lock (&mutex)) + if (pthread_mutex_lock (mutex())) throw lumiera::error::State("Mutex acquire failed."); ///////TODO capture the error-code __enter(); @@ -181,9 +192,9 @@ namespace lib { void release() { - __leave(); + pthread_mutex_unlock (mutex()); - pthread_mutex_unlock (&mutex); + __left(); } }; @@ -197,7 +208,7 @@ namespace lib { : public Mutex { protected: - using CDX::cond; + using CDX::condv; using CDX::mutex; public: @@ -205,9 +216,9 @@ namespace lib { signal (bool wakeAll=false) { if (wakeAll) - pthread_cond_broadcast (&cond); + pthread_cond_broadcast (condv()); else - pthread_cond_signal (&cond); + pthread_cond_signal (condv()); } @@ -218,9 +229,9 @@ namespace lib { int err=0; while (!predicate() && !err) if (waitEndTime) - err = pthread_cond_timedwait (&cond, &mutex, &waitEndTime); + err = pthread_cond_timedwait (condv(), mutex(), &waitEndTime); else - err = pthread_cond_wait (&cond, &mutex); + err = pthread_cond_wait (condv(), mutex()); if (!err) return true; if (ETIMEDOUT==err) return false; @@ -332,7 +343,7 @@ namespace lib { void setTimeout(ulong relative) {timeout_.setOffset(relative);} bool isTimedWait() {return (timeout_);} - LumieraCondition accessCond() {return static_cast (this);} + LumieraReccondition accessCond(){return static_cast (this);} }; typedef Mutex NonrecursiveLock_NoWait; @@ -436,3 +447,10 @@ namespace lib { } // namespace lumiera #endif +/* +// Local Variables: +// mode: C++ +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/proc/mobject/session/clip.cpp b/src/proc/mobject/session/clip.cpp index 6c8965a7e..12f3ba0f9 100644 --- a/src/proc/mobject/session/clip.cpp +++ b/src/proc/mobject/session/clip.cpp @@ -78,3 +78,4 @@ namespace session { }} // namespace mobject::session + diff --git a/src/proc/play/tick-service.hpp b/src/proc/play/tick-service.hpp index b27f936a3..c1df386c6 100644 --- a/src/proc/play/tick-service.hpp +++ b/src/proc/play/tick-service.hpp @@ -57,7 +57,8 @@ namespace proc { * with adjustable frequency. Quick'n dirty implementation! */ class TickService - : backend::Thread + : backend::JoinHandle, + backend::Thread { typedef function Tick; volatile uint timespan_; @@ -68,16 +69,18 @@ namespace proc { public: TickService (Tick callback) : Thread("Tick generator (dummy)", - bind (&TickService::timerLoop, this, callback)) + bind (&TickService::timerLoop, this, callback), + (backend::JoinHandle&)*this + ) { INFO (proc, "TickService started."); } ~TickService () { - uint curr_tick = timespan_; timespan_ = 0; - usleep (2*curr_tick); ////TODO actually should wait for timer thread termination + this->join(); + usleep (200000); // additional delay allowing GTK to dispatch the last output INFO (proc, "TickService shutdown."); } @@ -91,8 +94,8 @@ namespace proc { void activate (uint fps) { REQUIRE ( 0==fps - || 1000000/fps < std::numeric_limits::max() - && 1000000/fps > POLL_TIMEOUT); + ||( 1000000/fps < std::numeric_limits::max() + && 1000000/fps > POLL_TIMEOUT)); if (fps) timespan_ = 1000000/fps; // microseconds per tick else diff --git a/tests/15list.tests b/tests/15list.tests index e04044f27..1e6558e49 100644 --- a/tests/15list.tests +++ b/tests/15list.tests @@ -2,31 +2,22 @@ TESTING "Linked Lists" ./test-llist TEST "init nodes" basic <line = ' #comment bla' +TEST "check content of configitem with comment" configitem_simple_content_check $' #comment bla' << END +out: item->line = ' #comment bla' END TEST "check content of configitem with section" configitem_simple_content_check $'[ key.foo suffix.bar ] ' << END -out: item->line = '[ key.foo suffix.bar ] ' +out: item->line = '\[ key\.foo suffix.bar \] ' out: item->key_size = '7' -out: item->key = 'key.foo suffix.bar ] ' -out: item->delim = ' suffix.bar ] ' +out: item->key = 'key.foo suffix.bar \] ' +out: item->delim = ' suffix.bar \] ' END -TEST "check content of configitem with directive (without argument)" configitem_simple_content_check $'\t @directive ' << END -out: item->line = ' @directive ' +TEST "check content of configitem with directive (without argument)" configitem_simple_content_check $' @directive' << END +out: item->line = ' @directive' out: item->key_size = '9' -out: item->key = '@directive ' +out: item->key = '@directive' END -TEST "check content of configitem with directive (with argument)" configitem_simple_content_check $'\t @directive \targument' << END -out: item->line = ' @directive argument' +TEST "check content of configitem with directive (with argument)" configitem_simple_content_check $' @directive argument' << END +out: item->line = ' @directive argument' out: item->key_size = '9' -out: item->key = '@directive argument' -out: item->delim = ' argument' +out: item->key = '@directive argument' +out: item->delim = ' argument' END -TEST "check content of configitem with configentry" configitem_simple_content_check $' \t\t key.foo \t\t=\tbar' << END -out: item->line = ' key.foo = bar' +TEST "check content of configitem with configentry" configitem_simple_content_check $'key.foo =bar' << END +out: item->line = 'key.foo =bar' out: item->key_size = '7' -out: item->key = 'key.foo = bar' -out: item->delim = '= bar' +out: item->key = 'key.foo =bar' +out: item->delim = '=bar' END -TEST "check content of configitem with configentry (redirect)" configitem_simple_content_check $' \t\t key.foo \t\t<\tkey.bar' << END -out: item->line = ' key.foo < key.bar' +TEST "check content of configitem with configentry (redirect)" configitem_simple_content_check $'key.foo line = 'key.foo key_size = '7' -out: item->key = 'key.foo < key.bar' -out: item->delim = '< key.bar' +out: item->key = 'key.foo delim = ' out: wrapping cmdline: -out: ... +out: \.\.\. out: --> out: wrapping cmdline:spam... -out: 0|spam| +out: 0\|spam\| out: -->spam out: wrapping cmdline: out: spam... -out: 0|spam| +out: 0\|spam\| out: -->spam out: wrapping cmdline:eat more spam... -out: 0|eat| -out: 1|more| -out: 2|spam| +out: 0\|eat\| +out: 1\|more\| +out: 2\|spam\| out: -->eat more spam -out: wrapping cmdline: oo _O()O_ ä + €... -out: 0|oo| -out: 1|_O()O_| -out: 2|ä| -out: 3|+| -out: 4|€| -out: -->oo _O()O_ ä + € +out: wrapping cmdline: oo _O\(\)O_ ä \+ €... +out: 0\|oo\| +out: 1\|_O\(\)O_\| +out: 2\|ä\| +out: 3\|\+\| +out: 4\|€\| +out: -->oo _O\(\)O_ ä \+ € out: wrapping cmdline:Ω ooΩ oΩo Ωoo... -out: 0|Ω| -out: 1|ooΩ| -out: 2|oΩo| -out: 3|Ωoo| +out: 0\|Ω\| +out: 1\|ooΩ\| +out: 2\|oΩo\| +out: 3\|Ωoo\| out: -->Ω ooΩ oΩo Ωoo out: Standard Cmdlineformat:one two END @@ -62,98 +60,98 @@ out: Conf2 :-<2>- out: Conf3 :-<3>- out: Conf4 :-<2>-<4>- out: AllFlags :-<1>-<2>-<3>-<4>- -out: __________________________ -out: __________________________ check_flags() +out: __________________________$ +out: __________________________ check_flags\(\) out: Flags1 :-<2>-<4>- out: Flags2 :-<2>-<4>- out: SimpleConfig_defined_by_Typelist :-<1>- out: AnotherConfig_defined_by_Typelist :-<1>-<2>-<3>-<4>- -out: __________________________ -out: __________________________ check_instantiation() -out: defined Conf0? ---> 0 -out: defined Conf1? ---> 1 -out: defined Conf2? ---> 1 -out: defined Conf3? ---> 1 -out: defined Conf4? ---> 1 -out: defined Trash? ---> 0 -out: __________________________ -out: __________________________ check_filter() +out: __________________________$ +out: __________________________ check_instantiation\(\) +out: defined Conf0\? ---> 0 +out: defined Conf1\? ---> 1 +out: defined Conf2\? ---> 1 +out: defined Conf3\? ---> 1 +out: defined Conf4\? ---> 1 +out: defined Trash\? ---> 0 +out: __________________________$ +out: __________________________ check_filter\(\) out: SomeFlagsets : -out: +---<1>-<3>-+ -out: +---<2>-<4>-+- +out: \+---<1>-<3>-\+ +out: \+---<2>-<4>-\+- out: Configs_defined_by_Flagsets : -out: +-Conf-[-<1>-<3>-] -out: +-Conf-[-<2>-<4>-]- +out: \+-Conf-\[-<1>-<3>-\] +out: \+-Conf-\[-<2>-<4>-\]- out: Filter_possible_Configs : -out: +-Conf-[-<2>-<4>-]- +out: \+-Conf-\[-<2>-<4>-\]- out: AllFlagCombinations : -out: +---<1>-<2>-<3>-<4>-<·>-+ -out: +---<1>-<2>-<3>-<·>-+ -out: +---<1>-<2>-<4>-<·>-+ -out: +---<1>-<2>-<·>-+ -out: +---<1>-<3>-<4>-<·>-+ -out: +---<1>-<3>-<·>-+ -out: +---<1>-<4>-<·>-+ -out: +---<1>-<·>-+ -out: +---<2>-<3>-<4>-<·>-+ -out: +---<2>-<3>-<·>-+ -out: +---<2>-<4>-<·>-+ -out: +---<2>-<·>-+ -out: +---<3>-<4>-<·>-+ -out: +---<3>-<·>-+ -out: +---<4>-<·>-+ -out: +---<·>-+- +out: \+---<1>-<2>-<3>-<4>-<·>-\+ +out: \+---<1>-<2>-<3>-<·>-\+ +out: \+---<1>-<2>-<4>-<·>-\+ +out: \+---<1>-<2>-<·>-\+ +out: \+---<1>-<3>-<4>-<·>-\+ +out: \+---<1>-<3>-<·>-\+ +out: \+---<1>-<4>-<·>-\+ +out: \+---<1>-<·>-\+ +out: \+---<2>-<3>-<4>-<·>-\+ +out: \+---<2>-<3>-<·>-\+ +out: \+---<2>-<4>-<·>-\+ +out: \+---<2>-<·>-\+ +out: \+---<3>-<4>-<·>-\+ +out: \+---<3>-<·>-\+ +out: \+---<4>-<·>-\+ +out: \+---<·>-\+- out: ListAllConfigs : -out: +-Conf-[-<1>-<2>-<3>-<4>-] -out: +-Conf-[-<1>-<2>-<3>-] -out: +-Conf-[-<1>-<2>-<4>-] -out: +-Conf-[-<1>-<2>-] -out: +-Conf-[-<1>-<3>-<4>-] -out: +-Conf-[-<1>-<3>-] -out: +-Conf-[-<1>-<4>-] -out: +-Conf-[-<1>-] -out: +-Conf-[-<2>-<3>-<4>-] -out: +-Conf-[-<2>-<3>-] -out: +-Conf-[-<2>-<4>-] -out: +-Conf-[-<2>-] -out: +-Conf-[-<3>-<4>-] -out: +-Conf-[-<3>-] -out: +-Conf-[-<4>-] -out: +-Conf-[-]- +out: \+-Conf-\[-<1>-<2>-<3>-<4>-\] +out: \+-Conf-\[-<1>-<2>-<3>-\] +out: \+-Conf-\[-<1>-<2>-<4>-\] +out: \+-Conf-\[-<1>-<2>-\] +out: \+-Conf-\[-<1>-<3>-<4>-\] +out: \+-Conf-\[-<1>-<3>-\] +out: \+-Conf-\[-<1>-<4>-\] +out: \+-Conf-\[-<1>-\] +out: \+-Conf-\[-<2>-<3>-<4>-\] +out: \+-Conf-\[-<2>-<3>-\] +out: \+-Conf-\[-<2>-<4>-\] +out: \+-Conf-\[-<2>-\] +out: \+-Conf-\[-<3>-<4>-\] +out: \+-Conf-\[-<3>-\] +out: \+-Conf-\[-<4>-\] +out: \+-Conf-\[-\]- out: Filter_all_possible_Configs : -out: +-Conf-[-<1>-] -out: +-Conf-[-<2>-<3>-] -out: +-Conf-[-<2>-<4>-] -out: +-Conf-[-<2>-] -out: +-Conf-[-<3>-]- -out: __________________________ -out: __________________________ check_FlagInfo() +out: \+-Conf-\[-<1>-\] +out: \+-Conf-\[-<2>-<3>-\] +out: \+-Conf-\[-<2>-<4>-\] +out: \+-Conf-\[-<2>-\] +out: \+-Conf-\[-<3>-\]- +out: __________________________$ +out: __________________________ check_FlagInfo\(\) out: Flags1 :-<1>-<3>- out: max bit : 3 out: binary code: 10 out: SomeConfigs : -out: +-Conf-[-<1>-<3>-] -out: +-Conf-[-<2>-<4>-]- -out: max bit in [SomeConfigs] : 4 +out: \+-Conf-\[-<1>-<3>-\] +out: \+-Conf-\[-<2>-<4>-\]- +out: max bit in \[SomeConfigs\] : 4 out: TestVisitor application: -out: visit(code=10) --> -out: +-Conf-[-<1>-<3>-]- -out: visit(code=20) --> -out: +-Conf-[-<2>-<4>-]- -out: __________________________ -out: __________________________ check_ConfigSelector() +out: visit\(code=10\) --> +out: \+-Conf-\[-<1>-<3>-\]- +out: visit\(code=20\) --> +out: \+-Conf-\[-<2>-<4>-\]- +out: __________________________$ +out: __________________________ check_ConfigSelector\(\) out: Possible_Configs : -out: +-Conf-[-<1>-] -out: +-Conf-[-<2>-<3>-] -out: +-Conf-[-<2>-<4>-] -out: +-Conf-[-<2>-] -out: +-Conf-[-<3>-]- -out: Flag-code = 2 ConfigSelector() ---> 1010 -out: Flag-code = 12 ConfigSelector() ---> 1023 -out: Flag-code = 20 ConfigSelector() ---> 1024 -out: Flag-code = 4 ConfigSelector() ---> 1020 -out: Flag-code = 8 ConfigSelector() ---> 1030 -out: LUMIERA_ERROR_INVALID:invalid input or parameters (ConfigSelector: No preconfigured factory for config-bits=10111). +out: \+-Conf-\[-<1>-\] +out: \+-Conf-\[-<2>-<3>-\] +out: \+-Conf-\[-<2>-<4>-\] +out: \+-Conf-\[-<2>-\] +out: \+-Conf-\[-<3>-\]- +out: Flag-code = 2 ConfigSelector\(\) ---> 1010 +out: Flag-code = 12 ConfigSelector\(\) ---> 1023 +out: Flag-code = 20 ConfigSelector\(\) ---> 1024 +out: Flag-code = 4 ConfigSelector\(\) ---> 1020 +out: Flag-code = 8 ConfigSelector\(\) ---> 1030 +out: LUMIERA_ERROR_INVALID:invalid input or parameters \(ConfigSelector: No preconfigured factory for config-bits=10111\)\. return: 0 END @@ -164,54 +162,54 @@ END TEST "ExceptionError_test" ExceptionError_test < [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ] -out: removed 0 ---> [ 1, 2, 3, 4, 5, 6, 7, 8, 9, ] -out: removed 9 ---> [ 0, 1, 2, 3, 4, 5, 6, 7, 8, ] -out: removed 5 ---> [ 0, 1, 2, 3, 4, 6, 7, 8, 9, ] -out: removed 0 2 4 6 8 ---> [ 1, 3, 5, 7, 9, ] -out: removed 1 3 5 7 9 ---> [ 0, 2, 4, 6, 8, ] -out: removed 0 1 2 3 4 5 6 7 8 9 ---> [ ] -out: removed 0 1 2 3 4 5 6 7 8 ---> [ 9, ] -out: removed 1 2 3 4 5 6 7 8 9 ---> [ 0, ] -out: removed 0 1 2 3 4 6 7 8 9 ---> [ 5, ] +out: removed nothing ---> \[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, \] +out: removed 0 ---> \[ 1, 2, 3, 4, 5, 6, 7, 8, 9, \] +out: removed 9 ---> \[ 0, 1, 2, 3, 4, 5, 6, 7, 8, \] +out: removed 5 ---> \[ 0, 1, 2, 3, 4, 6, 7, 8, 9, \] +out: removed 0 2 4 6 8 ---> \[ 1, 3, 5, 7, 9, \] +out: removed 1 3 5 7 9 ---> \[ 0, 2, 4, 6, 8, \] +out: removed 0 1 2 3 4 5 6 7 8 9 ---> \[ \] +out: removed 0 1 2 3 4 5 6 7 8 ---> \[ 9, \] +out: removed 1 2 3 4 5 6 7 8 9 ---> \[ 0, \] +out: removed 0 1 2 3 4 6 7 8 9 ---> \[ 5, \] END @@ -246,7 +244,7 @@ out: 'trailing Withespace out: ' --> 'trailing_Withespace' out: 'with a lot out: of Whitespace' --> 'with_a_lot_of_Whitespace' -out: 'with"much (punctuation)[]!' --> 'withmuch_(punctuation)' +out: 'with"much \(punctuation\)\[\]!' --> 'withmuch_\(punctuation\)' out: '§&Ω%€ leading garbage' --> 'leading_garbage' out: 'mixed Ω garbage' --> 'mixed_garbage' out: 'Bääääh!!' --> 'Bh' @@ -255,24 +253,24 @@ END TEST "SingletonSubclass_test" SingletonSubclass_test 13 <... -out: . -out: ..install one element at index[0] -out: . -out: ..*** resize table to 16 elements -out: . -out: .throw some exceptions... +out: \.$ +out: \.\.install one element at index\[0\] +out: ^\.$ +out: \.\.\*\*\* resize table to 16 elements +out: ^\.$ +out: ^\.throw some exceptions... out: checking ScopedPtrHolder... -out: . -out: ..install one element at index[0] -out: . -out: ..*** resize table to 16 elements -out: . -out: .throw some exceptions... +out: ^\.$ +out: ..install one element at index\[0\] +out: ^\.$ +out: ..\*\*\* resize table to 16 elements +out: ^\.$ +out: ^\.throw some exceptions... END @@ -306,16 +304,16 @@ END TEST "Singleton_test" Singleton_test 23 < > out: ctor DoIt > out: ctor DoIt > out: ctor DoIt > -out: Block< 2>::eat(..) -out: Block< 5>::eat(..) -out: Block<13>::eat(..) +out: Block< 2>::eat\(..\) +out: Block< 5>::eat\(..\) +out: Block<13>::eat\(..\) out: gulp! out: dtor DoIt > out: dtor DoIt > @@ -457,87 +456,87 @@ out: Append8 :-<1>-<2>-<3>-<222>- out: Append9 :-<1>-<2>-<3>-<5>-<6>-<7>- out: FilterEven :-<2>-<6>- out: Prefix1 : -out: +---<11>-<22>-+- +out: \+---<11>-<22>-\+- out: Prefix2 : -out: +---<101>-<1>-+ -out: +---<101>-<2>-+ -out: +---<101>-<3>-+- +out: \+---<101>-<1>-\+ +out: \+---<101>-<2>-\+ +out: \+---<101>-<3>-\+- out: Prefix3 : -out: +---<1>-+ -out: +---<2>-+ -out: +---<3>-+- +out: \+---<1>-\+ +out: \+---<2>-\+ +out: \+---<3>-\+- out: Prefix4 : -out: +---<111>-<1>-<2>-<3>-+ -out: +---<111>-<0>-+ -out: +---<111>-<5>-<6>-<7>-+- +out: \+---<111>-<1>-<2>-<3>-\+ +out: \+---<111>-<0>-\+ +out: \+---<111>-<5>-<6>-<7>-\+- out: Prefix5 : -out: +---<1>-<2>-<3>-<5>-+ -out: +---<1>-<2>-<3>-<6>-+ -out: +---<1>-<2>-<3>-<7>-+- +out: \+---<1>-<2>-<3>-<5>-\+ +out: \+---<1>-<2>-<3>-<6>-\+ +out: \+---<1>-<2>-<3>-<7>-\+- out: Prefix6 : -out: +---<1>-<2>-<3>-<1>-<2>-<3>-+ -out: +---<1>-<2>-<3>-<0>-+ -out: +---<1>-<2>-<3>-<5>-<6>-<7>-+- +out: \+---<1>-<2>-<3>-<1>-<2>-<3>-\+ +out: \+---<1>-<2>-<3>-<0>-\+ +out: \+---<1>-<2>-<3>-<5>-<6>-<7>-\+- out: Dist1 : -out: +---<11>-<1>-+ -out: +---<11>-<2>-+ -out: +---<11>-<3>-+- +out: \+---<11>-<1>-\+ +out: \+---<11>-<2>-\+ +out: \+---<11>-<3>-\+- out: Dist2 : -out: +---<11>-<0>-+ -out: +---<22>-<0>-+ -out: +---<33>-<0>-+- +out: \+---<11>-<0>-\+ +out: \+---<22>-<0>-\+ +out: \+---<33>-<0>-\+- out: Dist3 : -out: +---<11>-<1>-+ -out: +---<11>-<2>-+ -out: +---<11>-<3>-+ -out: +---<22>-<1>-+ -out: +---<22>-<2>-+ -out: +---<22>-<3>-+ -out: +---<33>-<1>-+ -out: +---<33>-<2>-+ -out: +---<33>-<3>-+- +out: \+---<11>-<1>-\+ +out: \+---<11>-<2>-\+ +out: \+---<11>-<3>-\+ +out: \+---<22>-<1>-\+ +out: \+---<22>-<2>-\+ +out: \+---<22>-<3>-\+ +out: \+---<33>-<1>-\+ +out: \+---<33>-<2>-\+ +out: \+---<33>-<3>-\+- out: Dist4 : -out: +---<11>-<1>-<2>-<3>-+ -out: +---<11>-<5>-<6>-<7>-+ -out: +---<22>-<1>-<2>-<3>-+ -out: +---<22>-<5>-<6>-<7>-+ -out: +---<33>-<1>-<2>-<3>-+ -out: +---<33>-<5>-<6>-<7>-+- +out: \+---<11>-<1>-<2>-<3>-\+ +out: \+---<11>-<5>-<6>-<7>-\+ +out: \+---<22>-<1>-<2>-<3>-\+ +out: \+---<22>-<5>-<6>-<7>-\+ +out: \+---<33>-<1>-<2>-<3>-\+ +out: \+---<33>-<5>-<6>-<7>-\+- out: Down :-<11>-<10>-<9>-<8>-<7>-<6>-<5>-<4>-<3>-<2>-<1>-<0>- out: Combi : -out: +---<1>-<2>-<3>-<·>-+ -out: +---<1>-<2>-<2>-<·>-+ -out: +---<1>-<2>-<1>-<·>-+ -out: +---<1>-<2>-<0>-<·>-+ -out: +---<1>-<1>-<3>-<·>-+ -out: +---<1>-<1>-<2>-<·>-+ -out: +---<1>-<1>-<1>-<·>-+ -out: +---<1>-<1>-<0>-<·>-+ -out: +---<1>-<0>-<3>-<·>-+ -out: +---<1>-<0>-<2>-<·>-+ -out: +---<1>-<0>-<1>-<·>-+ -out: +---<1>-<0>-<0>-<·>-+ -out: +---<0>-<2>-<3>-<·>-+ -out: +---<0>-<2>-<2>-<·>-+ -out: +---<0>-<2>-<1>-<·>-+ -out: +---<0>-<2>-<0>-<·>-+ -out: +---<0>-<1>-<3>-<·>-+ -out: +---<0>-<1>-<2>-<·>-+ -out: +---<0>-<1>-<1>-<·>-+ -out: +---<0>-<1>-<0>-<·>-+ -out: +---<0>-<0>-<3>-<·>-+ -out: +---<0>-<0>-<2>-<·>-+ -out: +---<0>-<0>-<1>-<·>-+ -out: +---<0>-<0>-<0>-<·>-+- +out: \+---<1>-<2>-<3>-<·>-\+ +out: \+---<1>-<2>-<2>-<·>-\+ +out: \+---<1>-<2>-<1>-<·>-\+ +out: \+---<1>-<2>-<0>-<·>-\+ +out: \+---<1>-<1>-<3>-<·>-\+ +out: \+---<1>-<1>-<2>-<·>-\+ +out: \+---<1>-<1>-<1>-<·>-\+ +out: \+---<1>-<1>-<0>-<·>-\+ +out: \+---<1>-<0>-<3>-<·>-\+ +out: \+---<1>-<0>-<2>-<·>-\+ +out: \+---<1>-<0>-<1>-<·>-\+ +out: \+---<1>-<0>-<0>-<·>-\+ +out: \+---<0>-<2>-<3>-<·>-\+ +out: \+---<0>-<2>-<2>-<·>-\+ +out: \+---<0>-<2>-<1>-<·>-\+ +out: \+---<0>-<2>-<0>-<·>-\+ +out: \+---<0>-<1>-<3>-<·>-\+ +out: \+---<0>-<1>-<2>-<·>-\+ +out: \+---<0>-<1>-<1>-<·>-\+ +out: \+---<0>-<1>-<0>-<·>-\+ +out: \+---<0>-<0>-<3>-<·>-\+ +out: \+---<0>-<0>-<2>-<·>-\+ +out: \+---<0>-<0>-<1>-<·>-\+ +out: \+---<0>-<0>-<0>-<·>-\+- out: OnOff : -out: +---<1>-<2>-<3>-<·>-+ -out: +---<1>-<2>-<·>-+ -out: +---<1>-<3>-<·>-+ -out: +---<1>-<·>-+ -out: +---<2>-<3>-<·>-+ -out: +---<2>-<·>-+ -out: +---<3>-<·>-+ -out: +---<·>-+- +out: \+---<1>-<2>-<3>-<·>-\+ +out: \+---<1>-<2>-<·>-\+ +out: \+---<1>-<3>-<·>-\+ +out: \+---<1>-<·>-\+ +out: \+---<2>-<3>-<·>-\+ +out: \+---<2>-<·>-\+ +out: \+---<3>-<·>-\+ +out: \+---<·>-\+- return: 0 END @@ -568,7 +567,6 @@ out: we-do-everything-for-YOU! out: Hello Mr.Future, nice to meet you... out: === Babbler masqueraded as Tool meets Leader and Visionary masqueraded as HomoSapiens === out: Hello Boss, nice to meet you... -out: Hello Boss, nice to meet you... out: === Babbler masqueraded as Tool meets Leader and Visionary masqueraded as Leader === out: Hello Boss, nice to meet you... return: 0 diff --git a/tests/41asset.tests b/tests/41asset.tests index b76982317..0af6737c5 100644 --- a/tests/41asset.tests +++ b/tests/41asset.tests @@ -4,7 +4,7 @@ TESTING "Component Test Suite: Asset Manager" ./test-components --group=asset TEST "AssetCategory_test" AssetCategory_test < + + 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 +//#include + +#include "common/config.h" + +#include "include/logging.h" +#include "lib/mutex.h" +#include "backend/threads.h" +#include "tests/test.h" + +#include +#include +#include + +void threadfn(void* blah) +{ + (void) blah; + struct timespec wait = {0,300000000}; + + fprintf (stderr, "thread running %s\n", NOBUG_THREAD_ID_GET); + nanosleep (&wait, NULL); + fprintf (stderr, "thread done %s\n", NOBUG_THREAD_ID_GET); +} + + +void threadsyncfn(void* blah) +{ + struct timespec wait = {0,200000000}; + LumieraReccondition sync = (LumieraReccondition) blah; + + ECHO ("thread starting up %s", NOBUG_THREAD_ID_GET); + LUMIERA_RECCONDITION_SECTION(cond_sync, sync) + { + ECHO ("send startup signal %s", NOBUG_THREAD_ID_GET); + LUMIERA_RECCONDITION_SIGNAL; + ECHO ("wait for trigger %s", NOBUG_THREAD_ID_GET); + LUMIERA_RECCONDITION_WAIT(1); + } + + ECHO ("thread running %s", NOBUG_THREAD_ID_GET); + nanosleep (&wait, NULL); + ECHO ("thread done %s", NOBUG_THREAD_ID_GET); +} + + + +lumiera_mutex testmutex; + +void mutexfn(void* blah) +{ + (void) blah; + struct timespec wait = {0,300000000}; + + LUMIERA_MUTEX_SECTION (NOBUG_ON, &testmutex) + { + fprintf (stderr, "mutex thread running %s\n", NOBUG_THREAD_ID_GET); + nanosleep (&wait, NULL); + fprintf (stderr, "thread done %s\n", NOBUG_THREAD_ID_GET); + } +} + + + +TESTS_BEGIN + +TEST ("simple_thread") +{ + fprintf (stderr, "main before thread %s\n", NOBUG_THREAD_ID_GET); + + lumiera_thread_run (LUMIERA_THREAD_WORKER, + threadfn, + NULL, + NULL, + argv[1], + NULL); + + struct timespec wait = {0,600000000}; + nanosleep (&wait, NULL); + fprintf (stderr, "main after thread %s\n", NOBUG_THREAD_ID_GET); +} + +TEST ("thread_synced") +{ + lumiera_reccondition cnd; + lumiera_reccondition_init (&cnd, "threadsync", &NOBUG_FLAG(NOBUG_ON)); + + LUMIERA_RECCONDITION_SECTION(cond_sync, &cnd) + { + ECHO ("main before thread %s", NOBUG_THREAD_ID_GET); + + lumiera_thread_run (LUMIERA_THREAD_WORKER, + threadsyncfn, + &cnd, + &cnd, + argv[1], + NULL); + + ECHO ("main wait for thread being ready %s", NOBUG_THREAD_ID_GET); + LUMIERA_RECCONDITION_WAIT(1); + + ECHO ("main trigger thread %s", NOBUG_THREAD_ID_GET); + LUMIERA_RECCONDITION_SIGNAL; + + ECHO ("wait for thread end %s", NOBUG_THREAD_ID_GET); + LUMIERA_RECCONDITION_WAIT(1); + ECHO ("thread ended %s", NOBUG_THREAD_ID_GET); + } + + lumiera_reccondition_destroy (&cnd, &NOBUG_FLAG(NOBUG_ON)); +} + + + +TEST ("mutex_thread") +{ + lumiera_mutex_init (&testmutex, "test", &NOBUG_FLAG(NOBUG_ON)); + + LUMIERA_MUTEX_SECTION (NOBUG_ON, &testmutex) + { + fprintf (stderr, "main before thread %s\n", NOBUG_THREAD_ID_GET); + + lumiera_thread_run (LUMIERA_THREAD_WORKER, + mutexfn, + NULL, + NULL, + argv[1], + NULL); + + struct timespec wait = {0,600000000}; + nanosleep (&wait, NULL); + fprintf (stderr, "main after thread %s\n", NOBUG_THREAD_ID_GET); + } + + lumiera_mutex_destroy (&testmutex, &NOBUG_FLAG(NOBUG_ON)); +} + + +TESTS_END diff --git a/tests/lib/thread-wrapper-join-test.cpp b/tests/lib/thread-wrapper-join-test.cpp index 9ced9c4b3..57793d2ac 100644 --- a/tests/lib/thread-wrapper-join-test.cpp +++ b/tests/lib/thread-wrapper-join-test.cpp @@ -109,6 +109,7 @@ namespace backend { #ifdef DEBUG /////////////////////////////////////////////////////////////////////////////////////////////TODO: better way of detecting debug builds +#if false /////////////////////////////////////////////////////////////////////////////////////////////TODO: re-enable assertions to throw, and make this configurable try { Thread("test Thread joining-3", @@ -120,6 +121,7 @@ namespace backend { { ASSERT (lumiera_error() == lumiera::error::LUMIERA_ERROR_ASSERTION); } +#endif #endif // note: the waitingHandle goes out of scope here, diff --git a/tests/library/test-locking.c b/tests/library/test-locking.c index aa2f6312c..14ec0c458 100644 --- a/tests/library/test-locking.c +++ b/tests/library/test-locking.c @@ -21,7 +21,9 @@ #include "tests/test.h" #include "lib/mutex.h" +#include "lib/recmutex.h" #include "lib/condition.h" +#include "lib/reccondition.h" #include "lib/rwlock.h" #include @@ -63,6 +65,22 @@ TEST ("mutexforgotunlock") } +TEST ("mutexexplicitunlock") +{ + lumiera_mutex m; + lumiera_mutex_init (&m, "mutexforgotunlock", &NOBUG_FLAG(NOBUG_ON)); + + LUMIERA_MUTEX_SECTION (NOBUG_ON, &m) + { + ECHO("mutex locked section"); + LUMIERA_MUTEX_SECTION_UNLOCK; + break; + } + + lumiera_mutex_destroy (&m, &NOBUG_FLAG(NOBUG_ON)); +} + + TEST ("nestedmutexsection") { lumiera_mutex m; @@ -107,25 +125,29 @@ TEST ("chainedmutexsection") lumiera_mutex_destroy (&m, &NOBUG_FLAG(NOBUG_ON)); } + + TEST ("recursivemutexsection") { - lumiera_mutex m; - lumiera_recmutex_init (&m, "m_mutexsection", &NOBUG_FLAG(NOBUG_ON)); + lumiera_recmutex m; + lumiera_recmutex_init (&m, "m_recmutexsection", &NOBUG_FLAG(NOBUG_ON)); - LUMIERA_MUTEX_SECTION (NOBUG_ON, &m) + LUMIERA_RECMUTEX_SECTION (NOBUG_ON, &m) { - printf ("mutex locked once\n"); + printf ("recmutex locked once\n"); - LUMIERA_MUTEX_SECTION (NOBUG_ON, &m) + LUMIERA_RECMUTEX_SECTION (NOBUG_ON, &m) { - printf ("mutex locked twice\n"); + printf ("recmutex locked twice\n"); } } - lumiera_mutex_destroy (&m, &NOBUG_FLAG(NOBUG_ON)); + lumiera_recmutex_destroy (&m, &NOBUG_FLAG(NOBUG_ON)); } + + TEST ("rwlocksection") { lumiera_rwlock rwlock; @@ -144,6 +166,7 @@ TEST ("rwlocksection") lumiera_rwlock_destroy (&rwlock, &NOBUG_FLAG(NOBUG_ON)); } + TEST ("rwlockforgotunlock") { lumiera_rwlock rwlock; @@ -158,6 +181,60 @@ TEST ("rwlockforgotunlock") } + +TEST ("rwdeadlockwr") +{ + lumiera_rwlock rwlock; + lumiera_rwlock_init (&rwlock, "rwsection", &NOBUG_FLAG(NOBUG_ON)); + + LUMIERA_WRLOCK_SECTION (NOBUG_ON, &rwlock) + { + printf ("write locked section 1\n"); + LUMIERA_RDLOCK_SECTION (NOBUG_ON, &rwlock) + { + printf ("read locked section 2\n"); + } + } + + lumiera_rwlock_destroy (&rwlock, &NOBUG_FLAG(NOBUG_ON)); +} + + + +TEST ("rwdeadlockrw") +{ + lumiera_rwlock rwlock; + lumiera_rwlock_init (&rwlock, "rwsection", &NOBUG_FLAG(NOBUG_ON)); + + LUMIERA_RDLOCK_SECTION (NOBUG_ON, &rwlock) + { + printf ("read locked section 1\n"); + LUMIERA_WRLOCK_SECTION (NOBUG_ON, &rwlock) + { + printf ("write locked section 2\n"); + } + } + + lumiera_rwlock_destroy (&rwlock, &NOBUG_FLAG(NOBUG_ON)); +} + + +TEST ("conditionops (compiletest only)") +{ + lumiera_condition cond; + lumiera_condition_init (&cond, "conditionsection", &NOBUG_FLAG(NOBUG_ON)); + + LUMIERA_CONDITION_SECTION (NOBUG_ON, &cond) + { + LUMIERA_CONDITION_WAIT(1); + LUMIERA_CONDITION_SIGNAL; + LUMIERA_CONDITION_BROADCAST; + } + + lumiera_condition_destroy (&cond, &NOBUG_FLAG(NOBUG_ON)); +} + + TEST ("conditionsection") { lumiera_condition cond; @@ -177,6 +254,7 @@ TEST ("conditionsection") } + TEST ("conditionforgotunlock") { lumiera_condition cond; @@ -190,4 +268,55 @@ TEST ("conditionforgotunlock") lumiera_condition_destroy (&cond, &NOBUG_FLAG(NOBUG_ON)); } + + +TEST ("recconditionops (compiletest only)") +{ + lumiera_reccondition reccond; + lumiera_reccondition_init (&reccond, "recconditionsection", &NOBUG_FLAG(NOBUG_ON)); + + LUMIERA_RECCONDITION_SECTION (NOBUG_ON, &reccond) + { + LUMIERA_RECCONDITION_WAIT(1); + LUMIERA_RECCONDITION_SIGNAL; + LUMIERA_RECCONDITION_BROADCAST; + } + + lumiera_reccondition_destroy (&reccond, &NOBUG_FLAG(NOBUG_ON)); +} + + +TEST ("recconditionsection") +{ + lumiera_reccondition reccond; + lumiera_reccondition_init (&reccond, "recconditionsection", &NOBUG_FLAG(NOBUG_ON)); + + LUMIERA_RECCONDITION_SECTION (NOBUG_ON, &reccond) + { + printf ("reccondition locked section 1\n"); + } + + LUMIERA_RECCONDITION_SECTION (NOBUG_ON, &reccond) + { + printf ("reccondition locked section 2\n"); + } + + lumiera_reccondition_destroy (&reccond, &NOBUG_FLAG(NOBUG_ON)); +} + + +TEST ("recconditionforgotunlock") +{ + lumiera_reccondition reccond; + lumiera_reccondition_init (&reccond, "recconditionforgotunlock", &NOBUG_FLAG(NOBUG_ON)); + + LUMIERA_RECCONDITION_SECTION (NOBUG_ON, &reccond) + { + break; // RECCONDITION_SECTIONS must not be left by a jump + } + + lumiera_reccondition_destroy (&reccond, &NOBUG_FLAG(NOBUG_ON)); +} + + TESTS_END diff --git a/tests/library/test-mpool.c b/tests/library/test-mpool.c new file mode 100644 index 000000000..8e3bcd8a4 --- /dev/null +++ b/tests/library/test-mpool.c @@ -0,0 +1,343 @@ +/* + test-mpool.c - memory pool for constant sized objects + + Copyright (C) Lumiera.org + 2009, 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 "tests/test.h" +#include "lib/mpool.h" + +struct teststruct +{ + llist node; + void* ptr[2]; +}; + + +static inline uint32_t mpool_fast_prng () +{ + static uint32_t rnd=0xbabeface; + return rnd = rnd<<1 ^ ((rnd >> 30) & 1) ^ ((rnd>>2) & 1); +} + +static void +dtor (void* o) +{ + ECHO("%d @%p", *(int*)o, o); +} + + + +TESTS_BEGIN + +TEST ("basic") +{ + mpool mypool; + mpool_init (&mypool, sizeof(void*), 10, dtor); + ECHO ("initialized"); + + void* element; + element = mpool_alloc (&mypool); + ECHO ("allocated %p", element); + *(int*)element = 0xdeadbabe; + + DUMP(NOBUG_ON, mpool, &mypool, 4); + + mpool_free (&mypool, element); + ECHO ("freed"); + + DUMP(NOBUG_ON, mpool, &mypool, 4); + + mpool_destroy (&mypool); + ECHO ("destroyed"); +} + + +TEST ("destroy") +{ + mpool mypool; + mpool_init (&mypool, sizeof(void*), 10, dtor); + ECHO ("initialized"); + + void* element; + element = mpool_alloc (&mypool); + ECHO ("allocated %p", element); + *(int*)element = 0xbabeface; + + DUMP(NOBUG_ON, mpool, &mypool, 4); + + mpool_destroy (&mypool); + ECHO ("destroyed"); +} + + +TEST ("clusters") +{ + mpool mypool; + mpool_init (&mypool, sizeof(void*), 2, dtor); + ECHO ("initialized"); + + for (int i = 1; i <= 5; ++i) + { + void* element; + element = mpool_alloc (&mypool); + ECHO ("allocated %p", element); + *(int*)element = i; + } + + DUMP(NOBUG_ON, mpool, &mypool, 4); + + mpool_destroy (&mypool); + ECHO ("destroyed"); +} + + +TEST ("clusters_big") +{ + mpool mypool; + mpool_init (&mypool, sizeof(void*), 200, dtor); + ECHO ("initialized"); + + for (int i = 1; i <= 700; ++i) + { + void* element; + element = mpool_alloc (&mypool); + ECHO ("allocated %p", element); + *(int*)element = i; + } + + DUMP(NOBUG_ON, mpool, &mypool, 4); + + mpool_destroy (&mypool); + ECHO ("destroyed"); +} + + +TEST ("alloc_free") +{ + mpool mypool; + mpool_init (&mypool, 24, 4, dtor); + ECHO ("initialized"); + + void* elem[32]; + + for (int i = 1; i <= 15; ++i) + { + elem[i] = mpool_alloc (&mypool); + *(int*)(elem[i]) = i; + } + ECHO ("allocated"); + + for (int i = 1; i <= 15; i+=3) + { + mpool_free (&mypool, elem[i]); + } + ECHO ("freed some"); + DUMP(NOBUG_ON, mpool, &mypool, 4); + + mpool_destroy (&mypool); + ECHO ("destroyed"); +} + + +TEST ("alloc_free_big") +{ + mpool mypool; + mpool_init (&mypool, 24, 4, dtor); + ECHO ("initialized"); + + void* elem[2000]; + + for (int i = 1; i <= 2000; ++i) + { + elem[i] = mpool_alloc (&mypool); + *(int*)(elem[i]) = i; + } + ECHO ("allocated"); + + for (int i = 1; i <= 2000; i+=3) + { + mpool_free (&mypool, elem[i]); + } + ECHO ("freed some"); + DUMP(NOBUG_ON, mpool, &mypool, 4); + + DUMP(NOBUG_ON, mpool, &mypool, 4); + + mpool_destroy (&mypool); + ECHO ("destroyed"); +} + + + +TEST ("reserve") +{ +} + + +/* + benchmark mpool vs malloc, first only the allocation/free itself with some necessary llist ops +*/ +TEST ("bench_mpool") +{ + mpool mypool; + mpool_init (&mypool, sizeof(struct teststruct), 2000, NULL); + ECHO ("initialized"); + + llist list; + llist_init (&list); + + for (int j = 1; j<=100; ++j) + { + for (int i = 1; i <= 50000; ++i) + { + struct teststruct* element = mpool_alloc (&mypool); + llist_insert_tail (&list, llist_init (&element->node)); + } + + LLIST_WHILE_HEAD (&list, element) + { + llist_unlink_fast_ (element); + mpool_free (&mypool, element); + } + } + + mpool_destroy (&mypool); + ECHO ("destroyed"); +} + + +TEST ("bench_malloc") +{ + mpool mypool; + mpool_init (&mypool, sizeof(llist), 2000, NULL); + ECHO ("initialized"); + + llist list; + llist_init (&list); + + for (int j = 100; j; --j) + { + for (int i = 1; i <= 50000; ++i) + { + struct teststruct* element = malloc (sizeof(*element)); + llist_insert_tail (&list, llist_init (&element->node)); + } + + LLIST_WHILE_HEAD (&list, element) + { + llist_unlink_fast_ (element); + free (element); + } + } + + mpool_destroy (&mypool); + ECHO ("destroyed"); +} + + +/* + benchmark mpool vs malloc, try to simulate some slightly more realistic application usage + - allocate list nodes which have 2 data members as payload + - there is a 25% chance at each alloc that the head of the list gets deleted +*/ +TEST ("bench_mpool_sim") +{ + mpool mypool; + mpool_init (&mypool, sizeof(struct teststruct), 2000, NULL); + ECHO ("initialized"); + + llist list; + llist_init (&list); + + for (int j = 1; j<=100; ++j) + { + for (int i = 1; i <= 50000; ++i) + { + struct teststruct* element = mpool_alloc (&mypool); + llist_insert_tail (&list, llist_init (&element->node)); + element->ptr[0] = malloc(100+(mpool_fast_prng()%500)); + element->ptr[1] = malloc(100+(mpool_fast_prng()%500)); + + if (!(mpool_fast_prng()%4)) + { + struct teststruct* element = (struct teststruct*)llist_head (&list); + llist_unlink_fast_ (&element->node); + free(element->ptr[0]); + free(element->ptr[1]); + mpool_free (&mypool, element); + } + } + + LLIST_WHILE_HEAD (&list, element) + { + llist_unlink_fast_ (element); + free(((struct teststruct*)element)->ptr[0]); + free(((struct teststruct*)element)->ptr[1]); + mpool_free (&mypool, element); + } + } + + mpool_destroy (&mypool); + ECHO ("destroyed"); +} + + +TEST ("bench_malloc_sim") +{ + mpool mypool; + mpool_init (&mypool, sizeof(llist), 2000, NULL); + ECHO ("initialized"); + + llist list; + llist_init (&list); + + for (int j = 100; j; --j) + { + for (int i = 1; i <= 50000; ++i) + { + struct teststruct* element = malloc (sizeof(*element)); + llist_insert_tail (&list, llist_init (&element->node)); + element->ptr[0] = malloc(100+(mpool_fast_prng()%500)); + element->ptr[1] = malloc(100+(mpool_fast_prng()%500)); + + if (!(mpool_fast_prng()%4)) + { + struct teststruct* element = (struct teststruct*)llist_head (&list); + llist_unlink_fast_ (&element->node); + free(element->ptr[0]); + free(element->ptr[1]); + free (element); + } + } + + LLIST_WHILE_HEAD (&list, element) + { + llist_unlink_fast_ (element); + free(((struct teststruct*)element)->ptr[0]); + free(((struct teststruct*)element)->ptr[1]); + free (element); + } + } + + mpool_destroy (&mypool); + ECHO ("destroyed"); +} + + +TESTS_END diff --git a/tests/library/test-slist.c b/tests/library/test-slist.c new file mode 100644 index 000000000..c4c9bfbaa --- /dev/null +++ b/tests/library/test-slist.c @@ -0,0 +1,349 @@ +/* + * test-slist.c - test the linked list lib + * + * Copyright (C) Lumiera.org + * 2009 Anton Yakovlev + * + * 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/slist.h" +#include "tests/test.h" + +#include + +typedef struct item { + int key; + slist list; +} item_t; + +int cmp( const_SList a, const_SList b ) { + + item_t* x = SLIST_TO_STRUCTP( a, item_t, list ); + item_t* y = SLIST_TO_STRUCTP( b, item_t, list ); + + if ( x -> key < y -> key ) { + return -1; + } + + if ( x -> key > y -> key ) { + return +1; + } + + return 0; + +} + +TESTS_BEGIN + +/* + * 1. Basic: + * SLIST_AUTO( name ) + * void slist_init( SList list ) + * int slist_is_empty( const_SList list ) + * int slist_is_single( const_SList list ) + * int slist_is_head( const_SList list, const_SList head ) + * int slist_is_end( const_SList list, const_SList end ) + * int slist_is_member( const_SList list, const_SList member ) + * int slist_is_before_after( const_SList list, const_SList before, const_SList after ) + */ + +TEST( "basic" ) { + + SLIST_AUTO( listX ); + slist listY; + SLIST_AUTO( nodeA ); + SLIST_AUTO( nodeB ); + + printf( "%d\n", slist_is_end( &listX, &listX ) ); + + slist_init( &listY ); + + printf( "%d\n", slist_is_empty( &listY ) ); + + slist_insert( &listX, &nodeA ); + printf( "%d\n", slist_is_empty( &listX ) ); + printf( "%d\n", slist_is_single( &listX ) ); + printf( "%d\n", slist_is_head( &listX, &nodeA ) ); + printf( "%d\n", slist_is_end( &listX, &nodeA ) ); + printf( "%d\n", slist_is_member( &listX, &nodeA ) ); + printf( "%d\n", slist_is_member( &listX, &nodeB ) ); + + slist_insert( &nodeA, &nodeB ); + printf( "%d\n", slist_is_empty( &listX ) ); + printf( "%d\n", slist_is_single( &listX ) ); + printf( "%d\n", slist_is_head( &listX, &nodeB ) ); + printf( "%d\n", slist_is_end( &listX, &nodeB ) ); + printf( "%d\n", slist_is_member( &listX, &nodeB ) ); + + printf( "%d\n", slist_is_before_after( &listX, &nodeA, &nodeB ) ); + printf( "%d\n", slist_is_before_after( &listX, &nodeB, &nodeA ) ); + +} + +/* + * 2. Insert/delete: + * slist_insert_head( list, element ) + * SList slist_insert( SList head, SList node ) + * SList slist_insert_list( SList xnode, SList ylist ) + * SList slist_insert_range( SList node, SList start, SList end ) + * SList slist_unlink( SList list, SList node ) + */ + +TEST( "insert_delete" ) { + + SLIST_AUTO( listX ); + SLIST_AUTO( nodeA ); + SLIST_AUTO( nodeB ); + SLIST_AUTO( nodeC ); + + slist_insert_head( &listX, &nodeA ); + slist_insert( &nodeA, &nodeB ); + slist_insert( &nodeB, &nodeC ); + printf( "%d\n", slist_next( &listX ) == &nodeA ); + printf( "%d\n", slist_next( &nodeA ) == &nodeB ); + printf( "%d\n", slist_next( &nodeB ) == &nodeC ); + printf( "%d\n", slist_next( &nodeC ) == &listX ); + + slist_unlink( &listX, &nodeA ); + printf( "%d\n", slist_next( &listX ) == &nodeB ); + + slist_insert( &listX, &nodeA ); + printf( "%d\n", slist_next( &listX ) == &nodeA ); + + SLIST_AUTO( listY ); + + slist_insert_list( &listY, &listX ); + printf( "%d\n", slist_is_empty( &listX ) ); + printf( "%d\n", slist_next( &listY ) == &nodeA ); + printf( "%d\n", slist_next( &nodeA ) == &nodeB ); + printf( "%d\n", slist_next( &nodeB ) == &nodeC ); + printf( "%d\n", slist_next( &nodeC ) == &listY ); + + slist_insert_range( &listX, &nodeA, &nodeB ); + printf( "%d\n", slist_next( &listX ) == &nodeA ); + printf( "%d\n", slist_next( &nodeA ) == &nodeB ); + printf( "%d\n", slist_next( &nodeB ) == &listX ); + + printf( "%d\n", slist_is_single( &listY ) ); + printf( "%d\n", slist_next( &listY ) == &nodeC ); + printf( "%d\n", slist_next( &nodeC ) == &listY ); + +} + +/* + * 3. Movements: + * slist_head() + * SList slist_next( const_SList node ) + * SList slist_prev( SList list, SList node ) + * SList slist_advance( SList list, SList node ) + * void slist_forward( SList_ref node ) + */ + +TEST( "movement" ) { + + SLIST_AUTO( listX ); + SLIST_AUTO( nodeA ); + SLIST_AUTO( nodeB ); + SLIST_AUTO( nodeC ); + + slist_insert_head( &listX, &nodeA ); + slist_insert( &nodeA, &nodeB ); + slist_insert( &nodeB, &nodeC ); + + printf( "%d\n", slist_next( &listX ) == &nodeA ); + printf( "%d\n", slist_next( &nodeA ) == &nodeB ); + printf( "%d\n", slist_next( &nodeB ) == &nodeC ); + printf( "%d\n", slist_next( &nodeC ) == &listX ); + + printf( "%d\n", slist_prev( &listX, &listX ) == &nodeC ); + printf( "%d\n", slist_prev( &listX, &nodeC ) == &nodeB ); + printf( "%d\n", slist_prev( &listX, &nodeB ) == &nodeA ); + printf( "%d\n", slist_prev( &listX, &nodeA ) == &listX ); + + slist_advance( &listX, &nodeA ); + printf( "%d\n", slist_next( &listX ) == &nodeB ); + printf( "%d\n", slist_next( &nodeB ) == &nodeA ); + printf( "%d\n", slist_next( &nodeA ) == &nodeC ); + printf( "%d\n", slist_next( &nodeC ) == &listX ); + + SList node = &listX; + slist_forward( &node ); + printf( "%d\n", node == &nodeB ); + +} + +/* + * 4. Enumerations: + * SLIST_TO_STRUCTP( list, type, member ) + * SLIST_FOREACH( list, node ) + * SLIST_FORRANGE( start, end, node ) + * SLIST_WHILE_HEAD( list, head ) + */ + +TEST( "enumerations" ) { + + SLIST_AUTO( list ); + + item_t nodeA = { 'A', { NULL } }; + item_t nodeB = { 'B', { NULL } }; + item_t nodeC = { 'C', { NULL } }; + item_t nodeD = { 'D', { NULL } }; + + slist_init( &nodeA.list ); + slist_init( &nodeB.list ); + slist_init( &nodeC.list ); + slist_init( &nodeD.list ); + + slist_insert( &list, &nodeA.list ); + slist_insert( &nodeA.list, &nodeB.list ); + slist_insert( &nodeB.list, &nodeC.list ); + slist_insert( &nodeC.list, &nodeD.list ); + + SLIST_FOREACH ( &list, node ) { + item_t* item = ( item_t* ) SLIST_TO_STRUCTP( node, item_t, list ); + printf( "%c ", item -> key ); + } + printf( ".\n" ); + + printf( "---\n" ); + + SLIST_FORRANGE ( &nodeB.list, &nodeD.list, node ) { + item_t* item = ( item_t* ) SLIST_TO_STRUCTP( node, item_t, list ); + printf( "%c ", item -> key ); + } + printf( ".\n" ); + + printf( "---\n" ); + + SLIST_WHILE_HEAD ( &list, head ) { + item_t* item = ( item_t* ) SLIST_TO_STRUCTP( head, item_t, list ); + printf( "%c ", item -> key ); + slist_unlink( &list, head ); + } + printf( ".\n" ); + + printf( "%d\n", slist_is_empty( &list ) ); + +} + +/* + * 5. Counting: + * unsigned slist_count( const_SList list ) + * SList slist_get_nth( SList list, int n ) + * SList slist_get_nth_stop( SList list, int n, const_SList stop ) + */ + +TEST( "count" ) { + + SLIST_AUTO( list ); + SLIST_AUTO( nodeA ); + SLIST_AUTO( nodeB ); + SLIST_AUTO( nodeC ); + + slist_insert( &list, &nodeA ); + slist_insert( &nodeA, &nodeB ); + slist_insert( &nodeB, &nodeC ); + + printf( "%u\n", slist_count( &list ) ); + printf( "%d\n", slist_get_nth( &list, 3 ) == &nodeC ); + printf( "%d\n", slist_get_nth_stop( &list, 3, &nodeC ) == NULL ); + +} + +/* + * 6. Sort: + * SList slist_sort( SList list, slist_cmpfn cmp ) + */ + +TEST( "sort" ) { + + srand( time( NULL ) ); + + SLIST_AUTO( list ); + + unsigned int n = 1000000; + + item_t* items; + if ( ( items = ( item_t* ) malloc( sizeof( item_t ) * n ) ) == NULL ) { + return 1; // ERROR: not enough memory + } + + for ( unsigned int i = 0; i < n; i++ ) { + items[ i ].key = rand(); + slist_init( &items[ i ].list ); + slist_insert( &list, &items[ i ].list ); + } + + slist_sort( &list, cmp ); + + int is_first_cmp = 1; + int prev_key = 0; + + SLIST_FOREACH ( &list, x ) { + item_t* item = SLIST_TO_STRUCTP( x, item_t, list ); + if ( is_first_cmp ) { + is_first_cmp = 0; + } else if ( prev_key > item -> key ) { + return 2; // ERROR: wrong order of elements + } + prev_key = item -> key; + } + + free( items ); + + return 0; + +} + +/* + * 7. Search: + * SList slist_find( const_SList list, const_SList pattern, slist_cmpfn cmp ) + * SList slist_ufind( SList list, const_SList pattern, slist_cmpfn cmp ) + * SList slist_sfind( const_SList list, const_SList pattern, slist_cmpfn cmp ) + */ + +TEST( "search" ) { + + SLIST_AUTO( list ); + + item_t nodeA = { 'A', { NULL } }; + item_t nodeB = { 'B', { NULL } }; + item_t nodeC = { 'C', { NULL } }; + item_t nodeD = { 'D', { NULL } }; + item_t nodeX = { '?', { NULL } }; + + slist_init( &nodeA.list ); + slist_init( &nodeB.list ); + slist_init( &nodeC.list ); + slist_init( &nodeD.list ); + + slist_insert( &list, &nodeA.list ); + slist_insert( &nodeA.list, &nodeB.list ); + slist_insert( &nodeB.list, &nodeC.list ); + slist_insert( &nodeC.list, &nodeD.list ); + + nodeX.key = 'C'; + + printf( "%d\n", slist_find( &list, &nodeX.list, cmp ) == &nodeC.list ); + printf( "%d\n", slist_ufind( &list, &nodeX.list, cmp ) == &nodeC.list ); + printf( "%d\n", slist_next( &nodeC.list ) == &nodeA.list ); + + nodeX.key = 'A'; + printf( "%d\n", slist_sfind( &list, &nodeX.list, cmp ) == NULL ); + +} + +TESTS_END diff --git a/tests/test.sh b/tests/test.sh index 31c92da5b..ce4ef4dee 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/bash # Copyright (C) Lumiera.org # 2007 - 2008, Christian Thaeter # @@ -26,18 +26,34 @@ # stop testing on the first failure export LC_ALL=C +NOBUG_LOGREGEX='^\(\*\*[0-9]*\*\* \)\?[0-9]\{10,\}: \(TRACE\|INFO\|NOTICE\|WARNING\|ERR\|TODO\|PLANNED\|FIXME\|DEPRECATED\|UNIMPLEMENTED\|NOTREACHED\):' arg0="$0" srcdir="$(dirname "$arg0")" -ulimit -S -t 2 -v 524288 +ulimit -S -t 5 -v 524288 valgrind="" if [ "$VALGRINDFLAGS" = 'DISABLE' ]; then echo "valgrind explicit disabled" else if [ "$(which valgrind)" ]; then - valgrind="$(which valgrind) --leak-check=yes --show-reachable=yes -q $VALGRINDFLAGS" - ulimit -S -t 10 + ulimit -S -t 20 + if [[ -x 'vgsuppression' ]]; then + if [[ 'vgsuppression' -nt 'vgsuppression.supp' ]]; then + echo 'generating valgrind supression file' + + if [[ -x ".libs/vgsuppression" ]]; then + ./libtool --mode=execute valgrind --leak-check=yes --show-reachable=yes -q --gen-suppressions=all vgsuppression 2>&1 \ + | sed '/^\(==\)\|\(\*\*\)[0-9]*\(==\)\|\(\*\*\)/d;' >vgsuppression.supp + else + valgrind --leak-check=yes --show-reachable=yes -q --gen-suppressions=all ./vgsuppression 2>&1 \ + | sed '/^\(==\)\|\(\*\*\)[0-9]*\(==\)\|\(\*\*\)/d;' >vgsuppression.supp + fi + fi + valgrind="$(which valgrind) --leak-check=yes --show-reachable=no --suppressions=vgsuppression.supp -q $VALGRINDFLAGS" + else + valgrind="$(which valgrind) --leak-check=yes --show-reachable=no -q $VALGRINDFLAGS" + fi else echo "no valgrind found, go without it" fi @@ -50,6 +66,7 @@ TESTCNT=0 SKIPCNT=0 FAILCNT=0 + # the old testlog if existing will be used to check for previous test states if test -f ,testlog; then mv ,testlog ,testlog.pre @@ -59,6 +76,40 @@ fi date >,testlog +function compare_regex() # rxfile plainfile +{ + local regex + local line + local miss + local lineno=1 + local regexno=1 + { + IFS='' read -u 3 -r regex || return 0 + IFS='' read -u 4 -r line || { echo "no output"; return 1; } + while true; do + if [[ $line =~ $regex ]]; then + IFS='' read -u 4 -r line || + if IFS='' read -u 3 -r regex; then + echo "premature end in output, expecting: '$regex':$regexno" + return 1 + else + return 0 + fi + : $((++lineno)) + miss=0 + else + if [[ $((++miss)) -gt 1 ]]; then + echo -e "'$line':$lineno\ndoes not match\n'$regex':$regexno" + return 1 + fi + IFS='' read -u 3 -r regex || { echo "more output than expected: '$line':$lineno"; return 1; } + : $((++regexno)) + fi + done + } 3<"$1" 4<"$2" +} + + function TEST() { name="$1" @@ -69,7 +120,11 @@ function TEST() while read -r line; do cmd="${line%%:*}" - arg="${line#*: }" + arg="${line#*:}" + arg="${arg:1}" + if [[ ! "$arg" ]]; then + arg='^$' + fi expect_return=0 case $cmd in @@ -85,6 +140,9 @@ function TEST() 'return') expect_return="$arg" ;; + '#'*|'') + : + ;; *) echo "UNKOWN TEST COMMAND '$cmd'" 1>&2 exit @@ -128,36 +186,45 @@ function TEST() ((fails+=1)) else + if test -f ,send_stdin; then - cat ,send_stdin | $valgrind $TESTBIN "$@" 2>,stderr >,stdout + env $TESTBIN_PREFIX $TESTBIN "$@" <,send_stdin 2>,stderr >,stdout else - $valgrind $TESTBIN "$@" 2>,stderr >,stdout + env $TESTBIN_PREFIX $TESTBIN "$@" 2>,stderr >,stdout fi &>/dev/null return=$? if test -f ,expect_stdout; then - if ! cmp ,expect_stdout ,stdout &>/dev/null; then + grep -v "$NOBUG_LOGREGEX" <,stdout >,tmp + if ! compare_regex ,expect_stdout ,tmp >>,cmptmp; then echo "unexpected data on stdout" >>,testtmp - grep -v ': \(TRACE\|INFO\|NOTICE\|WARNING\|ERR\):' <,stdout >,tmp - diff -ua ,expect_stdout ,tmp >>,testtmp - rm ,tmp + cat ,cmptmp >>,testtmp ((fails+=1)) fi + rm ,tmp ,cmptmp fi if test -f ,expect_stderr; then - if ! cmp ,expect_stderr ,stderr &>/dev/null; then + grep -v "$NOBUG_LOGREGEX" <,stderr >,tmp + cat ,tmp >>,testtmp + if ! compare_regex ,expect_stderr ,tmp >>,cmptmp; then echo "unexpected data on stderr" >>,testtmp - grep -v ': \(TRACE\|INFO\|NOTICE\|WARNING\|ERR\):' <,stderr >,tmp - diff -ua ,expect_stderr ,tmp >>,testtmp - rm ,tmp + cat ,cmptmp >>,testtmp ((fails+=1)) fi + rm ,tmp ,cmptmp fi - if test "$expect_return" != "$return"; then - echo "unexpected return value $return" >>,testtmp - ((fails+=1)) + if [[ "${expect_return:0:1}" = '!' ]]; then + if [[ "${expect_return#\!}" = "$return" ]]; then + echo "unexpected return value $return, expected $expect_return" >>,testtmp + ((fails+=1)) + fi + else + if [[ "${expect_return}" != "$return" ]]; then + echo "unexpected return value $return, expected $expect_return" >>,testtmp + ((fails+=1)) + fi fi fi @@ -224,7 +291,14 @@ function TESTING() { echo echo "$1" - TESTBIN=$2 + echo -e "\n#### $1" >>,testlog + + if [[ -x ".libs/$2" ]]; then + TESTBIN_PREFIX="./libtool --mode=execute $valgrind" + else + TESTBIN_PREFIX="$valgrind" + fi + TESTBIN="$2" } TESTSUITES="${TESTSUITES}${1:+${TESTSUITES:+,}$1}"