diff options
Diffstat (limited to 'threadlib')
| -rw-r--r-- | threadlib/.gitignore | 2 | ||||
| -rw-r--r-- | threadlib/Makefile.am | 33 | ||||
| -rw-r--r-- | threadlib/configure.in | 84 | ||||
| -rw-r--r-- | threadlib/havepthread.h | 126 | ||||
| -rw-r--r-- | threadlib/nopthread.c | 48 | ||||
| -rw-r--r-- | threadlib/nopthread.h | 43 | ||||
| -rw-r--r-- | threadlib/pthread.c | 431 | ||||
| -rw-r--r-- | threadlib/test.c | 94 | ||||
| -rw-r--r-- | threadlib/testsuite | 4 | ||||
| -rw-r--r-- | threadlib/testsuite.txt | 40 | ||||
| -rw-r--r-- | threadlib/threadlib.h | 21 |
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 |
