diff --git a/.gitignore b/.gitignore index c89dffc5e..f66bd5b09 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ autom4te.cache semantic.cache wiki/backups/* doc/devel/draw/*.png +m4/* diff --git a/SConstruct b/SConstruct index 1f1673f6c..c40c90ce2 100644 --- a/SConstruct +++ b/SConstruct @@ -33,7 +33,7 @@ TESTDIR = 'tests' ICONDIR = 'icons' VERSION = '0.1+pre.01' TOOLDIR = './admin/scons' -SVGRENDERER = 'admin/render-icon' +SCRIPTDIR = './admin' #-----------------------------------Configuration # NOTE: scons -h for help. @@ -47,6 +47,7 @@ import os import sys sys.path.append(TOOLDIR) +sys.path.append(SCRIPTDIR) from Buildhelper import * from LumieraEnvironment import * @@ -84,7 +85,7 @@ def setupBasicEnvironment(): , CCFLAGS='-Wall -Wextra ' , CFLAGS='-std=gnu99' ) - RegisterIcon_Builder(env,SVGRENDERER) + RegisterIcon_Builder(env) handleNoBugSwitches(env) env.Append(CPPDEFINES = '_GNU_SOURCE') diff --git a/admin/render-icon.py b/admin/render_icon.py similarity index 99% rename from admin/render-icon.py rename to admin/render_icon.py index 0f9cee294..e3f76c129 100755 --- a/admin/render-icon.py +++ b/admin/render_icon.py @@ -1,6 +1,6 @@ #!/usr/bin/python # -# render-icons.py - Icon rendering utility script +# render_icons.py - Icon rendering utility script # # Copyright (C) Lumiera.org # 2008, Joel Holdsworth diff --git a/admin/scons/Buildhelper.py b/admin/scons/Buildhelper.py index 159dc4721..f8e349a5e 100644 --- a/admin/scons/Buildhelper.py +++ b/admin/scons/Buildhelper.py @@ -196,12 +196,13 @@ def checkCommandOption(env, optID, val=None, cmdName=None): -def RegisterIcon_Builder(env, renderer): +def RegisterIcon_Builder(env): """ Registers Custom Builders for generating and installing Icons. Additionally you need to build the tool (rsvg-convert.c) used to generate png from the svg source using librsvg. """ - renderer = __import__(renderer) # load python script for invoking the render + + import render_icon as renderer # load Joel's python script for invoking the rsvg-convert (SVG render) renderer.rsvgPath = env.subst("$BINDIR/rsvg-convert") def invokeRenderer(target, source, env): diff --git a/admin/scons/LumieraEnvironment.py b/admin/scons/LumieraEnvironment.py index 44b76ed07..21b2a6c09 100644 --- a/admin/scons/LumieraEnvironment.py +++ b/admin/scons/LumieraEnvironment.py @@ -74,6 +74,7 @@ class LumieraEnvironment(Environment): return False self.libInfo[libID] = libInfo = LumieraEnvironment() + libInfo["ENV"]["PKG_CONFIG_PATH"] = os.environ.get("PKG_CONFIG_PATH") libInfo.ParseConfig ('pkg-config --cflags --libs '+ libID ) if alias: self.libInfo[alias] = libInfo diff --git a/icons/Makefile.am b/icons/Makefile.am index 97d817ab4..ce0990f26 100644 --- a/icons/Makefile.am +++ b/icons/Makefile.am @@ -18,7 +18,7 @@ svgdir = $(top_srcdir)/icons/svg prerendereddir = $(top_srcdir)/icons/prerendered icondir = $(top_builddir) -iconcommand = python $(top_srcdir)/admin/render-icon.py +iconcommand = python $(top_srcdir)/admin/render_icon.py 16x16 = $(icondir)/16x16 22x22 = $(icondir)/22x22 diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am index cb52fba55..6b64b35f7 100644 --- a/src/backend/Makefile.am +++ b/src/backend/Makefile.am @@ -26,6 +26,7 @@ liblumierabackend_la_SOURCES = \ $(liblumierabackend_la_srcdir)/mediaaccessfacade.cpp \ $(liblumierabackend_la_srcdir)/backend.c \ $(liblumierabackend_la_srcdir)/threads.c \ + $(liblumierabackend_la_srcdir)/threadpool.c \ $(liblumierabackend_la_srcdir)/file.c \ $(liblumierabackend_la_srcdir)/filehandle.c \ $(liblumierabackend_la_srcdir)/filedescriptor.c \ @@ -41,6 +42,7 @@ liblumierabackend_la_SOURCES = \ noinst_HEADERS += \ $(liblumierabackend_la_srcdir)/backend.h \ $(liblumierabackend_la_srcdir)/threads.h \ + $(liblumierabackend_la_srcdir)/threadpool.h \ $(liblumierabackend_la_srcdir)/file.h \ $(liblumierabackend_la_srcdir)/filehandle.h \ $(liblumierabackend_la_srcdir)/filedescriptor.h \ diff --git a/src/backend/thread-wrapper.hpp b/src/backend/thread-wrapper.hpp index cde254c5f..2355a6f8a 100644 --- a/src/backend/thread-wrapper.hpp +++ b/src/backend/thread-wrapper.hpp @@ -177,10 +177,11 @@ namespace backend { lumiera_thread_run ( kind , &run // invoking the run helper and.. , this // passing this start context as parameter - , joinCond // maybe wait-blocking for the thread to terminate , purpose.c() , logging_flag ); + (void)joinCond; // TODO: this is a temporary fix to match the C API + // we might have to re-write more of this file or even remove it later if (!res) throw lumiera::error::State("failed to create new thread."); @@ -207,7 +208,7 @@ namespace backend { : started_(false), operation_(operation) { - start_thread (LUMIERA_THREAD_INTERACTIVE, purpose, logging_flag); + start_thread (LUMIERA_THREADCLASS_INTERACTIVE, purpose, logging_flag); } /** Variant of the standard case, used to register a JoinHandle in addition to starting a thread. @@ -220,7 +221,7 @@ namespace backend { : started_(false), operation_(operation) { - start_thread (LUMIERA_THREAD_INTERACTIVE, purpose, logging_flag, + start_thread (LUMIERA_THREADCLASS_INTERACTIVE, purpose, logging_flag, join.accessLockedCondition()); } }; diff --git a/src/backend/threadpool.c b/src/backend/threadpool.c new file mode 100644 index 000000000..ea88b4503 --- /dev/null +++ b/src/backend/threadpool.c @@ -0,0 +1,188 @@ +/* + threadpool.c - Manage pools of threads + + Copyright (C) Lumiera.org + 2009, Michael Ploujnikov + + 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. +*/ + +//TODO: Support library includes// + +#include "include/logging.h" +#include "lib/safeclib.h" + +//TODO: Lumiera header includes// +#include "backend/threadpool.h" + +//TODO: internal/static forward declarations// +static lumiera_threadpool threadpool; + +//TODO: System includes// +#include + +/** + * @file + * + */ +NOBUG_DEFINE_FLAG_PARENT (threadpool, threads_dbg); /*TODO insert a suitable/better parent flag here */ + + +//code goes here// + +void +lumiera_threadpool_init(void) +{ + NOBUG_INIT_FLAG (threadpool); + NOBUG_INIT_FLAG (threads); + TRACE (threadpool); + NOBUG_INIT_FLAG (threads); + + for (int i = 0; i < LUMIERA_THREADCLASS_COUNT; ++i) + { + llist_init (&threadpool.pool[i].working_list); + llist_init (&threadpool.pool[i].idle_list); + threadpool.pool[i].thread_count = 0; + threadpool.pool[i].idle_thread_count = 0; + + //TODO: configure each pools' pthread_attrs appropriately + pthread_attr_init (&threadpool.pool[i].pthread_attrs); + //cancel... + + lumiera_condition_init (&threadpool.pool[i].sync,"pool of threads", &NOBUG_FLAG (threadpool)); + } +} + +void +lumiera_threadpool_destroy(void) +{ + TRACE (threadpool); + + for (int i = 0; i < LUMIERA_THREADCLASS_COUNT; ++i) + { + TRACE (threadpool, "destroying individual pool #%d", i); + LUMIERA_CONDITION_SECTION (threadpool, &threadpool.pool[i].sync) + { + REQUIRE (threadpool.pool[i].thread_count + == threadpool.pool[i].idle_thread_count + && 0 == llist_count (&threadpool.pool[i].working_list), + "%d(llist_count=%d) threads are still running", + threadpool.pool[i].thread_count + - threadpool.pool[i].idle_thread_count, + llist_count (&threadpool.pool[i].working_list)); + REQUIRE ((int)(llist_count (&threadpool.pool[i].working_list) + + llist_count (&threadpool.pool[i].idle_list)) + == threadpool.pool[i].thread_count, + "threadpool counter miscalculation (working_list count = %u, idle_list count = %u, thread_count = %d )", + llist_count (&threadpool.pool[i].working_list), + llist_count (&threadpool.pool[i].idle_list), + threadpool.pool[i].thread_count); + LLIST_WHILE_HEAD (&threadpool.pool[i].idle_list, t) + { + lumiera_thread_delete ((LumieraThread)t); + threadpool.pool[i].thread_count--; + } + } + lumiera_condition_destroy (&threadpool.pool[i].sync, &NOBUG_FLAG (threadpool)); + pthread_attr_destroy (&threadpool.pool[i].pthread_attrs); + } +} + + +LumieraThread +lumiera_threadpool_acquire_thread(enum lumiera_thread_class kind, + const char* purpose, + struct nobug_flag* flag) +{ + TRACE (threadpool); + LumieraThread ret = NULL; + + REQUIRE (kind < LUMIERA_THREADCLASS_COUNT, "unknown pool kind specified: %d", kind); + + LUMIERA_CONDITION_SECTION (threadpool, &threadpool.pool[kind].sync) + { + if (llist_is_empty (&threadpool.pool[kind].idle_list)) + { + ret = lumiera_thread_new (kind, purpose, flag, + &threadpool.pool[kind].pthread_attrs); + ENSURE (ret, "did not create a valid thread"); + TODO ("no error handing, let the resourcecollector do it, no need when returning the thread"); + threadpool.pool[kind].thread_count++; + LUMIERA_CONDITION_WAIT (!llist_is_empty (&threadpool.pool[kind].idle_list)); + } + // use an existing thread, pick the first one + // remove it from the pool's list + ret = (LumieraThread) (llist_head (&threadpool.pool[kind].idle_list)); + + ENSURE (ret, "did not find a valid thread"); + + REQUIRE (ret->state == LUMIERA_THREADSTATE_IDLE, "trying to return a non-idle thread (state=%s)", lumiera_threadstate_names[ret->state]); + + // move thread to the working_list + llist_insert_head (&threadpool.pool[kind].working_list, &ret->node); + + threadpool.pool[kind].idle_thread_count--; // cheaper than using llist_count + REQUIRE ((int)(llist_count (&threadpool.pool[kind].working_list) + + llist_count (&threadpool.pool[kind].idle_list)) + == threadpool.pool[kind].thread_count, + "threadpool counter miscalculation (working_list count = %u, idle_list count = %u, thread_count = %d )", + llist_count (&threadpool.pool[kind].working_list), + llist_count (&threadpool.pool[kind].idle_list), + threadpool.pool[kind].thread_count); + } + return ret; +} + +// TODO: rename to lumiera_threadpool_park_thread +void +lumiera_threadpool_release_thread(LumieraThread thread) +{ + TRACE (threadpool); + REQUIRE (thread, "invalid thread given"); + REQUIRE (thread->kind < LUMIERA_THREADCLASS_COUNT, "thread belongs to an unknown pool kind: %d", thread->kind); + + REQUIRE (thread->state != LUMIERA_THREADSTATE_IDLE, "trying to park an already idle thread"); + LUMIERA_CONDITION_SECTION (threadpool, &threadpool.pool[thread->kind].sync) + { + REQUIRE (!llist_is_member (&threadpool.pool[thread->kind].idle_list, &thread->node), "thread is already in the idle list"); + REQUIRE (llist_is_member (&threadpool.pool[thread->kind].working_list, &thread->node) + || thread->state == LUMIERA_THREADSTATE_STARTUP, + "thread is not in the working list (state=%s)", + lumiera_threadstate_names[thread->state]); + thread->state = LUMIERA_THREADSTATE_IDLE; + // move thread to the idle_list + llist_insert_head (&threadpool.pool[thread->kind].idle_list, &thread->node); + + threadpool.pool[thread->kind].idle_thread_count++; // cheaper than using llist_count + REQUIRE ((int)(llist_count (&threadpool.pool[thread->kind].working_list) + + llist_count (&threadpool.pool[thread->kind].idle_list)) + == threadpool.pool[thread->kind].thread_count, + "threadpool counter miscalculation (working_list count = %u, idle_list count = %u, thread_count = %d )", + llist_count (&threadpool.pool[thread->kind].working_list), + llist_count (&threadpool.pool[thread->kind].idle_list), + threadpool.pool[thread->kind].thread_count); + + REQUIRE (!llist_is_empty (&threadpool.pool[thread->kind].idle_list), "thread pool is still empty after insertion"); + LUMIERA_CONDITION_BROADCAST; + } +} + +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/backend/threadpool.h b/src/backend/threadpool.h new file mode 100644 index 000000000..47d90e6fc --- /dev/null +++ b/src/backend/threadpool.h @@ -0,0 +1,96 @@ +/* + threadpool.h - Manage pools of threads + + Copyright (C) Lumiera.org + 2009, Michael Ploujnikov + + 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_THREADPOOL_H +#define LUMIERA_THREADPOOL_H + +//TODO: Support library includes// +#include "lib/condition.h" +#include "lib/llist.h" + +//TODO: Forward declarations// + + +//TODO: Lumiera header includes// +#include "threads.h" + +//TODO: System includes// +#include + + +/** + * @file + * + */ + +//TODO: declarations go here// + +/** + * Acquire a thread from a threadpool. + * This may either pick a thread from an appropriate pool or create a new one when the pool is empty. + * This function doesn't need to be accessible outside of the threadpool implementation. + */ +LumieraThread +lumiera_threadpool_acquire_thread(enum lumiera_thread_class kind, + const char* purpose, + struct nobug_flag* flag); + +/** + * Park a thread + * This ends up putting a finished thread back on the list of an appropriate threadpool. + * This function doesn't need to be accessible outside of the threadpool implementation. + */ +void +lumiera_threadpool_release_thread(LumieraThread thread); + +typedef struct lumiera_threadpool_struct lumiera_threadpool; +typedef lumiera_threadpool* LumieraThreadpool; + +struct lumiera_threadpool_struct +{ + struct + { + llist working_list; + llist idle_list; + int thread_count; + int idle_thread_count; + pthread_attr_t pthread_attrs; + lumiera_condition sync; + } pool[LUMIERA_THREADCLASS_COUNT]; +}; + +/** + * Initialize the thread pool. + */ +void +lumiera_threadpool_init(void); + +void +lumiera_threadpool_destroy(void); + +#endif +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/backend/threads.c b/src/backend/threads.c index 67f2d4aa4..3dd1bdbae 100644 --- a/src/backend/threads.c +++ b/src/backend/threads.c @@ -22,6 +22,8 @@ //TODO: Support library includes// #include "include/logging.h" +#include "lib/safeclib.h" + //TODO: Lumiera header includes// #include "threads.h" @@ -31,84 +33,155 @@ //TODO: System includes// #include +#include /** * @file * */ -//NOBUG_DEFINE_FLAG_PARENT (threads, lumiera); /*TODO insert a suitable/better parent flag here */ +NOBUG_DEFINE_FLAG_PARENT (threads, threads_dbg); /*TODO insert a suitable/better parent flag here */ //code goes here// - -struct lumiera_thread_mockup -{ - void (*fn)(void*); - void* arg; - LumieraReccondition finished; +#define LUMIERA_THREAD_CLASS(name) #name, +// enum string trick: expands as an array of thread class name strings +const char* lumiera_threadclass_names[] = { + LUMIERA_THREAD_CLASSES }; +#undef LUMIERA_THREAD_CLASS -static void* pthread_runner (void* thread) +#define LUMIERA_THREAD_STATE(name) #name, +const char* lumiera_threadstate_names[] = { + LUMIERA_THREAD_STATES +}; +#undef LUMIERA_THREAD_STATE + +static void* thread_loop (void* thread) { + TRACE (threads); + NOBUG_THREAD_ID_SET ("worker"); + LumieraThread t = (LumieraThread)thread; + pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, NULL); - struct lumiera_thread_mockup* starter = (struct lumiera_thread_mockup*) thread; - LumieraReccondition thread_end_notification = starter->finished; + REQUIRE (t, "thread does not exist"); - starter->fn (starter->arg); + LUMIERA_CONDITION_SECTION (threads, &t->signal) + { + do { + // NULL function means: no work to do + INFO (threads, "function %p", t->function); + if (t->function) + t->function (t->arguments); + lumiera_threadpool_release_thread(t); + LUMIERA_CONDITION_WAIT (t->state != LUMIERA_THREADSTATE_IDLE); + INFO (threads, "Thread awaken with state %d", t->state); + } while (t->state != LUMIERA_THREADSTATE_SHUTDOWN); + // SHUTDOWN state - 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; + INFO (threads, "Thread Shutdown"); + } + return 0; } - -static pthread_once_t attr_once = PTHREAD_ONCE_INIT; -static pthread_attr_t attrs; - -static void thread_attr_init (void) -{ - pthread_attr_init (&attrs); - pthread_attr_setdetachstate (&attrs, PTHREAD_CREATE_DETACHED); - //cancel ... -} - - +// when this is called it should have already been decided that the function +// shall run in parallel, as a thread LumieraThread lumiera_thread_run (enum lumiera_thread_class kind, - void (*start_routine)(void *), + void (*function)(void *), void * arg, - LumieraReccondition finished, const char* purpose, struct nobug_flag* flag) { - (void) kind; - (void) purpose; - (void) flag; + TRACE (threads); + // REQUIRE (function, "invalid function"); - if (attr_once == PTHREAD_ONCE_INIT) - pthread_once (&attr_once, thread_attr_init); + // ask the threadpool for a thread (it might create a new one) + LumieraThread self = lumiera_threadpool_acquire_thread (kind, purpose, flag); - static struct lumiera_thread_mockup thread; + // set the function and data to be run + self->function = function; + self->arguments = arg; - thread.fn = start_routine; - thread.arg = arg; - thread.finished = finished; + // and let it really run (signal the condition var, the thread waits on it) + self->state = LUMIERA_THREADSTATE_WAKEUP; - pthread_t dummy; - int error = pthread_create (&dummy, &attrs, pthread_runner, &thread); + LUMIERA_CONDITION_SECTION (cond_sync, &self->signal) + LUMIERA_CONDITION_SIGNAL; - if (error) return 0; /////TODO temporary addition by Ichthyo; probably we'll set lumiera_error? - return (LumieraThread) 1; + // NOTE: example only, add solid error handling! + + return self; } +/** + * Create a new thread structure with a matching pthread + */ +LumieraThread +lumiera_thread_new (enum lumiera_thread_class kind, + const char* purpose, + struct nobug_flag* flag, + pthread_attr_t* attrs) +{ + // TODO: do something with these: + (void) purpose; + REQUIRE (kind < LUMIERA_THREADCLASS_COUNT, "invalid thread kind specified: %d", kind); + REQUIRE (attrs, "invalid pthread attributes structure passed"); + + LumieraThread self = lumiera_malloc (sizeof (*self)); + llist_init (&self->node); + lumiera_condition_init (&self->signal, "thread-control", flag); + self->kind = kind; + self->state = LUMIERA_THREADSTATE_STARTUP; + self->function = NULL; + self->arguments = NULL; + + int error = pthread_create (&self->id, attrs, &thread_loop, self); + if (error) + { + LUMIERA_DIE (ERRNO); + } + return self; +} + +LumieraThread +lumiera_thread_destroy (LumieraThread self) +{ + TRACE (threads); + REQUIRE (self, "trying to destroy an invalid thread"); + + llist_unlink (&self->node); + + // get the pthread out of the processing loop + // need to signal to the thread that it should start quitting + // should this be within the section? + LUMIERA_CONDITION_SECTION (threads, &self->signal) + { + REQUIRE (self->state == LUMIERA_THREADSTATE_IDLE, "trying to delete a thread in state other than IDLE (%s)", lumiera_threadstate_names[self->state]); + self->state = LUMIERA_THREADSTATE_SHUTDOWN; + self->function = NULL; + self->arguments = NULL; + LUMIERA_CONDITION_SIGNAL; + } + + int error = pthread_join (self->id, NULL); + ENSURE (0 == error, "pthread_join returned %d:%s", error, strerror (error)); + + // condition has to be destroyed after joining with the thread + lumiera_condition_destroy (&self->signal, &NOBUG_FLAG (threads)); + + return self; +} + +void +lumiera_thread_delete (LumieraThread self) +{ + TRACE (threads); + lumiera_free (lumiera_thread_destroy (self)); +} /* // Local Variables: diff --git a/src/backend/threads.h b/src/backend/threads.h index 9d420fa2f..42d76bcfe 100644 --- a/src/backend/threads.h +++ b/src/backend/threads.h @@ -23,7 +23,7 @@ #define LUMIERA_THREADS_H //TODO: Support library includes// -#include "lib/reccondition.h" +#include "lib/condition.h" //TODO: Forward declarations// @@ -34,6 +34,7 @@ //TODO: System includes// #include +NOBUG_DECLARE_FLAG (threads); /** @@ -46,6 +47,22 @@ typedef struct lumiera_thread_struct lumiera_thread; typedef lumiera_thread* LumieraThread; +// this is used for an enum string trick +#define LUMIERA_THREAD_CLASSES \ + /** mostly idle, low latency **/ \ + LUMIERA_THREAD_CLASS(INTERACTIVE) \ + /** busy at average priority **/ \ + LUMIERA_THREAD_CLASS(WORKER) \ + /** busy, soft realtime, high priority **/ \ + LUMIERA_THREAD_CLASS(URGENT) \ + /** high latency, background jobs **/ \ + LUMIERA_THREAD_CLASS(BATCH) \ + /** Something to do when there is really nothing else to do **/ \ + LUMIERA_THREAD_CLASS(IDLE) + +// enum string trick: expands as an enum of thread classes +#define LUMIERA_THREAD_CLASS(name) LUMIERA_THREADCLASS_##name, + /** * Thread classes. * We define some 'classes' of threads for different purposes to abstract @@ -53,17 +70,12 @@ typedef lumiera_thread* LumieraThread; */ enum lumiera_thread_class { - /** mostly idle, low latency **/ - LUMIERA_THREAD_INTERACTIVE, - /** busy at average priority **/ - LUMIERA_THREAD_WORKER, - /** busy, soft realtime, high priority **/ - LUMIERA_THREAD_URGEND, - /** high latency, background jobs **/ - LUMIERA_THREAD_BATCH, - /** Something to do when there is really nothing else to do **/ - LUMIERA_THREAD_IDLE, + LUMIERA_THREAD_CLASSES + /** this just denotes the number of classes listed above, + it is used to create arrays **/ + LUMIERA_THREADCLASS_COUNT, + // .. various thread flags follow /** * flag to let the decision to run the function in a thread open to the backend. * depending on load it might decide to run it sequentially. @@ -74,6 +86,86 @@ enum lumiera_thread_class LUMIERA_THREAD_OR_NOT = 1<<16 }; +#undef LUMIERA_THREAD_CLASS + +// defined in threads.c +extern const char* lumiera_threadclass_names[]; + +// there is some confusion between the meaning of this +// on one hand it could be used to tell the current state of the thread +// on the other, it is used to tell the thread which state to enter on next iteration +#define LUMIERA_THREAD_STATES \ + LUMIERA_THREAD_STATE(IDLE) \ + LUMIERA_THREAD_STATE(ERROR) \ + LUMIERA_THREAD_STATE(RUNNING) \ + LUMIERA_THREAD_STATE(WAKEUP) \ + LUMIERA_THREAD_STATE(SHUTDOWN) \ + LUMIERA_THREAD_STATE(STARTUP) + +#define LUMIERA_THREAD_STATE(name) LUMIERA_THREADSTATE_##name, + +/** + * Thread state. + * These are the only states our threads can be in. + */ +typedef enum + { + LUMIERA_THREAD_STATES + } + lumiera_thread_state; + +#undef LUMIERA_THREAD_STATE + +// defined in threads.c +extern const char* lumiera_threadstate_names[]; + +#include "threadpool.h" + +/** + * The actual thread data + */ +struct lumiera_thread_struct +{ + llist node; // this should be first for easy casting + // the function and argument can be passed to the thread at creation time + // void (*function)(void*); + // void* arg; + pthread_t id; + // TODO: maybe this condition variable should be renamed when we have a better understanding of how it will be used + lumiera_condition signal; // control signal, state change signal + // the following member could have been called "class" except that it would conflict with C++ keyword + // as consequence, it's been decided to leave the type name containing the word "class", + // while all members/variables called "kind" + enum lumiera_thread_class kind; + // this is used both as a command and as a state tracker + lumiera_thread_state state; + void (*function)(void *); + void * arguments; +}; + +/** + * Create a thread structure. + */ +LumieraThread +lumiera_thread_new (enum lumiera_thread_class kind, + const char* purpose, + struct nobug_flag* flag, + pthread_attr_t* attrs); + +/** + * Destroy and de-initialize a thread structure. + * Memory is not freed by this function. + */ +LumieraThread +lumiera_thread_destroy (LumieraThread self); + +/** + * Actually free the memory used by the thread structure. + * Make sure to destroy the structure first. + */ +void +lumiera_thread_delete (LumieraThread self); + /** * Start a thread. * Threads are implemented as procedures which take a void* and dont return anything. @@ -85,23 +177,19 @@ enum lumiera_thread_class * * Threads shall not handle signals (all signals will be disabled for them) unless explicitly acknowledged * * @param kind class of the thread to start - * @param start_routine pointer to a function to execute in a thread (returning void, not void* as in pthreads) + * @param function pointer to a function to execute in a thread (returning void, not void* as in pthreads) * @param arg generic pointer passed to the thread - * @param finished a condition variable to be broadcasted, if not NULL. - * The associated mutex should be locked at thread_run time already, else the signal can get lost. * @param purpose descriptive name of this thread, used by NoBug * @param flag NoBug flag used for logging the thread startup and return */ LumieraThread lumiera_thread_run (enum lumiera_thread_class kind, - void (*start_routine)(void *), + void (*function)(void *), void * arg, - LumieraReccondition finished, const char* purpose, struct nobug_flag* flag); - #endif /* // Local Variables: diff --git a/src/proc/mobject/placement.hpp b/src/proc/mobject/placement.hpp index 1ebb966ac..569dc6686 100644 --- a/src/proc/mobject/placement.hpp +++ b/src/proc/mobject/placement.hpp @@ -242,6 +242,7 @@ namespace mobject { /** @todo cleanup uses of ref-to-placement. See Ticket #115 */ typedef Placement PlacementMO; + //‘typedef class mobject::Placement mobject::PlacementMO’ typedef Placement PMO; diff --git a/src/proc/mobject/session/placement-index.cpp b/src/proc/mobject/session/placement-index.cpp index f55e6fc3a..036fff3f4 100644 --- a/src/proc/mobject/session/placement-index.cpp +++ b/src/proc/mobject/session/placement-index.cpp @@ -95,7 +95,7 @@ namespace session { /* some type shorthands */ - typedef PlacementIndex::PlacementMO PlacementMO; + typedef mobject::PlacementMO PlacementMO; typedef PlacementIndex::PRef PRef; typedef PlacementIndex::ID ID; diff --git a/src/proc/mobject/session/placement-index.hpp b/src/proc/mobject/session/placement-index.hpp index a8df50b2a..2da6540b0 100644 --- a/src/proc/mobject/session/placement-index.hpp +++ b/src/proc/mobject/session/placement-index.hpp @@ -160,7 +160,6 @@ namespace session { public: - typedef Placement PlacementMO; typedef PlacementRef PRef; typedef PlacementMO::ID const& ID; diff --git a/src/tool/Makefile.am b/src/tool/Makefile.am index 4c1c50fd9..d3851349b 100644 --- a/src/tool/Makefile.am +++ b/src/tool/Makefile.am @@ -18,6 +18,7 @@ lumitool_srcdir = $(top_srcdir)/src/tool noinst_PROGRAMS += luidgen + luidgen_CFLAGS = $(CFLAGS) $(LUMIERA_TOOL_CFLAGS) -std=gnu99 -Wall -Werror luidgen_CPPFLAGS = -I$(top_srcdir)/src/ luidgen_LDADD = liblumiera.la $(LUMIERA_TOOL_LIBS) diff --git a/tests/15locking.tests b/tests/15locking.tests index 0936499f2..7401d4b58 100644 --- a/tests/15locking.tests +++ b/tests/15locking.tests @@ -86,3 +86,12 @@ PLANNED "reccondition broadcasting" < + + 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 "backend/threadpool.h" + +#include +#include +#include +#include + +void is_prime(void * arg) +{ + int number = *(int *)arg; + int prime = 1; + + for (int x = number; x >= sqrt(number); --x) + { + if (number % x == 0) + { + prime = 0; + break; + } + } + *(int *)arg = prime; +} + +TESTS_BEGIN + +TEST ("threadpool-basic") +{ + lumiera_threadpool_init(); + lumiera_threadpool_destroy(); +} + +TEST ("threadpool1") +{ + ECHO("start by initializing the threadpool"); + lumiera_threadpool_init(); + LumieraThread t1 = + lumiera_threadpool_acquire_thread(LUMIERA_THREADCLASS_INTERACTIVE, + "test purpose", + &NOBUG_FLAG(NOBUG_ON)); + // lumiera_threadpool_release_thread(t1); + ECHO("acquired thread 1 %p",t1); + lumiera_threadpool_destroy(); +} + + +TEST ("basic-acquire-release") +{ + ECHO("start by initializing the threadpool"); + lumiera_threadpool_init(); + ECHO("acquiring thread 1"); + LumieraThread t1 = + lumiera_threadpool_acquire_thread(LUMIERA_THREADCLASS_INTERACTIVE, + "test purpose", + &NOBUG_FLAG(NOBUG_ON)); + ECHO("acquiring thread 2"); + LumieraThread t2 = + lumiera_threadpool_acquire_thread(LUMIERA_THREADCLASS_IDLE, + "test purpose", + &NOBUG_FLAG(NOBUG_ON)); + + ECHO("thread 1 kind=%s", lumiera_threadclass_names[t1->kind]); + CHECK(LUMIERA_THREADCLASS_INTERACTIVE == t1->kind); + ECHO("thread 1 state=%s", lumiera_threadstate_names[t1->state]); + CHECK(LUMIERA_THREADSTATE_IDLE == t1->state); + ECHO("thread 2 kind=%s", lumiera_threadclass_names[t2->kind]); + CHECK(LUMIERA_THREADCLASS_IDLE == t2->kind); + ECHO("thread 2 state=%s", lumiera_threadstate_names[t2->state]); + CHECK(LUMIERA_THREADSTATE_IDLE == t2->state); + + ECHO("releasing thread 1"); + //lumiera_threadpool_release_thread(t1); + ECHO("thread 1 has been released"); + + ECHO("releasing thread 2"); + //lumiera_threadpool_release_thread(t2); + ECHO("thread 2 has been released"); + + lumiera_threadpool_destroy(); +} + +#if 0 +TEST ("many-acquire-release") +{ + + const int threads_per_pool_count = 10; + + lumiera_threadpool_init(10); + LumieraThread threads[threads_per_pool_count*LUMIERA_THREADCLASS_COUNT]; + + for (int kind = 0; kind < LUMIERA_THREADCLASS_COUNT; ++kind) + { + for (int i = 0; i < threads_per_pool_count; ++i) + { + threads[i+kind*threads_per_pool_count] = + lumiera_threadpool_acquire_thread(kind, + "test purpose", + &NOBUG_FLAG(NOBUG_ON)); + } + } + + for (int i = 0; i < threads_per_pool_count*LUMIERA_THREADCLASS_COUNT; ++i) + { + lumiera_threadpool_release_thread(threads[i]); + } + + lumiera_threadpool_destroy(); + +} + +TEST ("toomany-acquire-release") +{ + + const int threads_per_pool_count = 11; + + lumiera_threadpool_init(10); + LumieraThread threads[threads_per_pool_count*LUMIERA_THREADCLASS_COUNT]; + + for (int kind = 0; kind < LUMIERA_THREADCLASS_COUNT; ++kind) + { + for (int i = 0; i < threads_per_pool_count; ++i) + { + threads[i+kind*threads_per_pool_count] = + lumiera_threadpool_acquire_thread(kind, + "test purpose", + &NOBUG_FLAG(NOBUG_ON)); + } + } + + for (int i = 0; i < threads_per_pool_count*LUMIERA_THREADCLASS_COUNT; ++i) + { + lumiera_threadpool_release_thread(threads[i]); + } + + lumiera_threadpool_destroy(); + +} +#endif + +TEST ("no-function") +{ + LumieraThread t; + + lumiera_threadpool_init(); + + t = lumiera_thread_run (LUMIERA_THREADCLASS_INTERACTIVE, + NULL, + NULL, + "process my test function", + &NOBUG_FLAG(NOBUG_ON)); + + // cleanup + ECHO("wait 1 sec"); + usleep(1000000); + ECHO("finished waiting"); + lumiera_threadpool_destroy(); +} + +TEST ("process-function") +{ + // this is what the scheduler would do once it figures out what function a job needs to run + LumieraThread t; + int number = 440616; + + lumiera_threadpool_init(); + + ECHO ("the input to the function is %d", number); + + t = lumiera_thread_run (LUMIERA_THREADCLASS_INTERACTIVE, + &is_prime, + (void *)&number, //void * arg, + "process my test function", + &NOBUG_FLAG(NOBUG_ON)); // struct nobug_flag* flag) + + // cleanup + ECHO("wait 1 sec"); + usleep(1000000); + ECHO("finished waiting"); + lumiera_threadpool_destroy(); +} + +TESTS_END diff --git a/tests/backend/test-threads.c b/tests/backend/test-threads.c index 48fc3a114..1a22d361c 100644 --- a/tests/backend/test-threads.c +++ b/tests/backend/test-threads.c @@ -47,15 +47,15 @@ void threadfn(void* blah) void threadsyncfn(void* blah) { struct timespec wait = {0,200000000}; - LumieraReccondition sync = (LumieraReccondition) blah; + LumieraCondition sync = (LumieraCondition) blah; ECHO ("thread starting up %s", NOBUG_THREAD_ID_GET); - LUMIERA_RECCONDITION_SECTION(cond_sync, sync) + LUMIERA_CONDITION_SECTION(cond_sync, sync) { ECHO ("send startup signal %s", NOBUG_THREAD_ID_GET); - LUMIERA_RECCONDITION_SIGNAL; + LUMIERA_CONDITION_SIGNAL; ECHO ("wait for trigger %s", NOBUG_THREAD_ID_GET); - LUMIERA_RECCONDITION_WAIT(1); + LUMIERA_CONDITION_WAIT(1); } ECHO ("thread running %s", NOBUG_THREAD_ID_GET); @@ -88,10 +88,9 @@ TEST ("simple_thread") { fprintf (stderr, "main before thread %s\n", NOBUG_THREAD_ID_GET); - lumiera_thread_run (LUMIERA_THREAD_WORKER, + lumiera_thread_run (LUMIERA_THREADCLASS_WORKER, threadfn, NULL, - NULL, argv[1], NULL); @@ -102,32 +101,31 @@ TEST ("simple_thread") TEST ("thread_synced") { - lumiera_reccondition cnd; - lumiera_reccondition_init (&cnd, "threadsync", &NOBUG_FLAG(NOBUG_ON)); + lumiera_condition cnd; + lumiera_condition_init (&cnd, "threadsync", &NOBUG_FLAG(NOBUG_ON)); - LUMIERA_RECCONDITION_SECTION(cond_sync, &cnd) + LUMIERA_CONDITION_SECTION(cond_sync, &cnd) { ECHO ("main before thread %s", NOBUG_THREAD_ID_GET); - lumiera_thread_run (LUMIERA_THREAD_WORKER, + lumiera_thread_run (LUMIERA_THREADCLASS_WORKER, threadsyncfn, &cnd, - &cnd, argv[1], NULL); ECHO ("main wait for thread being ready %s", NOBUG_THREAD_ID_GET); - LUMIERA_RECCONDITION_WAIT(1); + LUMIERA_CONDITION_WAIT(1); ECHO ("main trigger thread %s", NOBUG_THREAD_ID_GET); - LUMIERA_RECCONDITION_SIGNAL; + LUMIERA_CONDITION_SIGNAL; ECHO ("wait for thread end %s", NOBUG_THREAD_ID_GET); - LUMIERA_RECCONDITION_WAIT(1); + LUMIERA_CONDITION_WAIT(1); ECHO ("thread ended %s", NOBUG_THREAD_ID_GET); } - lumiera_reccondition_destroy (&cnd, &NOBUG_FLAG(NOBUG_ON)); + lumiera_condition_destroy (&cnd, &NOBUG_FLAG(NOBUG_ON)); } @@ -140,10 +138,9 @@ TEST ("mutex_thread") { fprintf (stderr, "main before thread %s\n", NOBUG_THREAD_ID_GET); - lumiera_thread_run (LUMIERA_THREAD_WORKER, + lumiera_thread_run (LUMIERA_THREADCLASS_WORKER, mutexfn, NULL, - NULL, argv[1], NULL); @@ -156,7 +153,7 @@ TEST ("mutex_thread") } -PLANNED ("error_cleared_on_join") +TEST ("error_cleared_on_join") { //when a thread/job is finished, there must be no pending errors } diff --git a/tests/library/test-locking.c b/tests/library/test-locking.c index 14ec0c458..c073c18de 100644 --- a/tests/library/test-locking.c +++ b/tests/library/test-locking.c @@ -318,5 +318,41 @@ TEST ("recconditionforgotunlock") lumiera_reccondition_destroy (&reccond, &NOBUG_FLAG(NOBUG_ON)); } +TEST ("chainedrecconditionsection") +{ + lumiera_reccondition outer, inner; + lumiera_reccondition_init (&outer, "outer_recconditionsection", &NOBUG_FLAG(NOBUG_ON)); + lumiera_reccondition_init (&inner, "inner_recconditionsection", &NOBUG_FLAG(NOBUG_ON)); + + LUMIERA_RECCONDITION_SECTION (NOBUG_ON, &outer) + { + printf ("outer reccondition locked section\n"); + LUMIERA_RECCONDITION_SECTION_CHAIN (NOBUG_ON, &inner) + { + printf ("inner reccondition locked section\n"); + } + } + lumiera_reccondition_destroy (&outer, &NOBUG_FLAG(NOBUG_ON)); + lumiera_reccondition_destroy (&inner, &NOBUG_FLAG(NOBUG_ON)); +} + +TEST ("nestedrecconditionsection") +{ + lumiera_reccondition outer, inner; + lumiera_reccondition_init (&outer, "outer_recconditionsection", &NOBUG_FLAG(NOBUG_ON)); + lumiera_reccondition_init (&inner, "inner_recconditionsection", &NOBUG_FLAG(NOBUG_ON)); + + LUMIERA_RECCONDITION_SECTION (NOBUG_ON, &outer) + { + printf ("outer reccondition locked section\n"); + LUMIERA_RECCONDITION_SECTION (NOBUG_ON, &inner) + { + printf ("inner reccondition locked section\n"); + } + } + lumiera_reccondition_destroy (&outer, &NOBUG_FLAG(NOBUG_ON)); + lumiera_reccondition_destroy (&inner, &NOBUG_FLAG(NOBUG_ON)); +} + TESTS_END diff --git a/tests/test.conf b/tests/test.conf index c86e03260..38cdca9cc 100755 --- a/tests/test.conf +++ b/tests/test.conf @@ -1 +1 @@ -LOGSUPPRESS='^\(\*\*[0-9]*\*\* \)\?[0-9]\{10,\}: \(TRACE\|INFO\|NOTICE\|WARNING\|ERR\|TODO\|PLANNED\|FIXME\|DEPRECATED\|UNIMPLEMENTED\):' +LOGSUPPRESS='^\(\*\*[0-9]*\*\* \)\?[0-9]\{10,\}: \(TRACE\|INFO\|NOTICE\|WARNING\|ERR\|TODO\|PLANNED\|FIXME\|DEPRECATED\|UNIMPLEMENTED\|RESOURCE_ANNOUNCE\):'