From 41bb8b7e78b8df8f6724ce1b78edb0059d87324a Mon Sep 17 00:00:00 2001 From: Michael Ploujnikov Date: Thu, 3 Dec 2009 09:29:56 -0500 Subject: [PATCH 1/8] make a TODO note about the finished condition variable --- src/backend/threads.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/backend/threads.h b/src/backend/threads.h index e8b774299..ac8a163fd 100644 --- a/src/backend/threads.h +++ b/src/backend/threads.h @@ -124,6 +124,7 @@ struct lumiera_thread_struct // 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 LumieraReccondition finished; // 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", From 24e87c815ddf6522ccbe8af87b7f722c3a6f62e1 Mon Sep 17 00:00:00 2001 From: Michael Ploujnikov Date: Thu, 17 Dec 2009 22:08:57 -0500 Subject: [PATCH 2/8] remove old functions pthread_runner and lumiera_thread_run --- src/backend/threads.c | 53 ------------------------------------------- 1 file changed, 53 deletions(-) diff --git a/src/backend/threads.c b/src/backend/threads.c index 62f0d14a6..0520de288 100644 --- a/src/backend/threads.c +++ b/src/backend/threads.c @@ -73,59 +73,6 @@ static void* thread_loop (void* arg) return 0; } -#if 0 -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; -} -#endif - -#if 0 -// TODO: rewrite this using lumiera_threadpool_acquire() -LumieraThread -lumiera_thread_run (enum lumiera_thread_class kind, - void (*start_routine)(void *), - void * arg, - LumieraReccondition finished, - const char* purpose, - struct nobug_flag* flag) -{ - (void) kind; - (void) purpose; - (void) flag; - - if (attr_once == PTHREAD_ONCE_INIT) - pthread_once (&attr_once, thread_attr_init); - - static struct lumiera_thread_mockup thread; - - thread.fn = start_routine; - thread.arg = arg; - thread.finished = finished; - - pthread_t dummy; - int error = pthread_create (&dummy, &attrs, pthread_runner, &thread); - - if (error) return 0; /////TODO temporary addition by Ichthyo; probably we'll set lumiera_error? - return (LumieraThread) 1; -} -#endif - -// TODO: new implementation, remove the above one -// maybe this shouldn't return LumieraThread at all // when this is called it should have already been decided that the function // shall run in parallel, as a thread LumieraThread From f2406c23a1ffda8a2938690ff389bf8660ab7b5e Mon Sep 17 00:00:00 2001 From: Michael Ploujnikov Date: Wed, 23 Dec 2009 13:10:31 -0500 Subject: [PATCH 3/8] bork bork bork by pulling this you agree to... --- src/backend/thread-wrapper.hpp | 2 +- src/backend/threadpool.c | 96 ++++++++++++++++++-------------- src/backend/threadpool.h | 17 ++++-- src/backend/threads.c | 81 ++++++++++++++++++--------- src/backend/threads.h | 23 +++++--- tests/30backend-threadpool.tests | 63 +++------------------ tests/backend/test-threadpool.c | 70 +++++++++++++++++++++-- tests/backend/test-threads.c | 3 - tests/test.sh | 2 +- 9 files changed, 208 insertions(+), 149 deletions(-) diff --git a/src/backend/thread-wrapper.hpp b/src/backend/thread-wrapper.hpp index 3f897b582..f8ec2bc87 100644 --- a/src/backend/thread-wrapper.hpp +++ b/src/backend/thread-wrapper.hpp @@ -177,10 +177,10 @@ 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; if (!res) throw lumiera::error::State("failed to create new thread."); diff --git a/src/backend/threadpool.c b/src/backend/threadpool.c index 66ecd4e33..2a33978d6 100644 --- a/src/backend/threadpool.c +++ b/src/backend/threadpool.c @@ -53,21 +53,22 @@ void* pool_thread_loop(void * arg) } void -lumiera_threadpool_init(unsigned limit) +lumiera_threadpool_init() { for (int i = 0; i < LUMIERA_THREADCLASS_COUNT; ++i) { llist_init(&threadpool.pool[i].list); - threadpool.pool[i].max_threads = limit; - threadpool.pool[i].working_thread_count = 0; + threadpool.pool[i].working_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); - pthread_attr_setdetachstate (&threadpool.pool[i].pthread_attrs, PTHREAD_CREATE_DETACHED); + // cehteh prefers that threads are joinable by default + //pthread_attr_setdetachstate (&threadpool.pool[i].pthread_attrs, PTHREAD_CREATE_DETACHED); //cancel... lumiera_mutex_init(&threadpool.pool[i].lock,"pool of threads", &NOBUG_FLAG(threadpool)); + lumiera_reccondition_init (&threadpool.pool[i].signal, "thread-signal", &NOBUG_FLAG(threadpool)); } } @@ -78,10 +79,16 @@ lumiera_threadpool_destroy(void) for (int i = 0; i < LUMIERA_THREADCLASS_COUNT; ++i) { ECHO ("destroying individual pool #%d", i); - // no locking is done at this point - ECHO ("number of threads in the pool=%d", llist_count(&threadpool.pool[i].list)); - LLIST_WHILE_HEAD(&threadpool.pool[i].list, thread) - lumiera_thread_delete((LumieraThread)thread); + LUMIERA_MUTEX_SECTION (threadpool, &threadpool.pool[i].lock) + { + REQUIRE (0 == threadpool.pool[i].working_thread_count, "%d threads are running", threadpool.pool[i].working_thread_count); + // TODO need to have a stronger assertion that no threads are really running because they will not even be in the list + ECHO ("number of threads in the pool=%d", llist_count(&threadpool.pool[i].list)); + LLIST_WHILE_HEAD(&threadpool.pool[i].list, t) + { + lumiera_thread_delete((LumieraThread)t); + } + } ECHO ("destroying the pool mutex"); lumiera_mutex_destroy (&threadpool.pool[i].lock, &NOBUG_FLAG (threadpool)); ECHO ("pool mutex destroyed"); @@ -89,6 +96,15 @@ lumiera_threadpool_destroy(void) } } +void lumiera_threadpool_unlink(LumieraThread thread) +{ + REQUIRE (thread, "invalid thread given"); + REQUIRE (thread->kind < LUMIERA_THREADCLASS_COUNT, "thread belongs to an unknown pool kind: %d", thread->kind); + llist_unlink(&thread->node); + ENSURE (llist_is_empty(&thread->node), "failed to unlink the thread"); +} + + LumieraThread lumiera_threadpool_acquire_thread(enum lumiera_thread_class kind, const char* purpose, @@ -97,52 +113,45 @@ lumiera_threadpool_acquire_thread(enum lumiera_thread_class kind, LumieraThread ret; REQUIRE (kind < LUMIERA_THREADCLASS_COUNT, "unknown pool kind specified: %d", kind); - if (llist_is_empty (&threadpool.pool[kind].list)) - { - // TODO: fill in the reccondition argument, currently NULL - FIXME ("this max thread logic needs to be deeply thought about and made more efficient as well as rebust"); - if (threadpool.pool[kind].working_thread_count - + threadpool.pool[kind].idle_thread_count - < threadpool.pool[kind].max_threads) { - ret = lumiera_thread_new (kind, NULL, purpose, flag, + LUMIERA_RECCONDITION_SECTION (threadpool, &threadpool.pool[kind].signal) + { + if (llist_is_empty (&threadpool.pool[kind].list)) + { + + ret = lumiera_thread_new (kind, purpose, flag, &threadpool.pool[kind].pthread_attrs); - threadpool.pool[kind].working_thread_count++; + threadpool.pool[kind].idle_thread_count++; ENSURE (ret, "did not create a valid thread"); + LUMIERA_RECCONDITION_WAIT (!llist_is_empty (&threadpool.pool[kind].list)); } - else - { - //ERROR (threadpool, "did not create a new thread because per-pool limit was reached: %d", threadpool.pool[kind].max_threads); - LUMIERA_DIE(ERRNO); - } - } - else - { - // use an existing thread, pick the first one - // remove it from the pool's list - LUMIERA_MUTEX_SECTION (threadpool, &threadpool.pool[kind].lock) - { - ret = (LumieraThread)(llist_unlink(llist_head (&threadpool.pool[kind].list))); - threadpool.pool[kind].working_thread_count++; - threadpool.pool[kind].idle_thread_count--; // cheaper than using llist_count - ENSURE (threadpool.pool[kind].idle_thread_count == - llist_count(&threadpool.pool[kind].list), - "idle thread count %d is wrong, should be %d", - threadpool.pool[kind].idle_thread_count, - llist_count(&threadpool.pool[kind].list)); - } - ENSURE (ret, "did not find a valid thread"); - } - return ret; + // use an existing thread, pick the first one + // remove it from the pool's list + ret = (LumieraThread)(llist_unlink(llist_head (&threadpool.pool[kind].list))); + REQUIRE (ret->state == LUMIERA_THREADSTATE_IDLE, "trying to return a non-idle thread (state=%s)", lumiera_threadstate_names[ret->state]); + threadpool.pool[kind].working_thread_count++; + threadpool.pool[kind].idle_thread_count--; // cheaper than using llist_count + ENSURE (threadpool.pool[kind].idle_thread_count == + llist_count(&threadpool.pool[kind].list), + "idle thread count %d is wrong, should be %d", + threadpool.pool[kind].idle_thread_count, + llist_count(&threadpool.pool[kind].list)); + ENSURE (ret, "did not find a valid thread"); + } + return ret; } +// TODO: rename to lumiera_threadpool_park_thread void -lumiera_threadpool_release_thread(LumieraThread thread) +lumiera_threadpool_park_thread(LumieraThread thread) { REQUIRE (thread, "invalid thread given"); REQUIRE (thread->kind < LUMIERA_THREADCLASS_COUNT, "thread belongs to an unknown pool kind: %d", thread->kind); - LUMIERA_MUTEX_SECTION (threadpool, &threadpool.pool[thread->kind].lock) + REQUIRE (thread->state != LUMIERA_THREADSTATE_IDLE, "trying to park an already idle thread"); + + LUMIERA_RECCONDITION_SECTION (threadpool, &threadpool.pool[thread->kind].signal) { + thread->state = LUMIERA_THREADSTATE_IDLE; REQUIRE (llist_is_single(&thread->node), "thread already belongs to some list"); llist_insert_head(&threadpool.pool[thread->kind].list, &thread->node); threadpool.pool[thread->kind].working_thread_count--; @@ -153,6 +162,7 @@ lumiera_threadpool_release_thread(LumieraThread thread) threadpool.pool[thread->kind].idle_thread_count, llist_count(&threadpool.pool[thread->kind].list)); // REQUIRE (!llist_is_empty (&threadpool.pool[thread->kind].list), "thread pool is still empty after insertion"); + LUMIERA_RECCONDITION_BROADCAST; } } diff --git a/src/backend/threadpool.h b/src/backend/threadpool.h index 03b56a4fe..de545876c 100644 --- a/src/backend/threadpool.h +++ b/src/backend/threadpool.h @@ -55,12 +55,12 @@ lumiera_threadpool_acquire_thread(enum lumiera_thread_class kind, struct nobug_flag* flag); /** - * Release a thread - * This ends up putting a (parked/idle) thread back on the list of an appropriate threadpool. + * 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); +lumiera_threadpool_park_thread(LumieraThread thread); typedef struct lumiera_threadpool_struct lumiera_threadpool; typedef lumiera_threadpool* LumieraThreadpool; @@ -71,23 +71,28 @@ struct lumiera_threadpool_struct { llist list; lumiera_mutex lock; - unsigned max_threads; unsigned working_thread_count; unsigned idle_thread_count; pthread_attr_t pthread_attrs; + lumiera_reccondition signal; } pool[LUMIERA_THREADCLASS_COUNT]; }; /** * Initialize the thread pool. - * @param limit the maximum number of threads (idle+working) allowed per pool */ void -lumiera_threadpool_init(unsigned limit); +lumiera_threadpool_init(); void lumiera_threadpool_destroy(void); +/** + * Just remove the thread structure from an associated pool list. + */ +void +lumiera_threadpool_unlink(LumieraThread thread); + #endif /* // Local Variables: diff --git a/src/backend/threads.c b/src/backend/threads.c index 0520de288..a75b7f81b 100644 --- a/src/backend/threads.c +++ b/src/backend/threads.c @@ -67,9 +67,29 @@ struct lumiera_thread_mockup LumieraReccondition finished; }; -static void* thread_loop (void* arg) +static void* thread_loop (void* thread) { - (void)arg; + LumieraThread t = (LumieraThread)thread; + + pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, NULL); + + REQUIRE (t, "thread does not exist"); + + ECHO ("entering section 1"); + // this seems to deadlock unexpectedly: + LUMIERA_RECCONDITION_SECTION (threads, &t->signal) + { + do { + // NULL function means: no work to do + if (t->function) + t->function (t->arguments); + lumiera_threadpool_park_thread(t); + LUMIERA_RECCONDITION_WAIT(t->state != LUMIERA_THREADSTATE_IDLE); + } while (t->state != LUMIERA_THREADSTATE_SHUTDOWN); + // SHUTDOWN state + + ECHO ("thread quitting"); + } return 0; } @@ -79,22 +99,23 @@ LumieraThread lumiera_thread_run (enum lumiera_thread_class kind, void (*function)(void *), void * arg, - LumieraReccondition finished, const char* purpose, struct nobug_flag* flag) { - (void)finished; - (void)function; - (void)arg; + REQUIRE (function, "invalid function"); + // ask the threadpool for a thread (it might create a new one) LumieraThread self = lumiera_threadpool_acquire_thread(kind, purpose, flag); - // TODO: set the function and data to be run - // lumiera_thread_set_func_data (self, start_routine, arg, purpose, flag); + // set the function and data to be run + self->function = function; + self->arguments = arg; // and let it really run (signal the condition var, the thread waits on it) - LUMIERA_RECCONDITION_SECTION(cond_sync, self->finished) - LUMIERA_RECCONDITION_SIGNAL; + self->state = LUMIERA_THREADSTATE_WAKEUP; + ECHO ("entering section 2"); + LUMIERA_RECCONDITION_SECTION(threads, &self->signal) + LUMIERA_RECCONDITION_BROADCAST; // NOTE: example only, add solid error handling! @@ -106,29 +127,25 @@ lumiera_thread_run (enum lumiera_thread_class kind, */ LumieraThread lumiera_thread_new (enum lumiera_thread_class kind, - LumieraReccondition finished, const char* purpose, struct nobug_flag* flag, pthread_attr_t* attrs) { // TODO: do something with these: (void) purpose; - (void) flag; REQUIRE (kind < LUMIERA_THREADCLASS_COUNT, "invalid thread kind specified: %d", kind); REQUIRE (attrs, "invalid pthread attributes structure passed"); - //REQUIRE (finished, "invalid finished flag passed"); - - LumieraThread self = lumiera_malloc (sizeof (*self)); llist_init(&self->node); - self->finished = finished; + lumiera_reccondition_init (&self->signal, "thread-control", flag); self->kind = kind; - self->state = LUMIERA_THREADSTATE_IDLE; + self->state = LUMIERA_THREADSTATE_STARTUP; + self->function = NULL; + self->arguments = NULL; - //REQUIRE (thread_loop); int error = pthread_create (&self->id, attrs, &thread_loop, self); - ENSURE(error == 0 || EAGAIN == error, "pthread returned %d:%s", error, strerror(error)); + ENSURE(error == 0 || EAGAIN == error, "pthread_create returned %d:%s", error, strerror(error)); if (error) { // error here can only be EAGAIN, given the above ENSURE @@ -143,12 +160,26 @@ lumiera_thread_destroy (LumieraThread self) { REQUIRE (self, "trying to destroy an invalid thread"); - // TODO: stop the pthread - llist_unlink(&self->node); - //finished = NULL; // or free(finished)? - lumiera_reccondition_destroy (self->finished, &NOBUG_FLAG(threads)); - //kind = 0; - //state = 0; + lumiera_threadpool_unlink(self); + + // 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_RECCONDITION_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_RECCONDITION_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_reccondition_destroy (&self->signal, &NOBUG_FLAG(threads)); + return self; } diff --git a/src/backend/threads.h b/src/backend/threads.h index ac8a163fd..a55926123 100644 --- a/src/backend/threads.h +++ b/src/backend/threads.h @@ -90,10 +90,16 @@ enum lumiera_thread_class // defined in threads.c extern const char* lumiera_threadclass_names[]; -#define LUMIERA_THREAD_STATES \ - LUMIERA_THREAD_STATE(IDLE) \ - LUMIERA_THREAD_STATE(RUNNING) \ - LUMIERA_THREAD_STATE(ERROR) +// 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, @@ -125,12 +131,15 @@ struct lumiera_thread_struct // 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 - LumieraReccondition finished; + lumiera_reccondition 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; }; /** @@ -138,7 +147,6 @@ struct lumiera_thread_struct */ LumieraThread lumiera_thread_new (enum lumiera_thread_class kind, - LumieraReccondition finished, const char* purpose, struct nobug_flag* flag, pthread_attr_t* attrs); @@ -162,8 +170,6 @@ lumiera_thread_delete (LumieraThread self); * @param kind class of the thread to start * @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 */ @@ -171,7 +177,6 @@ LumieraThread lumiera_thread_run (enum lumiera_thread_class kind, void (*function)(void *), void * arg, - LumieraReccondition finished, const char* purpose, struct nobug_flag* flag); diff --git a/tests/30backend-threadpool.tests b/tests/30backend-threadpool.tests index c136aff42..2cad43477 100644 --- a/tests/30backend-threadpool.tests +++ b/tests/30backend-threadpool.tests @@ -5,24 +5,11 @@ PLANNED "create" PLANNED "yield" PLANNED "cancel" -TEST "Acquire/Release test" basic-acquire-release < #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(100); + lumiera_threadpool_destroy(); +} + +TEST ("threadpool1") +{ + ECHO("start by initializing the threadpool"); + lumiera_threadpool_init(100); + 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") { @@ -37,12 +73,12 @@ TEST ("basic-acquire-release") LumieraThread t1 = lumiera_threadpool_acquire_thread(LUMIERA_THREADCLASS_INTERACTIVE, "test purpose", - NULL); + &NOBUG_FLAG(NOBUG_ON)); ECHO("acquiring thread 2"); LumieraThread t2 = lumiera_threadpool_acquire_thread(LUMIERA_THREADCLASS_IDLE, "test purpose", - NULL); + &NOBUG_FLAG(NOBUG_ON)); ECHO("thread 1 kind=%s", lumiera_threadclass_names[t1->kind]); CHECK(LUMIERA_THREADCLASS_INTERACTIVE == t1->kind); @@ -54,16 +90,17 @@ TEST ("basic-acquire-release") CHECK(LUMIERA_THREADSTATE_IDLE == t2->state); ECHO("releasing thread 1"); - lumiera_threadpool_release_thread(t1); + //lumiera_threadpool_release_thread(t1); ECHO("thread 1 has been released"); ECHO("releasing thread 2"); - lumiera_threadpool_release_thread(t2); + //lumiera_threadpool_release_thread(t2); ECHO("thread 2 has been released"); lumiera_threadpool_destroy(); } +#if 0 TEST ("many-acquire-release") { @@ -79,7 +116,7 @@ TEST ("many-acquire-release") threads[i+kind*threads_per_pool_count] = lumiera_threadpool_acquire_thread(kind, "test purpose", - NULL); + &NOBUG_FLAG(NOBUG_ON)); } } @@ -107,7 +144,7 @@ TEST ("toomany-acquire-release") threads[i+kind*threads_per_pool_count] = lumiera_threadpool_acquire_thread(kind, "test purpose", - NULL); + &NOBUG_FLAG(NOBUG_ON)); } } @@ -119,5 +156,26 @@ TEST ("toomany-acquire-release") lumiera_threadpool_destroy(); } +#endif + +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(10); + + 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 + lumiera_threadpool_destroy(); +} TESTS_END diff --git a/tests/backend/test-threads.c b/tests/backend/test-threads.c index 76da2f450..21786ae8b 100644 --- a/tests/backend/test-threads.c +++ b/tests/backend/test-threads.c @@ -91,7 +91,6 @@ TEST ("simple_thread") lumiera_thread_run (LUMIERA_THREADCLASS_WORKER, threadfn, NULL, - NULL, argv[1], NULL); @@ -112,7 +111,6 @@ TEST ("thread_synced") lumiera_thread_run (LUMIERA_THREADCLASS_WORKER, threadsyncfn, &cnd, - &cnd, argv[1], NULL); @@ -143,7 +141,6 @@ TEST ("mutex_thread") lumiera_thread_run (LUMIERA_THREADCLASS_WORKER, mutexfn, NULL, - NULL, argv[1], NULL); diff --git a/tests/test.sh b/tests/test.sh index 951d2fcea..b1fd0d99b 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -26,7 +26,7 @@ # 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\):' +NOBUG_LOGREGEX='^\(\*\*[0-9]*\*\* \)\?[0-9]\{10,\}: \(TRACE\|INFO\|NOTICE\|WARNING\|ERR\|TODO\|PLANNED\|FIXME\|DEPRECATED\|UNIMPLEMENTED\|RESOURCE_ENTER\|RESOURCE_LEAVE\|RESOURCE_STATE\):' arg0="$0" srcdir="$(dirname "$arg0")" From bddb23d111824f584e5d082aab03a8d6b7cd1e6b Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Thu, 24 Dec 2009 01:45:44 +0100 Subject: [PATCH 4/8] remove threadpool_unlink() thread removes itself from the list, this is trival in place --- src/backend/threadpool.c | 8 -------- src/backend/threadpool.h | 6 ------ src/backend/threads.c | 2 +- 3 files changed, 1 insertion(+), 15 deletions(-) diff --git a/src/backend/threadpool.c b/src/backend/threadpool.c index 2a33978d6..d7c13551e 100644 --- a/src/backend/threadpool.c +++ b/src/backend/threadpool.c @@ -96,14 +96,6 @@ lumiera_threadpool_destroy(void) } } -void lumiera_threadpool_unlink(LumieraThread thread) -{ - REQUIRE (thread, "invalid thread given"); - REQUIRE (thread->kind < LUMIERA_THREADCLASS_COUNT, "thread belongs to an unknown pool kind: %d", thread->kind); - llist_unlink(&thread->node); - ENSURE (llist_is_empty(&thread->node), "failed to unlink the thread"); -} - LumieraThread lumiera_threadpool_acquire_thread(enum lumiera_thread_class kind, diff --git a/src/backend/threadpool.h b/src/backend/threadpool.h index de545876c..572727e3e 100644 --- a/src/backend/threadpool.h +++ b/src/backend/threadpool.h @@ -87,12 +87,6 @@ lumiera_threadpool_init(); void lumiera_threadpool_destroy(void); -/** - * Just remove the thread structure from an associated pool list. - */ -void -lumiera_threadpool_unlink(LumieraThread thread); - #endif /* // Local Variables: diff --git a/src/backend/threads.c b/src/backend/threads.c index a75b7f81b..9c4cc9f1f 100644 --- a/src/backend/threads.c +++ b/src/backend/threads.c @@ -160,7 +160,7 @@ lumiera_thread_destroy (LumieraThread self) { REQUIRE (self, "trying to destroy an invalid thread"); - lumiera_threadpool_unlink(self); + llist_unlink (&self->node); // get the pthread out of the processing loop // need to signal to the thread that it should start quitting From 026fab07dc845f0a7f696405fa8286cd456f0125 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Thu, 24 Dec 2009 01:46:43 +0100 Subject: [PATCH 5/8] cosmetics, deadcode removal --- src/backend/threadpool.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/backend/threadpool.c b/src/backend/threadpool.c index d7c13551e..1f2a1cc71 100644 --- a/src/backend/threadpool.c +++ b/src/backend/threadpool.c @@ -42,23 +42,13 @@ NOBUG_DEFINE_FLAG_PARENT (threadpool, threads_dbg); /*TODO insert a suitable/bet //code goes here// -void* pool_thread_loop(void * arg) -{ - (void) arg; - while (1) - { - ; - } - return arg; -} - void lumiera_threadpool_init() { for (int i = 0; i < LUMIERA_THREADCLASS_COUNT; ++i) { llist_init(&threadpool.pool[i].list); - threadpool.pool[i].working_thread_count = 0; + threadpool.pool[i].working_thread_count = 0; threadpool.pool[i].idle_thread_count = 0; //TODO: configure each pools' pthread_attrs appropriately From 6b4415d8fa4bfb86757a5abf16f10a9f3f585372 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Thu, 24 Dec 2009 01:50:59 +0100 Subject: [PATCH 6/8] nobugify declare and init the nobug flags and use them for logging diagnostics --- src/backend/threadpool.c | 16 +++++++++++----- src/backend/threads.c | 10 ++++++++-- src/backend/threads.h | 1 + 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/backend/threadpool.c b/src/backend/threadpool.c index 1f2a1cc71..706f5bb3b 100644 --- a/src/backend/threadpool.c +++ b/src/backend/threadpool.c @@ -45,6 +45,10 @@ NOBUG_DEFINE_FLAG_PARENT (threadpool, threads_dbg); /*TODO insert a suitable/bet void lumiera_threadpool_init() { + NOBUG_INIT_FLAG(threadpool); + NOBUG_INIT_FLAG(threads); + TRACE(threadpool); + for (int i = 0; i < LUMIERA_THREADCLASS_COUNT; ++i) { llist_init(&threadpool.pool[i].list); @@ -65,15 +69,16 @@ lumiera_threadpool_init() void lumiera_threadpool_destroy(void) { - ECHO ("destroying threadpool"); + TRACE(threadpool); + for (int i = 0; i < LUMIERA_THREADCLASS_COUNT; ++i) { - ECHO ("destroying individual pool #%d", i); + TRACE (threadpool, "destroying individual pool #%d", i); LUMIERA_MUTEX_SECTION (threadpool, &threadpool.pool[i].lock) { REQUIRE (0 == threadpool.pool[i].working_thread_count, "%d threads are running", threadpool.pool[i].working_thread_count); // TODO need to have a stronger assertion that no threads are really running because they will not even be in the list - ECHO ("number of threads in the pool=%d", llist_count(&threadpool.pool[i].list)); + INFO (threadpool, "number of threads in the pool=%d", llist_count(&threadpool.pool[i].list)); LLIST_WHILE_HEAD(&threadpool.pool[i].list, t) { lumiera_thread_delete((LumieraThread)t); @@ -92,8 +97,8 @@ lumiera_threadpool_acquire_thread(enum lumiera_thread_class kind, const char* purpose, struct nobug_flag* flag) { - LumieraThread ret; - + TRACE(threadpool); + LumieraThread ret = NULL; REQUIRE (kind < LUMIERA_THREADCLASS_COUNT, "unknown pool kind specified: %d", kind); LUMIERA_RECCONDITION_SECTION (threadpool, &threadpool.pool[kind].signal) { @@ -126,6 +131,7 @@ lumiera_threadpool_acquire_thread(enum lumiera_thread_class kind, void lumiera_threadpool_park_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); diff --git a/src/backend/threads.c b/src/backend/threads.c index 9c4cc9f1f..a34dcde4a 100644 --- a/src/backend/threads.c +++ b/src/backend/threads.c @@ -69,6 +69,7 @@ struct lumiera_thread_mockup static void* thread_loop (void* thread) { + TRACE(threads); LumieraThread t = (LumieraThread)thread; pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, NULL); @@ -81,14 +82,16 @@ static void* thread_loop (void* thread) { do { // NULL function means: no work to do + INFO(threads, "function %p", t->function); if (t->function) t->function (t->arguments); + INFO(threads, "Thread awaken with state %d", t->state); lumiera_threadpool_park_thread(t); LUMIERA_RECCONDITION_WAIT(t->state != LUMIERA_THREADSTATE_IDLE); } while (t->state != LUMIERA_THREADSTATE_SHUTDOWN); // SHUTDOWN state - ECHO ("thread quitting"); + INFO(threads, "Thread Shutdown"); } return 0; } @@ -102,7 +105,8 @@ lumiera_thread_run (enum lumiera_thread_class kind, const char* purpose, struct nobug_flag* flag) { - REQUIRE (function, "invalid function"); + TRACE(threads); + // REQUIRE (function, "invalid function"); // ask the threadpool for a thread (it might create a new one) LumieraThread self = lumiera_threadpool_acquire_thread(kind, purpose, flag); @@ -158,6 +162,7 @@ lumiera_thread_new (enum lumiera_thread_class kind, LumieraThread lumiera_thread_destroy (LumieraThread self) { + TRACE(threads); REQUIRE (self, "trying to destroy an invalid thread"); llist_unlink (&self->node); @@ -186,6 +191,7 @@ lumiera_thread_destroy (LumieraThread self) void lumiera_thread_delete (LumieraThread self) { + TRACE(threads); ECHO ("deleting thread"); lumiera_free (lumiera_thread_destroy (self)); } diff --git a/src/backend/threads.h b/src/backend/threads.h index a55926123..9a4e6bbdf 100644 --- a/src/backend/threads.h +++ b/src/backend/threads.h @@ -34,6 +34,7 @@ //TODO: System includes// #include +NOBUG_DECLARE_FLAG (threads); /** From 88195087d6817cf631beb5dc07b3be9ef400f415 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Thu, 24 Dec 2009 01:54:49 +0100 Subject: [PATCH 7/8] recondition to condition, remove the mutex from the pool rename park to release :P --- src/backend/threadpool.c | 58 ++++++++++++++++++------------------ src/backend/threadpool.h | 8 ++--- src/backend/threads.c | 26 ++++++++-------- src/backend/threads.h | 4 +-- tests/backend/test-threads.c | 22 +++++++------- 5 files changed, 57 insertions(+), 61 deletions(-) diff --git a/src/backend/threadpool.c b/src/backend/threadpool.c index 706f5bb3b..8b86617ff 100644 --- a/src/backend/threadpool.c +++ b/src/backend/threadpool.c @@ -57,12 +57,10 @@ lumiera_threadpool_init() //TODO: configure each pools' pthread_attrs appropriately pthread_attr_init (&threadpool.pool[i].pthread_attrs); - // cehteh prefers that threads are joinable by default - //pthread_attr_setdetachstate (&threadpool.pool[i].pthread_attrs, PTHREAD_CREATE_DETACHED); + // cehteh says that threads must be joinable //cancel... - lumiera_mutex_init(&threadpool.pool[i].lock,"pool of threads", &NOBUG_FLAG(threadpool)); - lumiera_reccondition_init (&threadpool.pool[i].signal, "thread-signal", &NOBUG_FLAG(threadpool)); + lumiera_condition_init (&threadpool.pool[i].sync, "threadpool", &NOBUG_FLAG(cond_sync)); } } @@ -74,7 +72,7 @@ lumiera_threadpool_destroy(void) for (int i = 0; i < LUMIERA_THREADCLASS_COUNT; ++i) { TRACE (threadpool, "destroying individual pool #%d", i); - LUMIERA_MUTEX_SECTION (threadpool, &threadpool.pool[i].lock) + LUMIERA_CONDITION_SECTION (threadpool, &threadpool.pool[i].sync) { REQUIRE (0 == threadpool.pool[i].working_thread_count, "%d threads are running", threadpool.pool[i].working_thread_count); // TODO need to have a stronger assertion that no threads are really running because they will not even be in the list @@ -84,9 +82,8 @@ lumiera_threadpool_destroy(void) lumiera_thread_delete((LumieraThread)t); } } - ECHO ("destroying the pool mutex"); - lumiera_mutex_destroy (&threadpool.pool[i].lock, &NOBUG_FLAG (threadpool)); - ECHO ("pool mutex destroyed"); + + lumiera_condition_destroy (&threadpool.pool[i].sync, &NOBUG_FLAG (cond_sync)); pthread_attr_destroy (&threadpool.pool[i].pthread_attrs); } } @@ -100,16 +97,17 @@ lumiera_threadpool_acquire_thread(enum lumiera_thread_class kind, TRACE(threadpool); LumieraThread ret = NULL; REQUIRE (kind < LUMIERA_THREADCLASS_COUNT, "unknown pool kind specified: %d", kind); - LUMIERA_RECCONDITION_SECTION (threadpool, &threadpool.pool[kind].signal) - { - if (llist_is_empty (&threadpool.pool[kind].list)) - { - ret = lumiera_thread_new (kind, purpose, flag, - &threadpool.pool[kind].pthread_attrs); - threadpool.pool[kind].idle_thread_count++; - ENSURE (ret, "did not create a valid thread"); - LUMIERA_RECCONDITION_WAIT (!llist_is_empty (&threadpool.pool[kind].list)); + LUMIERA_CONDITION_SECTION (threadpool, &threadpool.pool[kind].sync) + { + if (llist_is_empty (&threadpool.pool[kind].list)) + { + + ret = lumiera_thread_new (kind, purpose, flag, + &threadpool.pool[kind].pthread_attrs); + ENSURE (ret, "did not create a valid thread"); + TODO("no error handling but let the resourcecollector do, no need for return the thread"); + LUMIERA_CONDITION_WAIT (!llist_is_empty (&threadpool.pool[kind].list)); } // use an existing thread, pick the first one // remove it from the pool's list @@ -129,7 +127,7 @@ lumiera_threadpool_acquire_thread(enum lumiera_thread_class kind, // TODO: rename to lumiera_threadpool_park_thread void -lumiera_threadpool_park_thread(LumieraThread thread) +lumiera_threadpool_release_thread(LumieraThread thread) { TRACE(threadpool); REQUIRE (thread, "invalid thread given"); @@ -137,20 +135,22 @@ lumiera_threadpool_park_thread(LumieraThread thread) REQUIRE (thread->state != LUMIERA_THREADSTATE_IDLE, "trying to park an already idle thread"); - LUMIERA_RECCONDITION_SECTION (threadpool, &threadpool.pool[thread->kind].signal) + LUMIERA_CONDITION_SECTION (threadpool, &threadpool.pool[thread->kind].sync) { thread->state = LUMIERA_THREADSTATE_IDLE; - REQUIRE (llist_is_single(&thread->node), "thread already belongs to some list"); + REQUIRE (!llist_is_empty(&thread->node), "thread already belongs to some list"); llist_insert_head(&threadpool.pool[thread->kind].list, &thread->node); - threadpool.pool[thread->kind].working_thread_count--; - threadpool.pool[thread->kind].idle_thread_count++; // cheaper than using llist_count - ENSURE (threadpool.pool[thread->kind].idle_thread_count == - llist_count(&threadpool.pool[thread->kind].list), - "idle thread count %d is wrong, should be %d", - threadpool.pool[thread->kind].idle_thread_count, - llist_count(&threadpool.pool[thread->kind].list)); - // REQUIRE (!llist_is_empty (&threadpool.pool[thread->kind].list), "thread pool is still empty after insertion"); - LUMIERA_RECCONDITION_BROADCAST; + + threadpool.pool[thread->kind].working_thread_count--; + threadpool.pool[thread->kind].idle_thread_count++; // cheaper than using llist_count + + ENSURE (threadpool.pool[thread->kind].idle_thread_count == + llist_count(&threadpool.pool[thread->kind].list), + "idle thread count %d is wrong, should be %d", + threadpool.pool[thread->kind].idle_thread_count, + llist_count(&threadpool.pool[thread->kind].list)); + REQUIRE (!llist_is_empty (&threadpool.pool[thread->kind].list), "thread pool is still empty after insertion"); + LUMIERA_CONDITION_BROADCAST; } } diff --git a/src/backend/threadpool.h b/src/backend/threadpool.h index 572727e3e..a8fa457c5 100644 --- a/src/backend/threadpool.h +++ b/src/backend/threadpool.h @@ -23,9 +23,8 @@ #define LUMIERA_THREADPOOL_H //TODO: Support library includes// -#include "lib/reccondition.h" +#include "lib/condition.h" #include "lib/llist.h" -#include "lib/mutex.h" //TODO: Forward declarations// @@ -60,7 +59,7 @@ lumiera_threadpool_acquire_thread(enum lumiera_thread_class kind, * This function doesn't need to be accessible outside of the threadpool implementation. */ void -lumiera_threadpool_park_thread(LumieraThread thread); +lumiera_threadpool_release_thread(LumieraThread thread); typedef struct lumiera_threadpool_struct lumiera_threadpool; typedef lumiera_threadpool* LumieraThreadpool; @@ -70,11 +69,10 @@ struct lumiera_threadpool_struct struct { llist list; - lumiera_mutex lock; unsigned working_thread_count; unsigned idle_thread_count; pthread_attr_t pthread_attrs; - lumiera_reccondition signal; + lumiera_condition sync; } pool[LUMIERA_THREADCLASS_COUNT]; }; diff --git a/src/backend/threads.c b/src/backend/threads.c index a34dcde4a..a7f09b763 100644 --- a/src/backend/threads.c +++ b/src/backend/threads.c @@ -22,7 +22,6 @@ //TODO: Support library includes// #include "include/logging.h" -#include "lib/mutex.h" #include "lib/safeclib.h" @@ -64,7 +63,7 @@ struct lumiera_thread_mockup { void (*fn)(void*); void* arg; - LumieraReccondition finished; + LumieraCondition finished; }; static void* thread_loop (void* thread) @@ -73,21 +72,20 @@ static void* thread_loop (void* thread) LumieraThread t = (LumieraThread)thread; pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, NULL); - + REQUIRE (t, "thread does not exist"); - ECHO ("entering section 1"); // this seems to deadlock unexpectedly: - LUMIERA_RECCONDITION_SECTION (threads, &t->signal) + 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); - lumiera_threadpool_park_thread(t); - LUMIERA_RECCONDITION_WAIT(t->state != LUMIERA_THREADSTATE_IDLE); } while (t->state != LUMIERA_THREADSTATE_SHUTDOWN); // SHUTDOWN state @@ -117,9 +115,9 @@ lumiera_thread_run (enum lumiera_thread_class kind, // and let it really run (signal the condition var, the thread waits on it) self->state = LUMIERA_THREADSTATE_WAKEUP; - ECHO ("entering section 2"); - LUMIERA_RECCONDITION_SECTION(threads, &self->signal) - LUMIERA_RECCONDITION_BROADCAST; + + LUMIERA_CONDITION_SECTION(threads, &self->signal) + LUMIERA_CONDITION_SIGNAL; // NOTE: example only, add solid error handling! @@ -142,7 +140,7 @@ lumiera_thread_new (enum lumiera_thread_class kind, LumieraThread self = lumiera_malloc (sizeof (*self)); llist_init(&self->node); - lumiera_reccondition_init (&self->signal, "thread-control", flag); + lumiera_condition_init (&self->signal, "thread-control", flag); self->kind = kind; self->state = LUMIERA_THREADSTATE_STARTUP; self->function = NULL; @@ -170,20 +168,20 @@ lumiera_thread_destroy (LumieraThread self) // 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_RECCONDITION_SECTION(threads, &self->signal) + 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_RECCONDITION_SIGNAL; + 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_reccondition_destroy (&self->signal, &NOBUG_FLAG(threads)); + lumiera_condition_destroy (&self->signal, &NOBUG_FLAG(threads)); return self; } diff --git a/src/backend/threads.h b/src/backend/threads.h index 9a4e6bbdf..d4282da17 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// @@ -132,7 +132,7 @@ struct lumiera_thread_struct // 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_reccondition signal; // control signal, state change signal + 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" diff --git a/tests/backend/test-threads.c b/tests/backend/test-threads.c index 21786ae8b..ddf43214e 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); @@ -101,10 +101,10 @@ 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); @@ -115,17 +115,17 @@ TEST ("thread_synced") 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)); } From a698128cfc75aba631f0dec8da1d45bfc63b0464 Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Thu, 24 Dec 2009 01:55:24 +0100 Subject: [PATCH 8/8] new no-function test, sleep a bit before destroying the threadpool --- tests/backend/test-threadpool.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/backend/test-threadpool.c b/tests/backend/test-threadpool.c index ea5d60a76..b83920330 100644 --- a/tests/backend/test-threadpool.c +++ b/tests/backend/test-threadpool.c @@ -26,6 +26,7 @@ #include #include #include +#include void is_prime(void * arg) { @@ -158,6 +159,25 @@ TEST ("toomany-acquire-release") } #endif +TEST ("no-function") +{ + LumieraThread t; + + lumiera_threadpool_init(10); + + 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 @@ -175,6 +195,9 @@ TEST ("process-function") &NOBUG_FLAG(NOBUG_ON)); // struct nobug_flag* flag) // cleanup + ECHO("wait 1 sec"); + usleep(1000000); + ECHO("finished waiting"); lumiera_threadpool_destroy(); }