Merge backend and testsuite improvements

This commit is contained in:
Fischlurch 2009-06-19 01:19:20 +02:00
commit fba135c746
46 changed files with 3682 additions and 819 deletions

View file

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

View file

@ -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<NonrecursiveLock_Waitable>
, Sync<NonrecursiveLock_Waitable>::Lock
: public Sync<RecursiveLock_Waitable>
, Sync<RecursiveLock_Waitable>::Lock
{
typedef Sync<NonrecursiveLock_Waitable> SyncBase;
typedef Sync<RecursiveLock_Waitable> 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 =

View file

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

View file

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

View file

@ -21,7 +21,7 @@
#include "include/logging.h"
#include "lib/mutex.h"
#include "lib/recmutex.h"
#include "lib/safeclib.h"
#include "common/plugin.h"

View file

@ -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");

View file

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

View file

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

View file

@ -56,6 +56,7 @@ WorkspaceWindow::WorkspaceWindow(Project &source_project,
WorkspaceWindow::~WorkspaceWindow()
{
INFO (gui_dbg, "closing workspace window...");
}
Project&

View file

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

View file

@ -1,5 +1,5 @@
# Copyright (C) Lumiera.org
# 2007, Christian Thaeter <ct@pipapo.org>
# 2007, 2008, 2009 Christian Thaeter <ct@pipapo.org>
#
# 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 \

View file

@ -2,7 +2,7 @@
condition.c - condition variable
Copyright (C) Lumiera.org
2008, Christian Thaeter <ct@pipapo.org>
2008, 2009 Christian Thaeter <ct@pipapo.org>
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);
}

View file

@ -23,58 +23,73 @@
#define LUMIERA_CONDITION_H
#include "lib/error.h"
#include "lib/mutex.h"
#include "lib/sectionlock.h"
#include <pthread.h>
#include <nobug.h>
/**
* @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

View file

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

518
src/lib/mpool.c Normal file
View file

@ -0,0 +1,518 @@
/*
mpool.c - memory pool for constant sized objects
Copyright (C) Lumiera.org
2009, Christian Thaeter <ct@pipapo.org>
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 <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <limits.h>
#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))<<MPOOL_DIV_SHIFT);
uintptr_t* bitmap = (uintptr_t*)&cluster->data;
return bitmap[quot] & ((uintptr_t)1<<rem);
}
MPool
mpool_destroy (MPool self)
{
TRACE (mpool_dbg, "%p", self);
if (self)
{
LLIST_WHILE_TAIL(&self->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)<<n;
while (1)
{
if (v&mask)
{
if (v&mask& ~(~0ULL<<n))
return n-r;
else
return n+r;
}
if (mask == ~MPOOL_C(0))
return ~0U;
++r;
mask |= ((mask<<1)|(mask>>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))<<MPOOL_DIV_SHIFT);
uintptr_t* bitmap = (uintptr_t*)&cluster->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))<<MPOOL_DIV_SHIFT);
uintptr_t* bitmap = (uintptr_t*)&cluster->data;
bitmap[quot] |= ((uintptr_t)1<<rem);
TRACE (mpool_dbg, "set bit %d, index %d, of %p is %p", rem, quot, element, bitmap[quot]);
}
static inline void
bitmap_clear_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))<<MPOOL_DIV_SHIFT);
uintptr_t* bitmap = (uintptr_t*)&cluster->data;
bitmap[quot] &= ~((uintptr_t)1<<rem);
TRACE (mpool_dbg, "cleared bit %d, index %d, of %p is %p", rem, quot, element, bitmap[quot]);
}
void*
mpool_alloc (MPool self)
{
TRACE (mpool_dbg);
if (!self->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))<<MPOOL_DIV_SHIFT);
uintptr_t* bitmap = (uintptr_t*)&cluster->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:
*/

247
src/lib/mpool.h Normal file
View file

@ -0,0 +1,247 @@
/*
mpool.h - memory pool for constant sized objects
Copyright (C) Lumiera.org
2009, Christian Thaeter <ct@pipapo.org>
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 <stdint.h>
#include <nobug.h>
#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:
*/

