summaryrefslogtreecommitdiffstats
path: root/threadlib
diff options
context:
space:
mode:
authorSam Varshavchik2013-08-19 16:39:41 -0400
committerSam Varshavchik2013-08-25 14:43:51 -0400
commit9c45d9ad13fdf439d44d7443ae75da15ea0223ed (patch)
tree7a81a04cb51efb078ee350859a64be2ebc6b8813 /threadlib
parenta9520698b770168d1f33d6301463bb70a19655ec (diff)
downloadcourier-libs-9c45d9ad13fdf439d44d7443ae75da15ea0223ed.tar.bz2
Initial checkin
Imported from subversion report, converted to git. Updated all paths in scripts and makefiles, reflecting the new directory hierarchy.
Diffstat (limited to 'threadlib')
-rw-r--r--threadlib/.gitignore2
-rw-r--r--threadlib/Makefile.am33
-rw-r--r--threadlib/configure.in84
-rw-r--r--threadlib/havepthread.h126
-rw-r--r--threadlib/nopthread.c48
-rw-r--r--threadlib/nopthread.h43
-rw-r--r--threadlib/pthread.c431
-rw-r--r--threadlib/test.c94
-rw-r--r--threadlib/testsuite4
-rw-r--r--threadlib/testsuite.txt40
-rw-r--r--threadlib/threadlib.h21
11 files changed, 926 insertions, 0 deletions
diff --git a/threadlib/.gitignore b/threadlib/.gitignore
new file mode 100644
index 0000000..197552a
--- /dev/null
+++ b/threadlib/.gitignore
@@ -0,0 +1,2 @@
+/libthread.dep
+/threadtest
diff --git a/threadlib/Makefile.am b/threadlib/Makefile.am
new file mode 100644
index 0000000..1ff4301
--- /dev/null
+++ b/threadlib/Makefile.am
@@ -0,0 +1,33 @@
+#
+# Copyright 2000-2002 Double Precision, Inc.
+# See COPYING for distribution information.
+#
+
+
+noinst_LIBRARIES=libthreadlib.a
+noinst_DATA=libthread.dep
+noinst_PROGRAMS=threadtest
+
+CLEANFILES=libthread.dep
+
+if HAVE_PTHREADS
+threadc=pthread.c
+else
+threadc=nopthread.c
+endif
+
+libthreadlib_a_SOURCES=$(threadc) threadlib.h havepthread.h nopthread.h
+EXTRA_DIST=pthread.c nopthread.c testsuite testsuite.txt
+
+libthread.dep: config.status
+ echo @THREADLIB@ >libthread.dep
+
+threadtest_SOURCES=test.c
+threadtest_DEPENDENCIES=libthreadlib.a libthread.dep
+threadtest_LDADD=libthreadlib.a `cat libthread.dep`
+
+check-am: threadtest
+ @echo '------------- Testing threadlib ---------------'
+ @sh $(srcdir)/testsuite | tee t
+ @cmp -s t $(srcdir)/testsuite.txt
+ @rm -f t
diff --git a/threadlib/configure.in b/threadlib/configure.in
new file mode 100644
index 0000000..6962099
--- /dev/null
+++ b/threadlib/configure.in
@@ -0,0 +1,84 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_INIT(threadlib, 0.10, [courier-users@lists.sourceforge.net])
+
+>confdefs.h # Kill PACKAGE_ macros
+
+AC_CONFIG_SRCDIR(pthread.c)
+AC_CONFIG_AUX_DIR(../..)
+AM_INIT_AUTOMAKE([foreign no-define])
+
+AM_CONFIG_HEADER(config.h)
+
+dnl Checks for programs.
+AC_USE_SYSTEM_EXTENSIONS
+AC_PROG_AWK
+AC_PROG_INSTALL
+AC_PROG_LN_S
+AC_PROG_RANLIB
+AC_PROG_CC
+
+dnl Checks for libraries.
+
+dnl Checks for header files.
+AC_HEADER_STDC
+AC_CHECK_HEADERS(unistd.h pthread.h)
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+
+dnl Checks for library functions.
+
+THREADLIB=""
+save_LIBS="$LIBS"
+
+AC_CHECK_LIB(pthread, pthread_cond_wait, [
+ THREADLIB="-lpthread" ; LIBS="-lpthread $LIBS" ], [
+
+LIBS="-pthread $save_LIBS"
+AC_TRY_LINK([
+void pthread_cond_wait();
+],[
+ pthread_cond_wait();
+],
+ THREADLIB="-pthread"
+)
+]
+)
+
+LIBS="$THREADLIB $save_LIBS"
+
+have_pthreads=no
+
+AC_CHECK_HEADER(pthread.h, [
+ AC_CHECK_FUNC(pthread_cond_wait, have_pthreads=yes)
+]
+)
+
+LIBS="$save_LIBS"
+
+AC_ARG_WITH(pthreads, [--without-pthreads - do not use Posix threads ],
+ if test "$withval" = "no"
+ then
+ have_pthreads=no
+ fi
+ )
+
+if test "$have_pthreads" = "no"
+then
+ THREADLIB=""
+else
+ AC_DEFINE_UNQUOTED(HAVE_PTHREADS,1,
+ [ Whether pthreads are available ])
+fi
+
+AM_CONDITIONAL(HAVE_PTHREADS, test "$have_pthreads" != "no")
+
+AC_SUBST(THREADLIB)
+
+AC_CHECK_LIB(m, sqrt)
+
+if test "$GCC" = "yes"
+then
+ CFLAGS="-Wall $CFLAGS"
+fi
+AC_OUTPUT(Makefile)
diff --git a/threadlib/havepthread.h b/threadlib/havepthread.h
new file mode 100644
index 0000000..74e7baa
--- /dev/null
+++ b/threadlib/havepthread.h
@@ -0,0 +1,126 @@
+#ifndef havepthread_h
+#define havepthread_h
+
+/*
+** Copyright 2000 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/*
+
+This library encapsulates Posix threads in such a fashion as to be
+able to transparently emulate the encapsulation if Posix threads are
+not available.
+
+That is, the caller simply implements the documented interface, and if Posix
+threads are not available, this library will provide stub functions that will
+implement a functional equivalent (although probably a far less efficient
+one).
+
+The interface implement by this library is as follows:
+
+* A function is defined which will be executed by multiple threads in parallel.
+
+* When this function returns, another 'cleanup' function will be called, except
+ that this cleanup function will be single-threaded.
+
+ In fact, the cleanup function may be executed by any thread. All this is
+ is when the main function, the work function, completes, a mutex is locked
+ for the duration of the cleanup function call.
+
+* The work function, and the cleanup function, can make use of some amount of
+ metadata that can be initialized before starting the threaded function.
+
+The general concept is that you have some section of code that can benefit
+from being threaded. So, it is threaded, and the execution resumes in
+single-step function once the threaded section of the code completes.
+
+*/
+
+struct cthreadinfo *cthread_init(
+ unsigned, /* Number of threads */
+ unsigned, /* Size of per-thread metadata */
+ void (*)(void *), /* The work function */
+ void (*)(void *)); /* The cleanup function */
+
+/*
+
+cthread_init is used to initialize and start all threads. cthread_init returns
+NULL if there was an error in setting up threading. The first argument
+specifies the number of threads to start. The second argument specifies how
+many bytes are there in per-thread metadata. cthread_init will automatically
+allocate nthreads*metasize bytes internally. The third argument is a pointer
+to the threaded function. The fourth argument is a pointer to the cleanup
+function.
+
+cthread_init returns a handle pointer if threading has been succesfully
+set up.
+
+*/
+
+void cthread_wait(struct cthreadinfo *);
+
+/*
+
+cthread_wait pauses until all current active threads have finished. If there
+are any threads which are currently busy executed the work function or the
+cleanup function, cthread_wait pauses until they're done. Then, cthread_wait
+kills all the threads, and deallocates all allocated resources for the
+handle
+
+*/
+
+int cthread_go( struct cthreadinfo *, /* Handle */
+
+ void (*)(void *, void *), /* Initialization function */
+ void *); /* Second arg to the initialization func */
+
+/*
+
+cthread_go finds an unused thread, and has it execute the work function, then
+the cleanup function.
+
+Both the work function and the cleanup function receive a pointer to the
+thread-specific metadata. The second argument to cthread_go points to a
+function that will be used to initialize the thread-specific metadata before
+running the work function. The first argument to this initialization function
+will be a pointer to the thread-specific metadata, which is stored internally
+by this library (associated with the handle). When the initialization
+function returns, the work/cleanup functions are called with the same pointer.
+The third argument to cthread_go is passed as the second argument to the
+initialization function.
+
+If there are no available threads, cthread_go pauses until one becomes
+available. cthread_go returns immediately after starting the thread, so
+upon return from cthread_go the work and cleanup functions are NOT guaranteed
+to have been called already. cthread_go merely starts the thread, which will
+execute the work and the cleanup functions concurrently.
+
+When Posix threads are not available, cthread_go is a stub. It calls the
+initialization function, then the work function, then the cleanup function,
+and then returns to the caller. Hopefully, these semantics will be sufficient
+to carry out the necessary deed if threading is not available.
+*/
+
+/*
+
+cthreadlock structure is used to implement locks. Create a lock by calling
+cthread_lockcreate. Destroy a lock by calling cthread_lockdelete.
+
+Calling cthread_lock obtains the lock specified by the first argument, calls
+the function specified by the second argument. When the function returns,
+the lock is released. The third argument is passed as the argument to the
+function.
+
+*/
+
+struct cthreadlock *cthread_lockcreate(void);
+void cthread_lockdestroy(struct cthreadlock *);
+int cthread_lock(struct cthreadlock *, int (*)(void *), void *);
+
+#endif
diff --git a/threadlib/nopthread.c b/threadlib/nopthread.c
new file mode 100644
index 0000000..59cea13
--- /dev/null
+++ b/threadlib/nopthread.c
@@ -0,0 +1,48 @@
+/*
+** Copyright 2000-2001 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+
+#include "threadlib.h"
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+typedef struct cthreadinfo cthreadinfo_t;
+
+
+cthreadinfo_t *cthread_init(unsigned nthreads_, unsigned metasize,
+ void (*workfunc_)(void *),
+ void (*cleanupfunc_)(void *))
+{
+cthreadinfo_t *cit;
+
+ if ((cit=(cthreadinfo_t *)malloc(sizeof(cthreadinfo_t))) == 0)
+ return (0);
+
+ cit->cleanupfunc=cleanupfunc_;
+ cit->workfunc=workfunc_;
+
+ if ( (cit->metadata_buf=malloc(metasize)) == 0)
+ {
+ free( (char *)cit );
+ return (0);
+ }
+ return (cit);
+}
+
+void cthread_wait(cthreadinfo_t *cit)
+{
+ free(cit->metadata_buf);
+ free( (char *)cit);
+}
+
+
+struct cthreadlock {
+ int dummy;
+ } ;
+
+struct cthreadlock cthread_dummy;
diff --git a/threadlib/nopthread.h b/threadlib/nopthread.h
new file mode 100644
index 0000000..e3c8680
--- /dev/null
+++ b/threadlib/nopthread.h
@@ -0,0 +1,43 @@
+#ifndef nopthread_h
+#define nopthread_h
+
+/*
+** Copyright 2000 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+struct cthreadinfo {
+ void (*workfunc)(void *);
+ void (*cleanupfunc)(void *);
+ char *metadata_buf;
+ } ;
+
+struct cthreadinfo *cthread_init(
+ unsigned, /* Number of threads */
+ unsigned, /* Size of per-thread metadata */
+ void (*)(void *), /* The work function */
+ void (*)(void *)); /* The cleanup function */
+
+void cthread_wait(struct cthreadinfo *);
+
+#define cthread_go(cit, gofunc, arg) \
+ ( (*gofunc)(cit->metadata_buf, arg), \
+ (*cit->workfunc)(cit->metadata_buf), \
+ (*cit->cleanupfunc)(cit->metadata_buf), 0)
+
+struct cthreadlock *cthread_lockcreate(void);
+void cthread_lockdestroy(struct cthreadlock *);
+int cthread_lock(struct cthreadlock *, int (*)(void *), void *);
+
+extern struct cthreadlock cthread_dummy;
+
+#define cthread_lockcreate() ( &cthread_dummy )
+#define cthread_lockdestroy(p)
+#define cthread_lock(p,func,arg) ( (*func)(arg))
+
+#endif
diff --git a/threadlib/pthread.c b/threadlib/pthread.c
new file mode 100644
index 0000000..c0d414c
--- /dev/null
+++ b/threadlib/pthread.c
@@ -0,0 +1,431 @@
+/*
+** Copyright 2000-2007 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+
+#include "threadlib.h"
+
+#include <pthread.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+typedef struct {
+ unsigned me;
+ pthread_t pt;
+
+ pthread_cond_t gocond;
+ pthread_mutex_t gomutex;
+ int goflag;
+
+ struct cthreadinfo *myinfo;
+
+ void *metadata;
+ } cthread_t;
+
+struct cthreadinfo {
+ unsigned nthreads;
+ void (*workfunc)(void *);
+ void (*cleanupfunc)(void *);
+ cthread_t *threads;
+ char *metadata_buf;
+ int *newbuf;
+ int newcnt;
+
+ pthread_cond_t newtask_cond;
+ pthread_mutex_t newtask_mutex;
+
+ pthread_mutex_t cleanup_mutex;
+ } ;
+
+typedef struct cthreadinfo cthreadinfo_t;
+
+static void mutexcleanup(void *vp)
+{
+pthread_mutex_t *mp=(pthread_mutex_t *)vp;
+
+ pthread_mutex_unlock( mp );
+}
+
+#define PTHREAD_CHK(x) (errno=(x))
+
+/*
+** This is the thread function wrapper. It waits for its conditional
+** signal, runs the workhorse function, then puts its task id onto the ready
+** queue, then goes back to waiting for another go signal.
+*/
+
+static void *threadfunc(void *p)
+{
+cthread_t *c= (cthread_t *)p;
+cthreadinfo_t *i= c->myinfo;
+
+ for (;;)
+ {
+ while (PTHREAD_CHK(pthread_mutex_lock(&c->gomutex)))
+ {
+ perror("pthread_mutex_lock");
+ sleep(3);
+ }
+
+ pthread_cleanup_push(&mutexcleanup, (void *)&c->gomutex);
+
+ while (!c->goflag)
+ {
+ if (PTHREAD_CHK(pthread_cond_wait(&c->gocond,
+ &c->gomutex)))
+ {
+ perror("pthread_cond_wait");
+ sleep(3);
+ }
+ }
+ c->goflag=0;
+ if (PTHREAD_CHK(pthread_mutex_unlock( &c->gomutex )))
+ perror("pthread_mutex_unlock");
+
+ pthread_cleanup_pop(0);
+
+ (*i->workfunc)(c->metadata);
+
+ while (PTHREAD_CHK(pthread_mutex_lock(&i->cleanup_mutex)))
+ {
+ perror("pthread_mutex_lock");
+ sleep(3);
+ }
+
+ pthread_cleanup_push(&mutexcleanup, (void *)&i->cleanup_mutex);
+
+ (*i->cleanupfunc)(c->metadata);
+
+ if (PTHREAD_CHK(pthread_mutex_unlock( &i->cleanup_mutex )))
+ perror("pthread_mutex_unlock");
+
+ pthread_cleanup_pop(0);
+
+ while (PTHREAD_CHK(pthread_mutex_lock(&i->newtask_mutex)))
+ {
+ perror("pthread_mutex_lock");
+ sleep(3);
+ }
+
+ i->newbuf[i->newcnt++] = c->me;
+
+ if (PTHREAD_CHK(pthread_mutex_unlock( &i->newtask_mutex )))
+ perror("pthread_mutex_unlock");
+
+ if (PTHREAD_CHK(pthread_cond_signal( &i->newtask_cond )))
+ perror("pthread_cond_signal");
+ }
+}
+
+static int initcondmutex(pthread_cond_t *c, pthread_mutex_t *m)
+{
+pthread_condattr_t cattr;
+pthread_mutexattr_t mattr;
+
+ if (c)
+ {
+ if ( PTHREAD_CHK(pthread_condattr_init(&cattr))) return (-1);
+ if ( PTHREAD_CHK(pthread_cond_init(c, &cattr)))
+ {
+ pthread_condattr_destroy(&cattr);
+ return (-1);
+ }
+ pthread_condattr_destroy(&cattr);
+ }
+
+ if ( PTHREAD_CHK(pthread_mutexattr_init(&mattr)))
+ {
+ if (c) pthread_cond_destroy(c);
+ return (-1);
+ }
+
+ if ( PTHREAD_CHK(pthread_mutex_init(m, &mattr)))
+ {
+ pthread_mutexattr_destroy(&mattr);
+
+ if (c) pthread_cond_destroy(c);
+ return (-1);
+ }
+ pthread_mutexattr_destroy(&mattr);
+ return (0);
+}
+
+cthreadinfo_t *cthread_init(unsigned nthreads_, unsigned metasize,
+ void (*workfunc_)(void *),
+ void (*cleanupfunc_)(void *))
+{
+unsigned i;
+pthread_attr_t pat;
+cthreadinfo_t *cit;
+
+ if ((cit=(cthreadinfo_t *)malloc(sizeof(cthreadinfo_t))) == 0)
+ return (0);
+
+ cit->nthreads=nthreads_;
+ cit->cleanupfunc=cleanupfunc_;
+ cit->workfunc=workfunc_;
+ if ( (cit->threads=(cthread_t *)malloc(cit->nthreads * sizeof(cthread_t))) == 0)
+ {
+ free((char *)cit);
+ return (0);
+ }
+ if ( (cit->metadata_buf=malloc(metasize * cit->nthreads)) == 0)
+ {
+ free( (char *)cit->threads);
+ free( (char *)cit );
+ return (0);
+ }
+
+ cit->newcnt=cit->nthreads;
+ if ( (cit->newbuf=(int *)malloc(cit->nthreads * sizeof(int))) == 0)
+ {
+ free(cit->metadata_buf);
+ free( (char *)cit->threads);
+ free( (char *)cit );
+ return (0);
+ }
+ for (i=0; i<cit->nthreads; i++)
+ {
+ cit->newbuf[i]=i;
+ }
+
+ if (initcondmutex(&cit->newtask_cond, &cit->newtask_mutex))
+ {
+ free(cit->newbuf);
+ free(cit->metadata_buf);
+ free( (char *)cit->threads);
+ free( (char *)cit );
+ return (0);
+ }
+
+ if (initcondmutex(0, &cit->cleanup_mutex))
+ {
+ pthread_cond_destroy(&cit->newtask_cond);
+ pthread_mutex_destroy(&cit->newtask_mutex);
+
+ free(cit->newbuf);
+ free(cit->metadata_buf);
+ free( (char *)cit->threads);
+ free( (char *)cit );
+ return (0);
+ }
+
+ if (PTHREAD_CHK(pthread_attr_init(&pat)))
+ {
+ pthread_mutex_destroy(&cit->cleanup_mutex);
+ pthread_cond_destroy(&cit->newtask_cond);
+ pthread_mutex_destroy(&cit->newtask_mutex);
+ free(cit->newbuf);
+ free(cit->metadata_buf);
+ free( (char *)cit->threads);
+ free( (char *)cit );
+ return (0);
+ }
+
+ for (i=0; i<cit->nthreads; i++)
+ {
+ cit->threads[i].me=i;
+ cit->threads[i].metadata=(void *) (cit->metadata_buf+i*metasize);
+ cit->threads[i].myinfo=cit;
+
+ if (initcondmutex(&cit->threads[i].gocond,
+ &cit->threads[i].gomutex))
+ break;
+
+ cit->threads[i].goflag=0;
+
+ if (PTHREAD_CHK(pthread_create(&cit->threads[i].pt, &pat,
+ &threadfunc,
+ (void *)&cit->threads[i])))
+ {
+ pthread_cond_destroy(&cit->threads[i].gocond);
+ pthread_mutex_destroy(&cit->threads[i].gomutex);
+ break;
+ }
+ }
+
+ if ( i >= cit->nthreads)
+ {
+ pthread_attr_destroy(&pat);
+ return (cit);
+ }
+
+ while (i)
+ {
+ --i;
+ if (PTHREAD_CHK(pthread_cancel(cit->threads[i].pt)))
+ perror("pthread_cancel");
+
+ if (PTHREAD_CHK(pthread_join(cit->threads[i].pt, NULL)))
+ perror("pthread_join");
+
+ pthread_cond_destroy(&cit->threads[i].gocond);
+ pthread_mutex_destroy(&cit->threads[i].gomutex);
+ }
+
+ pthread_attr_destroy(&pat);
+ pthread_mutex_destroy(&cit->cleanup_mutex);
+ pthread_cond_destroy(&cit->newtask_cond);
+ pthread_mutex_destroy(&cit->newtask_mutex);
+ free(cit->newbuf);
+ free(cit->metadata_buf);
+ free( (char *)cit->threads);
+ free( (char *)cit );
+ return (0);
+}
+
+void cthread_wait(cthreadinfo_t *cit)
+{
+ unsigned i;
+
+ if (PTHREAD_CHK(pthread_mutex_lock(&cit->newtask_mutex)))
+ {
+ perror("pthread_mutex_lock");
+ return;
+ }
+
+ pthread_cleanup_push(&mutexcleanup, (void *)&cit->newtask_mutex);
+
+ while (cit->newcnt < cit->nthreads)
+ {
+ if (PTHREAD_CHK(pthread_cond_wait(&cit->newtask_cond,
+ &cit->newtask_mutex)))
+ {
+ perror("pthread_cond_wait");
+ sleep(3);
+ }
+ }
+
+ if (PTHREAD_CHK(pthread_mutex_unlock(&cit->newtask_mutex)))
+ perror("pthread_mutex_unlock");
+
+ pthread_cleanup_pop(0);
+
+ for (i=0; i<cit->nthreads; i++)
+ {
+ if (PTHREAD_CHK(pthread_cancel(cit->threads[i].pt) ))
+ perror("pthread_cancel");
+
+ if (PTHREAD_CHK(pthread_join(cit->threads[i].pt, NULL)))
+ perror("pthread_join");
+
+ if (PTHREAD_CHK(pthread_cond_destroy(&cit->threads[i].gocond)))
+ perror("pthread_cond_destroy(gocond)");
+
+ if (PTHREAD_CHK(pthread_mutex_destroy(&cit->threads[i]
+ .gomutex)))
+ perror("pthread_mutex_destroy");
+ }
+
+ if (PTHREAD_CHK(pthread_mutex_destroy(&cit->cleanup_mutex)))
+ perror("pthread_mutex_destroy");
+
+ if (PTHREAD_CHK(pthread_cond_destroy(&cit->newtask_cond)))
+ perror("pthread_cond_destroy(newtask_cond)");
+
+ if (PTHREAD_CHK(pthread_mutex_destroy(&cit->newtask_mutex)))
+ perror("pthread_mutex_destroy");
+ free(cit->newbuf);
+ free(cit->metadata_buf);
+ free( (char *)cit->threads);
+ free( (char *)cit);
+}
+
+int cthread_go( cthreadinfo_t *cit, void (*gofunc)(void *, void *), void *arg)
+{
+ int n=0;
+ int err=0;
+
+ if (PTHREAD_CHK(pthread_mutex_lock(&cit->newtask_mutex)))
+ return (-1);
+
+ pthread_cleanup_push(&mutexcleanup, (void *)&cit->newtask_mutex);
+
+ while (cit->newcnt == 0)
+ {
+ if (PTHREAD_CHK(pthread_cond_wait(&cit->newtask_cond,
+ &cit->newtask_mutex)))
+ {
+ err=1;
+ break;
+ }
+ }
+
+ if (!err)
+ n=cit->newbuf[ --cit->newcnt ];
+
+ if (PTHREAD_CHK(pthread_mutex_unlock(&cit->newtask_mutex)))
+ err=1;
+
+ pthread_cleanup_pop(0);
+ if (err) return (-1);
+
+ (*gofunc)( cit->threads[n].metadata, arg);
+
+ if (PTHREAD_CHK(pthread_mutex_lock(&cit->threads[n].gomutex)))
+ return (-1);
+ cit->threads[n].goflag=1;
+ if (PTHREAD_CHK(pthread_mutex_unlock(&cit->threads[n].gomutex)))
+ return (-1);
+ if (PTHREAD_CHK(pthread_cond_signal( &cit->threads[n].gocond )))
+ return (-1);
+ return (0);
+}
+
+struct cthreadlock {
+ pthread_mutex_t mutex;
+ } ;
+
+struct cthreadlock *cthread_lockcreate(void)
+{
+struct cthreadlock *p= (struct cthreadlock *)malloc(sizeof(struct cthreadlock));
+pthread_mutexattr_t mattr;
+
+ if (!p) return (0);
+
+ if (PTHREAD_CHK(pthread_mutexattr_init(&mattr)))
+ {
+ errno=EIO;
+ free( (char *)p );
+ return (0);
+ }
+
+ if (PTHREAD_CHK(pthread_mutex_init(&p->mutex, &mattr)))
+ {
+ pthread_mutexattr_destroy(&mattr);
+ errno=EIO;
+ free( (char *)p );
+ return (0);
+ }
+ pthread_mutexattr_destroy(&mattr);
+ return (p);
+}
+
+void cthread_lockdestroy(struct cthreadlock *p)
+{
+ pthread_mutex_destroy(&p->mutex);
+ free( (char *)p );
+}
+
+int cthread_lock(struct cthreadlock *p, int (*func)(void *), void *arg)
+{
+int rc;
+
+ while (pthread_mutex_lock(&p->mutex))
+ {
+ perror("pthread_mutex_lock");
+ sleep(3);
+ }
+
+ pthread_cleanup_push(&mutexcleanup, (void *)&p->mutex);
+
+ rc= (*func)(arg);
+
+ if (PTHREAD_CHK(pthread_mutex_unlock( &p->mutex )))
+ perror("pthread_mutex_unlock");
+ pthread_cleanup_pop(0);
+ return (rc);
+}
diff --git a/threadlib/test.c b/threadlib/test.c
new file mode 100644
index 0000000..9857832
--- /dev/null
+++ b/threadlib/test.c
@@ -0,0 +1,94 @@
+/*
+** Copyright 2000 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <errno.h>
+#include <time.h>
+#include "threadlib.h"
+
+static double buf[100];
+static int bufcnt=0;
+static unsigned ndelay=0;
+
+static void workfunc(void *p)
+{
+double *dp= (double *)p;
+time_t t1, t2;
+
+ *dp=sqrt( *dp );
+
+ time(&t1);
+ while ( time(&t2) < t1 + ndelay)
+ ;
+}
+
+static void cleanupfunc(void *p)
+{
+double *dp= (double *)p;
+
+ buf[bufcnt++]= *dp;
+}
+
+static void getwork(void *p, void *q)
+{
+double *dp= (double *)p;
+double *qp= (double *)q;
+
+ *dp= *qp;
+}
+
+static int cmpdbl (const void *a, const void *b)
+{
+const double *pa=(const double *)a;
+const double *pb=(const double *)b;
+
+ return (*pa < *pb ? -1: *pa > *pb ? 1:0);
+}
+
+int main(int argc, char **argv)
+{
+double n;
+struct cthreadinfo *cit;
+int i, j;
+
+ if (argc < 2) return (1);
+
+ j=atoi(argv[1]);
+ if (argc > 2)
+ ndelay=atoi(argv[2]);
+
+ if (j <= 0 || j > sizeof(buf)/sizeof(buf[0])) return (1);
+
+ if ( (cit=cthread_init(2, sizeof(double), workfunc, cleanupfunc)) == 0)
+ {
+ perror("cthread_init");
+ return (1);
+ }
+
+ for (i=0; i<j; i++)
+ {
+ n= i+1;
+
+ if (cthread_go(cit, getwork, &n))
+ {
+ perror("cthread_go");
+ return (1);
+ }
+ printf ("Started %d\n", i+1);
+ }
+ cthread_wait(cit);
+
+ qsort(buf, bufcnt, sizeof(buf[0]), &cmpdbl);
+
+ for (i=0; i<bufcnt; i++)
+ {
+ printf("%6.2f\n", buf[i]);
+ }
+ return (0);
+}
diff --git a/threadlib/testsuite b/threadlib/testsuite
new file mode 100644
index 0000000..fd9a413
--- /dev/null
+++ b/threadlib/testsuite
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+./threadtest 16 2>&1
+./threadtest 4 2 2>&1
diff --git a/threadlib/testsuite.txt b/threadlib/testsuite.txt
new file mode 100644
index 0000000..59ea021
--- /dev/null
+++ b/threadlib/testsuite.txt
@@ -0,0 +1,40 @@
+Started 1
+Started 2
+Started 3
+Started 4
+Started 5
+Started 6
+Started 7
+Started 8
+Started 9
+Started 10
+Started 11
+Started 12
+Started 13
+Started 14
+Started 15
+Started 16
+ 1.00
+ 1.41
+ 1.73
+ 2.00
+ 2.24
+ 2.45
+ 2.65
+ 2.83
+ 3.00
+ 3.16
+ 3.32
+ 3.46
+ 3.61
+ 3.74
+ 3.87
+ 4.00
+Started 1
+Started 2
+Started 3
+Started 4
+ 1.00
+ 1.41
+ 1.73
+ 2.00
diff --git a/threadlib/threadlib.h b/threadlib/threadlib.h
new file mode 100644
index 0000000..697ba18
--- /dev/null
+++ b/threadlib/threadlib.h
@@ -0,0 +1,21 @@
+#ifndef threadlib_h
+#define threadlib_h
+
+/*
+** Copyright 2000 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+
+#include "config.h"
+
+struct cthreadinfo;
+struct cthreadlock;
+
+#if HAVE_PTHREADS
+#include "havepthread.h"
+#else
+#include "nopthread.h"
+#endif
+
+#endif