Merge backend and testsuite improvements
This commit is contained in:
commit
fba135c746
46 changed files with 3682 additions and 819 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 =
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
#include "include/logging.h"
|
||||
|
||||
#include "lib/mutex.h"
|
||||
#include "lib/recmutex.h"
|
||||
#include "lib/safeclib.h"
|
||||
|
||||
#include "common/plugin.h"
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ WorkspaceWindow::WorkspaceWindow(Project &source_project,
|
|||
|
||||
WorkspaceWindow::~WorkspaceWindow()
|
||||
{
|
||||
INFO (gui_dbg, "closing workspace window...");
|
||||
}
|
||||
|
||||
Project&
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
518
src/lib/mpool.c
Normal 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
247
src/lib/mpool.h
Normal 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:
|
||||
*/
|
||||
|
|
@ -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
|
||||
|
|
|
|||
184
src/lib/mutex.h
184
src/lib/mutex.h
|
|
@ -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
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
74
src/lib/recmutex.c
Normal 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
132
src/lib/recmutex.h
Normal 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:
|
||||
*/
|
||||
|
|
@ -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
|
||||
|
|
|
|||
151
src/lib/rwlock.h
151
src/lib/rwlock.h
|
|
@ -45,53 +45,106 @@ LUMIERA_ERROR_DECLARE(RWLOCK_WRLOCK);
|
|||
/**
|
||||
* Read locked section.
|
||||
*/
|
||||
#define LUMIERA_RDLOCK_SECTION(nobugflag, rwlck) \
|
||||
for (lumiera_rwlockacquirer NOBUG_CLEANUP(lumiera_rwlockacquirer_ensureunlocked) lumiera_rwlock_section_ = {(LumieraRWLock)1}; \
|
||||
lumiera_rwlock_section_.rwlock;) \
|
||||
for ( \
|
||||
({ \
|
||||
lumiera_rwlock_section_.rwlock = (rwlck); \
|
||||
NOBUG_RESOURCE_HANDLE_INIT (lumiera_rwlock_section_.rh); \
|
||||
RESOURCE_ENTER (nobugflag, (rwlck)->rh, "acquire rwlock for reading", &lumiera_rwlock_section_, \
|
||||
NOBUG_RESOURCE_EXCLUSIVE, lumiera_rwlock_section_.rh); \
|
||||
if (pthread_rwlock_rdlock (&(rwlck)->rwlock)) LUMIERA_DIE (RWLOCK_RDLOCK); \
|
||||
}); \
|
||||
lumiera_rwlock_section_.rwlock; \
|
||||
({ \
|
||||
if (lumiera_rwlock_section_.rwlock) \
|
||||
{ \
|
||||
pthread_rwlock_unlock (&lumiera_rwlock_section_.rwlock->rwlock); \
|
||||
lumiera_rwlock_section_.rwlock = NULL; \
|
||||
RESOURCE_LEAVE(nobugflag, lumiera_rwlock_section_.rh); \
|
||||
} \
|
||||
#define LUMIERA_RDLOCK_SECTION(nobugflag, rwlck) \
|
||||
for (lumiera_sectionlock NOBUG_CLEANUP(lumiera_sectionlock_ensureunlocked) \
|
||||
lumiera_lock_section_ = { \
|
||||
(void*)1, lumiera_rwlock_unlock_cb NOBUG_ALPHA_COMMA_NULL NOBUG_ALPHA_COMMA_NULL}; \
|
||||
lumiera_lock_section_.lock;) \
|
||||
for ( \
|
||||
({ \
|
||||
lumiera_lock_section_.lock = (rwlck); \
|
||||
NOBUG_IF_ALPHA(lumiera_lock_section_.flag = &NOBUG_FLAG(nobugflag);) \
|
||||
RESOURCE_ENTER (nobugflag, (rwlck)->rh, "acquire readlock", \
|
||||
&lumiera_lock_section_, NOBUG_RESOURCE_WAITING, \
|
||||
lumiera_lock_section_.rh); \
|
||||
if (pthread_rwlock_rdlock (&(rwlck)->rwlock)) \
|
||||
LUMIERA_DIE (LOCK_ACQUIRE); \
|
||||
TODO ("implement NOBUG_RESOURCE_SHARED"); \
|
||||
/*RESOURCE_STATE (nobugflag, NOBUG_RESOURCE_SHARED, lumiera_lock_section_.rh); */ \
|
||||
}); \
|
||||
lumiera_lock_section_.lock; \
|
||||
({ \
|
||||
LUMIERA_RWLOCK_SECTION_UNLOCK; \
|
||||
}))
|
||||
|
||||
|
||||
#define LUMIERA_RDLOCK_SECTION_CHAIN(nobugflag, rwlck) \
|
||||
for (lumiera_sectionlock *lumiera_lock_section_old_ = &lumiera_lock_section_, \
|
||||
NOBUG_CLEANUP(lumiera_sectionlock_ensureunlocked) lumiera_lock_section_ = { \
|
||||
(void*)1, lumiera_rwlock_unlock_cb NOBUG_ALPHA_COMMA_NULL NOBUG_ALPHA_COMMA_NULL}; \
|
||||
lumiera_lock_section_.lock;) \
|
||||
for ( \
|
||||
({ \
|
||||
REQUIRE (lumiera_lock_section_old_->lock, "section prematurely unlocked"); \
|
||||
lumiera_lock_section_.lock = (rwlck); \
|
||||
NOBUG_IF_ALPHA(lumiera_lock_section_.flag = &NOBUG_FLAG(nobugflag);) \
|
||||
RESOURCE_ENTER (nobugflag, (rwlck)->rh, "acquire readlock", &lumiera_lock_section_, \
|
||||
NOBUG_RESOURCE_WAITING, lumiera_lock_section_.rh); \
|
||||
if (pthread_rwlock_rdlock (&(rwlck)->rwlock)) \
|
||||
LUMIERA_DIE (LOCK_ACQUIRE); \
|
||||
/*RESOURCE_STATE (nobugflag, NOBUG_RESOURCE_SHARED, lumiera_lock_section_.rh); */ \
|
||||
LUMIERA_SECTION_UNLOCK_(lumiera_lock_section_old_); \
|
||||
}); \
|
||||
lumiera_lock_section_.lock; \
|
||||
({ \
|
||||
LUMIERA_MUTEX_SECTION_UNLOCK; \
|
||||
}))
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Write locked section.
|
||||
*/
|
||||
#define LUMIERA_WRLOCK_SECTION(nobugflag, rwlck) \
|
||||
for (lumiera_rwlockacquirer NOBUG_CLEANUP(lumiera_rwlockacquirer_ensureunlocked) lumiera_rwlock_section_ = {(LumieraRWLock)1}; \
|
||||
lumiera_rwlock_section_.rwlock;) \
|
||||
for ( \
|
||||
({ \
|
||||
lumiera_rwlock_section_.rwlock = (rwlck); \
|
||||
NOBUG_RESOURCE_HANDLE_INIT (lumiera_rwlock_section_.rh); \
|
||||
RESOURCE_ENTER (nobugflag, (rwlck)->rh, "acquire rwlock for reading", &lumiera_rwlock_section_, \
|
||||
NOBUG_RESOURCE_EXCLUSIVE, lumiera_rwlock_section_.rh); \
|
||||
if (pthread_rwlock_wrlock (&(rwlck)->rwlock)) LUMIERA_DIE (RWLOCK_WRLOCK); \
|
||||
}); \
|
||||
lumiera_rwlock_section_.rwlock; \
|
||||
({ \
|
||||
if (lumiera_rwlock_section_.rwlock) \
|
||||
{ \
|
||||
pthread_rwlock_unlock (&lumiera_rwlock_section_.rwlock->rwlock); \
|
||||
lumiera_rwlock_section_.rwlock = NULL; \
|
||||
RESOURCE_LEAVE(nobugflag, lumiera_rwlock_section_.rh); \
|
||||
} \
|
||||
#define LUMIERA_WRLOCK_SECTION(nobugflag, rwlck) \
|
||||
for (lumiera_sectionlock NOBUG_CLEANUP(lumiera_sectionlock_ensureunlocked) \
|
||||
lumiera_lock_section_ = { \
|
||||
(void*)1, lumiera_rwlock_unlock_cb NOBUG_ALPHA_COMMA_NULL NOBUG_ALPHA_COMMA_NULL}; \
|
||||
lumiera_lock_section_.lock;) \
|
||||
for ( \
|
||||
({ \
|
||||
lumiera_lock_section_.lock = (rwlck); \
|
||||
NOBUG_IF_ALPHA(lumiera_lock_section_.flag = &NOBUG_FLAG(nobugflag);) \
|
||||
RESOURCE_ENTER (nobugflag, (rwlck)->rh, "acquire writelock", \
|
||||
&lumiera_lock_section_, NOBUG_RESOURCE_WAITING, \
|
||||
lumiera_lock_section_.rh); \
|
||||
if (pthread_rwlock_wrlock (&(rwlck)->rwlock)) \
|
||||
LUMIERA_DIE (LOCK_ACQUIRE); \
|
||||
RESOURCE_STATE (nobugflag, NOBUG_RESOURCE_EXCLUSIVE, lumiera_lock_section_.rh); \
|
||||
}); \
|
||||
lumiera_lock_section_.lock; \
|
||||
({ \
|
||||
LUMIERA_RWLOCK_SECTION_UNLOCK; \
|
||||
}))
|
||||
|
||||
|
||||
#define LUMIERA_WRLOCK_SECTION_CHAIN(nobugflag, rwlck) \
|
||||
for (lumiera_sectionlock *lumiera_lock_section_old_ = &lumiera_lock_section_, \
|
||||
NOBUG_CLEANUP(lumiera_sectionlock_ensureunlocked) lumiera_lock_section_ = { \
|
||||
(void*)1, lumiera_rwlock_unlock_cb NOBUG_ALPHA_COMMA_NULL NOBUG_ALPHA_COMMA_NULL}; \
|
||||
lumiera_lock_section_.lock;) \
|
||||
for ( \
|
||||
({ \
|
||||
REQUIRE (lumiera_lock_section_old_->lock, "section prematurely unlocked"); \
|
||||
lumiera_lock_section_.lock = (rwlck); \
|
||||
NOBUG_IF_ALPHA(lumiera_lock_section_.flag = &NOBUG_FLAG(nobugflag);) \
|
||||
RESOURCE_ENTER (nobugflag, (rwlck)->rh, "acquire writelock", &lumiera_lock_section_, \
|
||||
NOBUG_RESOURCE_WAITING, lumiera_lock_section_.rh); \
|
||||
if (pthread_rwlock_wrlock (&(twlck)->rwlock)) \
|
||||
LUMIERA_DIE (LOCK_ACQUIRE); \
|
||||
RESOURCE_STATE (nobugflag, NOBUG_RESOURCE_EXCLUSIVE, lumiera_lock_section_.rh); \
|
||||
LUMIERA_SECTION_UNLOCK_(lumiera_lock_section_old_); \
|
||||
}); \
|
||||
lumiera_lock_section_.lock; \
|
||||
({ \
|
||||
LUMIERA_MUTEX_SECTION_UNLOCK; \
|
||||
}))
|
||||
|
||||
|
||||
#define LUMIERA_RWLOCK_SECTION_UNLOCK \
|
||||
LUMIERA_SECTION_UNLOCK_(&lumiera_lock_section_)
|
||||
|
||||
|
||||
/**
|
||||
* RWLock.
|
||||
*
|
||||
|
|
@ -121,26 +174,8 @@ LumieraRWLock
|
|||
lumiera_rwlock_destroy (LumieraRWLock self, struct nobug_flag* flag);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* rwlockacquirer used to manage the state of a rwlock variable.
|
||||
*/
|
||||
struct lumiera_rwlockacquirer_struct
|
||||
{
|
||||
LumieraRWLock rwlock;
|
||||
RESOURCE_HANDLE (rh);
|
||||
};
|
||||
typedef struct lumiera_rwlockacquirer_struct lumiera_rwlockacquirer;
|
||||
typedef struct lumiera_rwlockacquirer_struct* LumieraRWLockacquirer;
|
||||
|
||||
/* helper function for nobug */
|
||||
static inline void
|
||||
lumiera_rwlockacquirer_ensureunlocked (LumieraRWLockacquirer self)
|
||||
{
|
||||
ENSURE (!self->rwlock, "forgot to unlock rwlock");
|
||||
}
|
||||
int
|
||||
lumiera_rwlock_unlock_cb (void* rwlock);
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
|||
82
src/lib/sectionlock.h
Normal file
82
src/lib/sectionlock.h
Normal 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
666
src/lib/slist.h
Normal 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 */
|
||||
|
|
@ -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:
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -78,3 +78,4 @@ namespace session {
|
|||
|
||||
|
||||
}} // namespace mobject::session
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
38
tests/15mpool.tests
Normal 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
46
tests/15slist.tests
Normal 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
|
||||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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!
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
159
tests/backend/test-threads.c
Normal file
159
tests/backend/test-threads.c
Normal 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
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
343
tests/library/test-mpool.c
Normal 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
349
tests/library/test-slist.c
Normal 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
|
||||
112
tests/test.sh
112
tests/test.sh
|
|
@ -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}"
|
||||
|
|
|
|||
Loading…
Reference in a new issue