View file

@ -2,7 +2,7 @@
mutex.c - mutex
Copyright (C) Lumiera.org
2008, Christian Thaeter <ct@pipapo.org>
2008, 2009, Christian Thaeter <ct@pipapo.org>
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

View file

@ -2,7 +2,7 @@
mutex.h - mutal exclusion locking
Copyright (C) Lumiera.org
2008, Christian Thaeter <ct@pipapo.org>
2008, 2009, Christian Thaeter <ct@pipapo.org>
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 <pthread.h>
#include <nobug.h>
@ -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
/*

View file

@ -2,7 +2,7 @@
reccondition.c - condition variable, w/ recursive mutex
Copyright (C) Lumiera.org
2008, Christian Thaeter <ct@pipapo.org>
2008, 2009, Christian Thaeter <ct@pipapo.org>
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);
}

View file

@ -1,8 +1,8 @@
/*
reccondition.h - condition variables, w/ recursive mutex
reccondition.h - recursive locked condition variables
Copyright (C) Lumiera.org
2008, Christian Thaeter <ct@pipapo.org>
2008, 2009, Christian Thaeter <ct@pipapo.org>
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 <pthread.h>
#include <nobug.h>
/**
* @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
/*

74
src/lib/recmutex.c Normal file
View file

@ -0,0 +1,74 @@
/*
recmutex.c - recursive mutex
Copyright (C) Lumiera.org
2008, 2009, Christian Thaeter <ct@pipapo.org>
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:
*/

132
src/lib/recmutex.h Normal file
View file

@ -0,0 +1,132 @@
/*
recmutex.h - recursive mutal exclusion locking
Copyright (C) Lumiera.org
2008, 2009, Christian Thaeter <ct@pipapo.org>
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 <pthread.h>
#include <nobug.h>
/**
* @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:
*/

View file

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

View file

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

82
src/lib/sectionlock.h Normal file
View file

@ -0,0 +1,82 @@
/*
sectionlock.h - mutex state handle
Copyright (C) Lumiera.org
2008, Christian Thaeter <ct@pipapo.org>
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 <pthread.h>
#include <nobug.h>
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:
*/

666
src/lib/slist.h Normal file
View file

@ -0,0 +1,666 @@
/*
* slist.h - simple intrusive cyclic single linked list
*
* Copyright (C) Lumiera.org
* 2009 Anton Yakovlev <just.yakovlev@gmail.com>
*
* 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 <stddef.h>
/**
* @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 */

View file

@ -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<CDX>
{
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<LumieraCondition> (this);}
LumieraReccondition accessCond(){return static_cast<LumieraReccondition> (this);}
};
typedef Mutex<Wrapped_LumieraExcMutex> NonrecursiveLock_NoWait;
@ -436,3 +447,10 @@ namespace lib {
} // namespace lumiera
#endif
/*
// Local Variables:
// mode: C++
// c-file-style: "gnu"
// indent-tabs-mode: nil
// End:
*/

View file

@ -78,3 +78,4 @@ namespace session {
}} // namespace mobject::session

View file

@ -57,7 +57,8 @@ namespace proc {
* with adjustable frequency. Quick'n dirty implementation!
*/
class TickService
: backend::Thread
: backend::JoinHandle,
backend::Thread
{
typedef function<void(void)> 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<uint>::max()
&& 1000000/fps > POLL_TIMEOUT);
||( 1000000/fps < std::numeric_limits<uint>::max()
&& 1000000/fps > POLL_TIMEOUT));
if (fps)
timespan_ = 1000000/fps; // microseconds per tick
else

View file

@ -2,31 +2,22 @@ TESTING "Linked Lists" ./test-llist
TEST "init nodes" basic <<END
out: 1
out: 1
END
TEST "insert nodes" nodeinsert <<END
out: 0
out: 0
out: 1
out: 0
out: 1
out: 1
out: 1
out: 1
out: 3
END
TEST "remaining predicates" predicates <<END
out: 1
out: 1
out: 0
out: 0
out: 1
out: 1
out: 0
out: 1
out: 0
out: 1
out: 0
END
@ -34,14 +25,11 @@ TEST "unlink" unlink <<END
out: node4 node3 node2 node1 .
out: node1 node4 .
out: 1
out: 1
out: 1
END
TEST "whiles" whiles <<END
out: node4 node3 node2 node1 .
out: .
out: .
END
TEST "llist_relocate" relocate <<END

