Updated 2012-09-14 00:48:56 by RLE

A thread pool manager using the Tcl library thread interface. This is likely duplicating some of the function of that supports the thread pool script interface, but it wasn't clear to me how to access that from C.

tpool.h:
 typedef void (*TPoolWork)(void *data);

 typedef struct _TPoolThread {
    struct _TPool *tp;
    Tcl_ThreadId   id;
    TPoolWork    func;

    Tcl_Mutex     lock;
    Tcl_Condition wait;

    void        *data;
    int          work;
 } TPoolThread;

 typedef struct _TPool {
    Tcl_Mutex     lock;
    Tcl_Condition wait;

    int         next;
    int         nthread;
    TPoolThread *thread;
 } TPool;

 TPool *TPoolInit(int n);
 TPoolThread *TPoolThreadStart(TPool *tp, TPoolWork func, void *data);
 TPoolThread *TPoolThreadWair (TPoolThread *t);

tpool.c:
 /* Simple thread pool manager

   John Roll 2012
 */

 #include <stdlib.h>

 #define TCL_THREADS 1

 #include "tcl.h"
 #include "tpool.h"

 void TPoolWorker(void *data) {
    TPoolThread *t = (TPoolThread *) data;
    TPool       *tp = t->tp;
    int i_have_work;

    Tcl_MutexLock(&t->lock);

    while ( 1 ) {
        t->work = 0;
        Tcl_ConditionNotify(&t->wait);
        Tcl_ConditionWait(&t->wait, &t->lock, NULL /* no timeout */);

        if ( t->work ) {
            Tcl_MutexUnlock(&t->lock);

                t->func(t->data);

            Tcl_MutexLock(&t->lock);

            t->work = 0;

            Tcl_ConditionNotify(&t->wait);

            Tcl_MutexLock(&tp->lock);
            Tcl_ConditionNotify(&tp->wait);
            Tcl_MutexUnlock(&tp->lock);
        }
    }
 }

 TPool *TPoolInit(int n) {
    int    i;
    TPool *tp  = calloc(sizeof(TPool), 1);
    tp->thread = calloc(sizeof(TPoolThread), n);
    tp->nthread = n;

    for ( i = 0; i < n; i++ ) {
        tp->thread[i].work = 1;
        tp->thread[i].tp   = tp;
        Tcl_CreateThread(&tp->thread[i].id, TPoolWorker, &tp->thread[i], TCL_THREAD_STACK_DEFAULT, TCL_THREAD_NOFL
    }
    for ( i = 0; i < n; i++ ) {
        TPoolThread *t = &tp->thread[i];

        Tcl_MutexLock(&t->lock);
        if ( t->work ) {
            Tcl_ConditionWait(&t->wait, &t->lock, NULL /* no timeout */);
        }
        Tcl_MutexUnlock(&t->lock);
    }

    return tp;
 }

 TPoolThread *TPoolThreadStart(TPool *tp, TPoolWork func, void *data) {
    int start = tp->next;

    Tcl_MutexLock(&tp->lock);

    while ( tp->thread[tp->next].work ) {       // Find a thread that will work for us.
        TPoolThread *t;
        tp->next = ++tp->next % tp->nthread;

        if ( tp->next == start ) {
            Tcl_ConditionWait(&tp->wait, &tp->lock, NULL /* no timeout */);
        }
    }
    Tcl_MutexUnlock(&tp->lock);

    {
        TPoolThread *t = &tp->thread[tp->next];

        Tcl_MutexLock(&t->lock);

        t->func = func;
        t->data = data;
        t->work = 1;

        Tcl_ConditionNotify(&t->wait);

        Tcl_MutexUnlock(&t->lock);

        return t;
    }
 }

 TPoolThreadWait(TPoolThread *t) {
    Tcl_MutexLock(&t->lock);

    while ( t->work ) {
        Tcl_ConditionWait(&t->wait, &t->lock, NULL /* no timeout */);
    }

    Tcl_MutexUnlock(&t->lock);
 }