View file

@ -11,6 +11,10 @@ TEST "mutex not unlocked asserts" mutexforgotunlock <<END
return: 134
END
TEST "mutex explicitly unlocked" mutexexplicitunlock <<END
return: 0
END
TEST "nested mutex section" nestedmutexsection <<END
out: outer mutex locked section
@ -19,14 +23,14 @@ END
TEST "chained mutex section" chainedmutexsection <<END
out: outer mutex locked section
out: ^outer mutex locked section
out: inner but not outer mutex locked section
END
TEST "recursive mutex section" recursivemutexsection <<END
out: mutex locked once
out: mutex locked twice
out: recmutex locked once
out: recmutex locked twice
END
@ -41,6 +45,15 @@ return: 134
END
PLANNED "rwlock readlock in writelock asserts" rwlockdeadlockwr <<END
return: 134
END
PLANNED "rwlock writelock in readlock asserts" rwlockdeadlockrw <<END
return: 134
END
TEST "condition not unlocked asserts" conditionforgotunlock <<END
return: 134
END
@ -57,3 +70,19 @@ PLANNED "condition broadcasting" <<END
END
TEST "reccondition not unlocked asserts" recconditionforgotunlock <<END
return: 134
END
TEST "reccondition section" recconditionsection <<END
out: reccondition locked section 1
out: reccondition locked section 2
END
PLANNED "reccondition signaling" <<END
END
PLANNED "reccondition broadcasting" <<END
END

38
tests/15mpool.tests Normal file
View file

@ -0,0 +1,38 @@
TESTING "Memory pool tests" ./test-mpool
TEST "init/destroy" basic <<END
err: initialized
err: allocated
err: DUMP
err: freed
err: DUMP
err: destroyed
return: 0
END
TEST "auto destruction" destroy <<END
return: 0
END
TEST "cluster allocation" clusters <<END
return: 0
END
PLANNED "random usage" random <<END
return: 0
END
PLANNED "stats" statscheck <<END
return: 0
END
TEST "reserve" reserve <<END
return: 0
END

46
tests/15slist.tests Normal file
View file

@ -0,0 +1,46 @@
TESTING "Single Linked Lists" ./test-slist
TEST "initialization and predicates" basic <<END
out: 1
out: 0
out: 1
out: 0
out: 1
out: 0
out: 1
out: 0
END
TEST "insert/delete nodes" insert_delete <<END
out: 1
out: 0
out: 1
out: 0
out: 1
END
TEST "moving across a list" movement <<END
out: 1
END
TEST "enumerates elements of a list" enumerations <<END
out: A B C D .
out: ---
out: B C .
out: ---
out: A B C D .
out: 1
END
TEST "get length and n-th element of a list" count <<END
out: 3
out: 1
END
TEST "sorts a list" sort <<END
return: 0
END
TEST "finds element inside a list" search <<END
out: 1
END

View file

@ -10,7 +10,8 @@ TEST "create configitem with empty line" configitem_simple '' <<END
out: line = ''
END
TEST "create configitem with blank line" configitem_simple ' ' <<END
# fails due libtool problems with empty strings
PLANNED "create configitem with blank line" configitem_simple ' ' <<END
out: line = ' '
END
@ -50,42 +51,42 @@ END
TEST "check content of configitem with empty line" configitem_simple_content_check $'' << END
END
TEST "check content of configitem with comment" configitem_simple_content_check $'\t #comment bla' << END
out: item->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 <key.bar' << END
out: item->line = 'key.foo <key.bar'
out: item->key_size = '7'
out: item->key = 'key.foo < key.bar'
out: item->delim = '< key.bar'
out: item->key = 'key.foo <key.bar'
out: item->delim = '<key.bar'
END

View file

@ -209,18 +209,16 @@ out: ' baz barf gnarf first second'
END
TEST "wordlist add same word" wordlist_add 'foo.bar' 'baz barf gnarf' same same << END
out: ' baz barf gnarf same'
out: ' baz barf gnarf same'
out: ^' baz barf gnarf same'$
END
TEST "wordlist add to empty list" wordlist_add 'foo.bar' '' first second << END
out: ' first'
out: ' first second'
out: ^' first'$
out: ^' first second'$
END
TEST "wordlist add to empty list, same" wordlist_add 'foo.bar' '' same same << END
out: ' same'
out: ' same'
out: ^' same'$
END

View file

@ -2,7 +2,7 @@ TESTING "testing plugins" ./test-interfaces
TEST "discovering plugins, missing path" plugin_discover <<END
out: found plugin: (null)
out: found plugin: \(null\)
return: 0
END
@ -10,12 +10,12 @@ export LUMIERA_PLUGIN_PATH=.libs
export LUMIERA_CONFIG_PATH=./
TEST "discovering plugins" plugin_discover <<END
out: found plugin: .libs/examplepluginc.lum
out: found plugin: \.libs/examplepluginc.lum
return: 0
END
TEST "plugin unloading" plugin_unload <<END
out: plugin unload: (nil)
out: plugin unload: \(nil\)
return: 0
END
@ -28,8 +28,8 @@ return: 0
END
TEST "C plugin test, nested" plugin_examplepluginc_nested <<END
out: config path is: ./
out: plugin path is: .libs
out: config path is: \./
out: plugin path is: \.libs
out: Hallo Welt!
out: Hello World!
out: Bye World!

View file

@ -3,9 +3,7 @@ TESTING "Component Test Suite: common and basic components" ./test-lib --group=c
TEST "Hello test" HelloWorld_test 3 <<END
out: This is how the world ends...
out: This is how the world ends...
out: This is how the world ends...
out: ^This is how the world ends\.\.\.$
return: 0
END
@ -21,35 +19,35 @@ END
TEST "CmdlineWrapper_test" CmdlineWrapper_test <<END
out: wrapping cmdline:...
out: wrapping cmdline:\.\.\.
out: -->
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 <<END
out: caught: LUMIERA_ERROR_LIFE_AND_UNIVERSE:and everything? (don't panic)...the answer is: 42
out: caught: LUMIERA_ERROR_LIFE_AND_UNIVERSE:and everything\? \(don't panic\)...the answer is: 42
out: caught lumiera::Error: LUMIERA_ERROR_DERIVED:convoluted exception
out: caught error::Logic: LUMIERA_ERROR_FATAL:floundered (test-2).
out: caught error::Invalid: LUMIERA_ERROR_INVALID:invalid input or parameters (test-3).
out: caught lumiera::Error: LUMIERA_ERROR_EXTERNAL:failure in external service (test-4).
out: caught error::Logic: LUMIERA_ERROR_FATAL:floundered \(test-2\).
out: caught error::Invalid: LUMIERA_ERROR_INVALID:invalid input or parameters \(test-3\).
out: caught lumiera::Error: LUMIERA_ERROR_EXTERNAL:failure in external service \(test-4\).
out: caught std::runtime_error: test-5
out: caught std::exception. (unspecific)
out: intermediate handler caught: LUMIERA_ERROR_EXTERNAL:failure in external service (test-7).....will rethrow as error::State
out: caught lumiera::Error: LUMIERA_ERROR_STATE:unforeseen state -- caused by: LUMIERA_ERROR_EXTERNAL:failure in external service (test-7).
out: intermediate handler caught: LUMIERA_ERROR_EXTERNAL:failure in external service (test-8).....will rethrow as error::State
out: 2nd intermediate handler caught: LUMIERA_ERROR_STATE:unforeseen state -- caused by: LUMIERA_ERROR_EXTERNAL:failure in external service (test-8).....will rethrow as error::Config
out: caught lumiera::Error: LUMIERA_ERROR_CONFIG:misconfiguration -- caused by: LUMIERA_ERROR_EXTERNAL:failure in external service (test-8).
out: caught std::exception. \(unspecific\)
out: intermediate handler caught: LUMIERA_ERROR_EXTERNAL:failure in external service \(test-7\).....will rethrow as error::State
out: caught lumiera::Error: LUMIERA_ERROR_STATE:unforeseen state -- caused by: LUMIERA_ERROR_EXTERNAL:failure in external service \(test-7\).
out: intermediate handler caught: LUMIERA_ERROR_EXTERNAL:failure in external service \(test-8\).....will rethrow as error::State
out: 2nd intermediate handler caught: LUMIERA_ERROR_STATE:unforeseen state -- caused by: LUMIERA_ERROR_EXTERNAL:failure in external service \(test-8\).....will rethrow as error::Config
out: caught lumiera::Error: LUMIERA_ERROR_CONFIG:misconfiguration -- caused by: LUMIERA_ERROR_EXTERNAL:failure in external service \(test-8\).
END
TEST "Factory_test" Factory_test 5 <<END
out: ctor TargetObj(5) successful
out: ctor TargetObj\(5\) successful
out: now the smart-ptr has refcount=3
out: .....TargetObj(5): data="*****", array[5]={0,1,2,3,4,}
out: dtor ~TargetObj(5) successful
out: .....TargetObj\(5\): data="\*\*\*\*\*", array\[5\]=\{0,1,2,3,4,\}
out: dtor ~TargetObj\(5\) successful
END
TEST "Factory_special_test" Factory_special_test 5 <<END
out: checkPlacement--------
out: ctor TargetObj(5) successful
out: ctor TargetObj\(5\) successful
out: created 3 shared_ptrs to Object placed in static buffer.
out: .....TargetObj(5): data="*****", array[5]={0,1,2,3,4,}
out: dtor ~TargetObj(5) successful
out: ctor TargetObj(6) successful
out: .....TargetObj\(5\): data="\*\*\*\*\*", array\[5\]=\{0,1,2,3,4,\}
out: dtor ~TargetObj\(5\) successful
out: ctor TargetObj\(6\) successful
out: created 4 shared_ptrs to Object placed in static buffer.
out: dtor ~TargetObj(6) successful
out: dtor ~TargetObj\(6\) successful
out: checkPrivate--------
out: ctor TargetObj(5) successful
out: ctor TargetObj\(5\) successful
out: created 3 shared_ptrs to paranoid Object.
out: .....TargetObj(5): data="*****", array[5]={0,1,2,3,4,}
out: dtor ~TargetObj(5) successful
out: .....TargetObj\(5\): data="\*\*\*\*\*", array\[5\]=\{0,1,2,3,4,\}
out: dtor ~TargetObj\(5\) successful
out: checkMalloc--------
out: ctor TargetObj(7) successful
out: ctor TargetObj\(7\) successful
out: created auto_ptr to malloc-ed Object.
out: .....TargetObj(7): data="*******", array[7]={0,1,2,3,4,5,6,}
out: dtor ~TargetObj(7) successful
out: .....TargetObj\(7\): data="\*\*\*\*\*\*\*", array\[7\]=\{0,1,2,3,4,5,6,\}
out: dtor ~TargetObj\(7\) successful
out: checkPImpl--------
out: ctor TargetObj(12) successful
out: ctor TargetObj\(12\) successful
out: created auto_ptr to Interface Object.
out: .....ImplObj::funky() called
out: .....TargetObj(12): data="************", array[12]={0,1,2,3,4,5,6,7,8,9,10,11,}
out: dtor ~TargetObj(12) successful
out: .....ImplObj::funky\(\) called
out: .....TargetObj\(12\): data="\*\*\*\*\*\*\*\*\*\*\*\*", array\[12\]=\{0,1,2,3,4,5,6,7,8,9,10,11,\}
out: dtor ~TargetObj\(12\) successful
END
@ -226,16 +224,16 @@ END
TEST "RemoveFromSet_test" RemoveFromSet_test <<END
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, ]
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 <<END
out: using the Singleton should create TargetObj(13)...
out: ctor TargetObj(13) successful
out: using the Singleton should create TargetObj\(13\)...
out: ctor TargetObj\(13\) successful
out: calling a non-static method on the Singleton-Implementation
out: .....TargetObj(13): data="*************", array[13]={0,1,2,3,4,5,6,7,8,9,10,11,12,}
out: dtor ~TargetObj(13) successful
out: .....TargetObj\(13\): data="\*\*\*\*\*\*\*\*\*\*\*\*\*", array\[13\]=\{0,1,2,3,4,5,6,7,8,9,10,11,12,\}
out: dtor ~TargetObj\(13\) successful
END
TEST "SingletonTestMock_test" SingletonTestMock_test <<END
out: TestSingletonO::doIt() call=1
out: TestSingletonO::doIt() call=2
out: Mock_1::doIt() call=1
out: Mock_1::doIt() call=2
out: Mock_1::doIt() call=3
out: Mock_1::doIt() call=4
out: Mock_1::doIt() call=5
out: Mock_2::doIt() call=1
out: TestSingletonO::doIt() call=3
out: TestSingletonO::doIt\(\) call=1
out: TestSingletonO::doIt\(\) call=2
out: Mock_1::doIt\(\) call=1
out: Mock_1::doIt\(\) call=2
out: Mock_1::doIt\(\) call=3
out: Mock_1::doIt\(\) call=4
out: Mock_1::doIt\(\) call=5
out: Mock_2::doIt\(\) call=1
out: TestSingletonO::doIt\(\) call=3
END
@ -284,19 +282,19 @@ END
TEST "ScopedHolderTransfer_test" ScopedHolderTransfer_test <<END
out: checking ScopedHolder<Dummy>...
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<Dummy>...
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 <<END
out: testing TargetObj(23) as Singleton(statically allocated)
out: ctor TargetObj(23) successful
out: testing TargetObj\(23\) as Singleton\(statically allocated\)
out: ctor TargetObj\(23\) successful
out: calling a non-static method on the Singleton instance
out: .....TargetObj(23): data="***********************", array[23]={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,}
out: testing TargetObj(24) as Singleton(heap allocated)
out: ctor TargetObj(24) successful
out: .....TargetObj\(23\): data="\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*", array\[23\]=\{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,\}
out: testing TargetObj\(24\) as Singleton\(heap allocated\)
out: ctor TargetObj\(24\) successful
out: calling a non-static method on the Singleton instance
out: .....TargetObj(24): data="************************", array[24]={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,}
out: dtor ~TargetObj(23) successful
out: dtor ~TargetObj(24) successful
out: .....TargetObj\(24\): data="\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*", array\[24\]=\{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,\}
out: dtor ~TargetObj\(23\) successful
out: dtor ~TargetObj\(24\) successful
END
@ -347,7 +345,8 @@ return: 0
END
PLANNED "Waiting on Thread termination" ThreadWrapperJoin_test <<END
TEST "Waiting on Thread termination" ThreadWrapperJoin_test <<END
return: 0
END
@ -428,9 +427,9 @@ out: ctor DoIt<Block< 5> >
out: ctor DoIt<Block< 3> >
out: ctor DoIt<Block< 2> >
out: ctor DoIt<Block< 1> >
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<Block< 1> >
out: dtor DoIt<Block< 2> >
@ -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

View file

@ -4,7 +4,7 @@ TESTING "Component Test Suite: Asset Manager" ./test-components --group=asset
TEST "AssetCategory_test" AssetCategory_test <<END
out: Category: AUDIO
out: Category: VIDEO/bin1
out: Category: VIDEO/bin1$
out: Category: VIDEO/bin1/subbin
out: Category: EFFECT/some_kind
return: 0

View file

@ -1,14 +1,12 @@
TESTING "Component Test Suite: Builder" ./test-components --group=builder
TEST "BuilderTool_test" BuilderTool_test <<END
out: apply (tool, clip);
out: Clip on media : Asset(VIDEO:lumi.test-1 v1)
out: apply (tool, test1);
out: treat (AbstractMO&);
out: apply (tool, test2);
out: apply \(tool, clip\);
out: Clip on media : Asset\(VIDEO:lumi.test-1 v1\)
out: apply \(tool, test1\);
out: treat \(AbstractMO&\);
out: apply \(tool, test2\);
out: catch-all-function called...
END

View file

@ -34,6 +34,12 @@ test_llist_SOURCES = $(tests_srcdir)/library/test-llist.c
test_llist_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror
test_llist_LDADD = $(NOBUGMT_LUMIERA_LIBS) liblumiera.la -lboost_program_options-mt -lboost_regex-mt
check_PROGRAMS += test-mpool
test_mpool_SOURCES = $(tests_srcdir)/library/test-mpool.c
test_mpool_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror
test_mpool_LDADD = $(NOBUGMT_LUMIERA_LIBS) \
liblumiera.la liblumieracommon.la liblumierabackend.la liblumieraproc.la -lboost_program_options-mt -lboost_regex-mt -ldl
check_PROGRAMS += test-psplay
test_psplay_SOURCES = $(tests_srcdir)/library/test-psplay.c
test_psplay_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror -I$(top_srcdir)/src/
@ -69,6 +75,10 @@ test_resourcecollector_SOURCES = $(tests_srcdir)/library/test-resourcecollector.
test_resourcecollector_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror -I$(top_srcdir)/src/
test_resourcecollector_LDADD = $(NOBUGMT_LUMIERA_LIBS) liblumierabackend.la liblumieracommon.la liblumieraproc.la -ldl liblumiera.la -lboost_program_options-mt -lboost_regex-mt
check_PROGRAMS += test-slist
test_slist_SOURCES = $(tests_srcdir)/library/test-slist.c
test_slist_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror
test_slist_LDADD = $(NOBUGMT_LUMIERA_LIBS) liblumiera.la -lboost_program_options-mt -lboost_regex-mt
check_PROGRAMS += test-config
test_config_SOURCES = $(tests_srcdir)/common/test-config.c
@ -105,4 +115,10 @@ test_interfaces_LDADD = \
-lboost_program_options-mt \
-lboost_regex-mt
check_PROGRAMS += test-threads
test_threads_SOURCES = $(tests_srcdir)/backend/test-threads.c
test_threads_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror -I$(top_srcdir)/src/
test_threads_CFLAGS = $(AM_CFLAGS) $(PTHREAD_CFLAGS)
test_threads_LDADD = liblumierabackend.la liblumiera.la -lnobugmt -lpthread -ldl -lm liblumieracommon.la liblumieraproc.la -ldl -lboost_program_options-mt -lboost_regex-mt
TESTS = $(tests_srcdir)/test.sh

View file

@ -0,0 +1,159 @@
/*
test-threads.c - test thread management
Copyright (C) Lumiera.org
2008, Christian Thaeter <ct@pipapo.org>
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 <stdio.h>
//#include <string.h>
#include "common/config.h"
#include "include/logging.h"
#include "lib/mutex.h"
#include "backend/threads.h"
#include "tests/test.h"
#include <unistd.h>
#include <errno.h>
#include <time.h>
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

View file

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

View file

@ -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 <stdio.h>
@ -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

343
tests/library/test-mpool.c Normal file
View file

@ -0,0 +1,343 @@
/*
test-mpool.c - memory pool for constant sized objects
Copyright (C) Lumiera.org
2009, Christian Thaeter <ct@pipapo.org>
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

349
tests/library/test-slist.c Normal file
View file

@ -0,0 +1,349 @@
/*
* test-slist.c - test the linked list lib
*
* Copyright (C) Lumiera.org
* 2009 Anton Yakovlev <just.yakovlev@gmail.com>
*
* 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 <sys/time.h>
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

View file

@ -1,4 +1,4 @@
#!/bin/bash
#!/bin/bash
# Copyright (C) Lumiera.org
# 2007 - 2008, Christian Thaeter <ct@pipapo.org>
#
@ -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}"