diff options
author | Waldemar Brodkorb <wbx@uclibc-ng.org> | 2016-10-24 20:22:12 +0200 |
---|---|---|
committer | Waldemar Brodkorb <wbx@uclibc-ng.org> | 2016-10-24 20:22:12 +0200 |
commit | 7988979a722b4cdf287b2093956a76a3f19b9897 (patch) | |
tree | d35e251d0472ceca55a2eef61cff261c8ee68fab /test/pthread |
add uClibc-ng test directory
Diffstat (limited to 'test/pthread')
-rw-r--r-- | test/pthread/Makefile | 8 | ||||
-rw-r--r-- | test/pthread/Makefile.in | 10 | ||||
-rw-r--r-- | test/pthread/cancellation-points.c | 286 | ||||
-rw-r--r-- | test/pthread/ex1.c | 35 | ||||
-rw-r--r-- | test/pthread/ex2.c | 113 | ||||
-rw-r--r-- | test/pthread/ex3.c | 152 | ||||
-rw-r--r-- | test/pthread/ex4.c | 107 | ||||
-rw-r--r-- | test/pthread/ex5.c | 102 | ||||
-rw-r--r-- | test/pthread/ex6.c | 44 | ||||
-rw-r--r-- | test/pthread/ex7.c | 106 | ||||
-rw-r--r-- | test/pthread/ex8-mtx-odd.c | 56 | ||||
-rw-r--r-- | test/pthread/tst-c99.c | 2 | ||||
-rw-r--r-- | test/pthread/tst-join2.c | 103 | ||||
-rw-r--r-- | test/pthread/tst-join3.c | 122 | ||||
-rw-r--r-- | test/pthread/tst-too-many-cleanups.c | 104 |
15 files changed, 1350 insertions, 0 deletions
diff --git a/test/pthread/Makefile b/test/pthread/Makefile new file mode 100644 index 0000000..97ebee8 --- /dev/null +++ b/test/pthread/Makefile @@ -0,0 +1,8 @@ +# uClibc pthread tests +# Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. + +top_builddir=../../ +top_srcdir=../../ +include ../Rules.mak +-include Makefile.in +include ../Test.mak diff --git a/test/pthread/Makefile.in b/test/pthread/Makefile.in new file mode 100644 index 0000000..bd6b29b --- /dev/null +++ b/test/pthread/Makefile.in @@ -0,0 +1,10 @@ +# uClibc pthread tests +# Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. + +TESTS_DISABLED += cancellation-points + +EXTRA_LDFLAGS := -lpthread + +LDFLAGS_cancellation-points := -lrt + +CFLAGS_tst-c99 := -std=c99 diff --git a/test/pthread/cancellation-points.c b/test/pthread/cancellation-points.c new file mode 100644 index 0000000..5453060 --- /dev/null +++ b/test/pthread/cancellation-points.c @@ -0,0 +1,286 @@ +/* + * Make sure functions marked as cancellation points actually are. + * http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_09.html#tag_02_09_05 + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include <features.h> +#include <sys/ipc.h> +#include <sys/mman.h> +#include <sys/msg.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <fcntl.h> +#include <mqueue.h> +#include <poll.h> +#include <pthread.h> +#include <semaphore.h> +#include <signal.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <termios.h> +#include <time.h> +#include <unistd.h> + +/* take care of optional things ... */ +#define STUB(func, args) static void func args { sleep(0); } +#if defined(__UCLIBC_AIO__) +# include <aio.h> +#else +STUB(aio_suspend, (void *p, int n, const void *p2)) +#endif +#if defined(__UCLIBC_STROPTS__) +# include <stropts.h> +#else +STUB(getmsg, (int f, void *p, void *p2, void *p3)) +STUB(getpmsg, (int f, void *p, void *p2, void *p3, void *p4)) +STUB(putmsg, (int f, void *p, void *p2, void *p3)) +STUB(putpmsg, (int f, void *p, void *p2, void *p3, void *p4)) +#endif +#if defined(__UCLIBC__) +STUB(clock_nanosleep, (int i, int f, const void *p, void *p2)) +#endif + +int cnt; +bool ready; + +void cancel_timeout(int sig) +{ + ready = false; +} +void cancel_thread_cleanup(void *arg) +{ + ready = false; +} + +/* some funcs need some help as they wont take NULL args ... */ +const struct timespec zero_sec = { .tv_sec = 0, .tv_nsec = 0 }; + +sem_t sem; +void help_sem_setup(void) +{ + if (sem_init(&sem, 0, 1) == -1) { + perror("sem_init() failed"); + exit(-1); + } +} + +pthread_cond_t cond = PTHREAD_COND_INITIALIZER; +pthread_mutex_t mutex; +void help_pthread_setup(void) +{ + pthread_mutex_init(&mutex, NULL); + pthread_mutex_lock(&mutex); +} + +/* the pthread function that will call the cancellable function over and over */ +#define _MAKE_CANCEL_THREAD_FUNC_EX(func, sysfunc, args, setup) \ +void *cancel_thread_##func(void *arg) \ +{ \ + if (pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL)) { \ + perror("unable to set cancel type to deferred; something is seriously broken"); \ + exit(-1); \ + } \ + pthread_cleanup_push(cancel_thread_cleanup, NULL); \ + setup; \ + ready = true; \ + while (ready) \ + sysfunc args; \ + pthread_cleanup_pop(1); \ + return NULL; \ +} +#define MAKE_CANCEL_THREAD_FUNC_RE(func, sysfunc, args) _MAKE_CANCEL_THREAD_FUNC_EX(func, sysfunc, args, (void)0) +#define MAKE_CANCEL_THREAD_FUNC_EX(func, args, setup) _MAKE_CANCEL_THREAD_FUNC_EX(func, func, args, setup) +#define MAKE_CANCEL_THREAD_FUNC(func, args) _MAKE_CANCEL_THREAD_FUNC_EX(func, func, args, (void)0) + +MAKE_CANCEL_THREAD_FUNC(accept, (-1, NULL, NULL)) +MAKE_CANCEL_THREAD_FUNC(aio_suspend, (NULL, 0, &zero_sec)) +MAKE_CANCEL_THREAD_FUNC(clock_nanosleep, (0, 0, NULL, NULL)) +MAKE_CANCEL_THREAD_FUNC(close, (-1)) +MAKE_CANCEL_THREAD_FUNC(connect, (-1, NULL, 0)) +MAKE_CANCEL_THREAD_FUNC(creat, ("", 0)) +MAKE_CANCEL_THREAD_FUNC(fcntl, (0, F_SETLKW, NULL)) +MAKE_CANCEL_THREAD_FUNC(fdatasync, (-1)) +MAKE_CANCEL_THREAD_FUNC(fsync, (0)) +MAKE_CANCEL_THREAD_FUNC(getmsg, (-1, NULL, NULL, NULL)) +MAKE_CANCEL_THREAD_FUNC(getpmsg, (-1, NULL, NULL, NULL, NULL)) +MAKE_CANCEL_THREAD_FUNC(lockf, (-1, F_TEST, 0)) +MAKE_CANCEL_THREAD_FUNC(mq_receive, (0, NULL, 0, NULL)) +MAKE_CANCEL_THREAD_FUNC(mq_send, (0, NULL, 0, 0)) +MAKE_CANCEL_THREAD_FUNC(mq_timedreceive, (0, NULL, 0, NULL, NULL)) +MAKE_CANCEL_THREAD_FUNC(mq_timedsend, (0, NULL, 0, 0, NULL)) +MAKE_CANCEL_THREAD_FUNC(msgrcv, (-1, NULL, 0, 0, 0)) +MAKE_CANCEL_THREAD_FUNC(msgsnd, (-1, NULL, 0, 0)) +MAKE_CANCEL_THREAD_FUNC(msync, (NULL, 0, 0)) +MAKE_CANCEL_THREAD_FUNC(nanosleep, (NULL, NULL)) +MAKE_CANCEL_THREAD_FUNC(open, ("", 0)) +MAKE_CANCEL_THREAD_FUNC(pause, ()) +MAKE_CANCEL_THREAD_FUNC(poll, (NULL, 0, 0)) +MAKE_CANCEL_THREAD_FUNC(pread, (-1, NULL, 0, 0)) +MAKE_CANCEL_THREAD_FUNC(pselect, (0, NULL, NULL, NULL, NULL, NULL)) +MAKE_CANCEL_THREAD_FUNC_EX(pthread_cond_timedwait, (&cond, &mutex, &zero_sec), help_pthread_setup()) +MAKE_CANCEL_THREAD_FUNC_EX(pthread_cond_wait, (&cond, &mutex), help_pthread_setup()) +/*MAKE_CANCEL_THREAD_FUNC_EX(pthread_join, (0, NULL))*/ +MAKE_CANCEL_THREAD_FUNC(pthread_testcancel, ()) +MAKE_CANCEL_THREAD_FUNC(putmsg, (-1, NULL, NULL, 0)) +MAKE_CANCEL_THREAD_FUNC(putpmsg, (-1, NULL, NULL, 0, 0)) +MAKE_CANCEL_THREAD_FUNC(pwrite, (-1, NULL, 0, 0)) +MAKE_CANCEL_THREAD_FUNC(read, (-1, NULL, 0)) +MAKE_CANCEL_THREAD_FUNC(readv, (-1, NULL, 0)) +MAKE_CANCEL_THREAD_FUNC(recv, (-1, NULL, 0, 0)) +MAKE_CANCEL_THREAD_FUNC(recvfrom, (-1, NULL, 0, 0, NULL, NULL)) +MAKE_CANCEL_THREAD_FUNC(recvmsg, (-1, NULL, 0)) +MAKE_CANCEL_THREAD_FUNC(select, (0, NULL, NULL, NULL, NULL)) +MAKE_CANCEL_THREAD_FUNC_EX(sem_timedwait, (&sem, &zero_sec), help_sem_setup()) +MAKE_CANCEL_THREAD_FUNC_EX(sem_wait, (&sem), help_sem_setup()) +MAKE_CANCEL_THREAD_FUNC(send, (-1, NULL, 0, 0)) +MAKE_CANCEL_THREAD_FUNC(sendmsg, (-1, NULL, 0)) +MAKE_CANCEL_THREAD_FUNC(sendto, (-1, NULL, 0, 0, NULL, 0)) +#ifdef __UCLIBC_SUSV4_LEGACY__ +MAKE_CANCEL_THREAD_FUNC(sigpause, (0)) +#endif +MAKE_CANCEL_THREAD_FUNC(sigsuspend, (NULL)) +MAKE_CANCEL_THREAD_FUNC(sigtimedwait, (NULL, NULL, NULL)) +MAKE_CANCEL_THREAD_FUNC(sigwait, (NULL, NULL)) +MAKE_CANCEL_THREAD_FUNC(sigwaitinfo, (NULL, NULL)) +MAKE_CANCEL_THREAD_FUNC(sleep, (0)) +MAKE_CANCEL_THREAD_FUNC(system, ("")) +MAKE_CANCEL_THREAD_FUNC(tcdrain, (-1)) +#ifdef __UCLIBC_SUSV3_LEGACY__ +MAKE_CANCEL_THREAD_FUNC(usleep, (0)) +#endif +MAKE_CANCEL_THREAD_FUNC(wait, (NULL)) +MAKE_CANCEL_THREAD_FUNC(waitid, (0, 0, NULL, 0)) +MAKE_CANCEL_THREAD_FUNC(waitpid, (-1, NULL, 0)) +MAKE_CANCEL_THREAD_FUNC(write, (-1, NULL, 0)) +MAKE_CANCEL_THREAD_FUNC(writev, (-1, NULL, 0)) + +/* test a few variations that should not cancel ... */ +MAKE_CANCEL_THREAD_FUNC_RE(fcntl_another, fcntl, (0, F_GETFD)) + +/* main test that creates thread, cancels it, etc... */ +int _test_func(const char *func_name, void *(*func)(void*), const int should_cancel) +{ + int ret; + pthread_t cancel_thread_id; + + ++cnt; + + printf("testing %-30s ", func_name); + + printf("."); + if (signal(SIGALRM, cancel_timeout) == SIG_ERR) { + perror("unable to bind SIGALRM"); + exit(-1); + } + + printf("."); + ready = false; + pthread_create(&cancel_thread_id, NULL, func, NULL); + + printf("."); + while (!ready) + sched_yield(); + + printf("."); + if (pthread_cancel(cancel_thread_id)) { + perror("unable to cancel thread"); + exit(-1); + } + + printf("."); + alarm(5); + while (ready) + sched_yield(); + + printf("."); + ret = (!!!alarm(0) == should_cancel); + + if (ret) + printf(" failed ;(\n"); + else + printf(" OK!\n"); + + return ret; +} +#define TEST_FUNC(f) _test_func(#f, cancel_thread_##f, 1) +#define TEST_FUNC_RE(f) _test_func(#f, cancel_thread_##f, 0) + +int main(int argc, char *argv[]) +{ + int ret = 0; + setbuf(stdout, NULL); + cnt = 0; + + ret += TEST_FUNC(accept); + ret += TEST_FUNC(aio_suspend); + ret += TEST_FUNC(clock_nanosleep); + ret += TEST_FUNC(close); + ret += TEST_FUNC(connect); + ret += TEST_FUNC(creat); + ret += TEST_FUNC(fcntl); + ret += TEST_FUNC(fdatasync); + ret += TEST_FUNC(fsync); + ret += TEST_FUNC(getmsg); + ret += TEST_FUNC(getpmsg); + ret += TEST_FUNC(lockf); + ret += TEST_FUNC(mq_receive); + ret += TEST_FUNC(mq_send); + ret += TEST_FUNC(mq_timedreceive); + ret += TEST_FUNC(mq_timedsend); + ret += TEST_FUNC(msgrcv); + ret += TEST_FUNC(msgsnd); + ret += TEST_FUNC(msync); + ret += TEST_FUNC(nanosleep); + ret += TEST_FUNC(open); + ret += TEST_FUNC(pause); + ret += TEST_FUNC(poll); + ret += TEST_FUNC(pread); + ret += TEST_FUNC(pselect); + ret += TEST_FUNC(pthread_cond_timedwait); + ret += TEST_FUNC(pthread_cond_wait); + /*ret += TEST_FUNC(pthread_join);*/ + ret += TEST_FUNC(pthread_testcancel); + ret += TEST_FUNC(putmsg); + ret += TEST_FUNC(putpmsg); + ret += TEST_FUNC(pwrite); + ret += TEST_FUNC(read); + ret += TEST_FUNC(readv); + ret += TEST_FUNC(recv); + ret += TEST_FUNC(recvfrom); + ret += TEST_FUNC(recvmsg); + ret += TEST_FUNC(select); + ret += TEST_FUNC(sem_timedwait); + ret += TEST_FUNC(sem_wait); + ret += TEST_FUNC(send); + ret += TEST_FUNC(sendmsg); + ret += TEST_FUNC(sendto); + ret += TEST_FUNC(sigpause); + ret += TEST_FUNC(sigsuspend); + ret += TEST_FUNC(sigtimedwait); + ret += TEST_FUNC(sigwait); + ret += TEST_FUNC(sigwaitinfo); + ret += TEST_FUNC(sleep); + ret += TEST_FUNC(system); + ret += TEST_FUNC(tcdrain); +#ifdef __UCLIBC_SUSV3_LEGACY__ + ret += TEST_FUNC(usleep); +#endif + ret += TEST_FUNC(wait); + ret += TEST_FUNC(waitid); + ret += TEST_FUNC(waitpid); + ret += TEST_FUNC(write); + ret += TEST_FUNC(writev); + + ret += TEST_FUNC_RE(fcntl_another); + + if (ret) + printf("!!! %i / %i tests failed\n", ret, cnt); + + return ret; +} diff --git a/test/pthread/ex1.c b/test/pthread/ex1.c new file mode 100644 index 0000000..4d9de03 --- /dev/null +++ b/test/pthread/ex1.c @@ -0,0 +1,35 @@ +/* Creates two threads, one printing 10000 "a"s, the other printing + 10000 "b"s. + Illustrates: thread creation, thread joining. */ + +#include <stddef.h> +#include <stdio.h> +#include <unistd.h> +#include "pthread.h" + +static void *process(void * arg) +{ + int i; + printf("Starting process %s\n", (char *)arg); + for (i = 0; i < 10000; i++) + write(1, (char *) arg, 1); + return NULL; +} + +#define sucfail(r) (r != 0 ? "failed" : "succeeded") +int main(void) +{ + int pret, ret = 0; + pthread_t th_a, th_b; + void *retval; + + ret += (pret = pthread_create(&th_a, NULL, process, (void *)"a")); + printf("create a %s %d\n", sucfail(pret), pret); + ret += (pret = pthread_create(&th_b, NULL, process, (void *)"b")); + printf("create b %s %d\n", sucfail(pret), pret); + ret += (pret = pthread_join(th_a, &retval)); + printf("join a %s %d\n", sucfail(pret), pret); + ret += (pret = pthread_join(th_b, &retval)); + printf("join b %s %d\n", sucfail(pret), pret); + return ret; +} diff --git a/test/pthread/ex2.c b/test/pthread/ex2.c new file mode 100644 index 0000000..98bd4b3 --- /dev/null +++ b/test/pthread/ex2.c @@ -0,0 +1,113 @@ +/* The classic producer-consumer example. + Illustrates mutexes and conditions. + All integers between 0 and 9999 should be printed exactly twice, + once to the right of the arrow and once to the left. */ + +#include <stdio.h> +#include "pthread.h" + +#define BUFFER_SIZE 16 + +/* Circular buffer of integers. */ + +struct prodcons { + int buffer[BUFFER_SIZE]; /* the actual data */ + pthread_mutex_t lock; /* mutex ensuring exclusive access to buffer */ + int readpos, writepos; /* positions for reading and writing */ + pthread_cond_t notempty; /* signaled when buffer is not empty */ + pthread_cond_t notfull; /* signaled when buffer is not full */ +}; + +/* Initialize a buffer */ + +static void init(struct prodcons * b) +{ + pthread_mutex_init(&b->lock, NULL); + pthread_cond_init(&b->notempty, NULL); + pthread_cond_init(&b->notfull, NULL); + b->readpos = 0; + b->writepos = 0; +} + +/* Store an integer in the buffer */ + +static void put(struct prodcons * b, int data) +{ + pthread_mutex_lock(&b->lock); + /* Wait until buffer is not full */ + while ((b->writepos + 1) % BUFFER_SIZE == b->readpos) { + pthread_cond_wait(&b->notfull, &b->lock); + /* pthread_cond_wait reacquired b->lock before returning */ + } + /* Write the data and advance write pointer */ + b->buffer[b->writepos] = data; + b->writepos++; + if (b->writepos >= BUFFER_SIZE) b->writepos = 0; + /* Signal that the buffer is now not empty */ + pthread_cond_signal(&b->notempty); + pthread_mutex_unlock(&b->lock); +} + +/* Read and remove an integer from the buffer */ + +static int get(struct prodcons * b) +{ + int data; + pthread_mutex_lock(&b->lock); + /* Wait until buffer is not empty */ + while (b->writepos == b->readpos) { + pthread_cond_wait(&b->notempty, &b->lock); + } + /* Read the data and advance read pointer */ + data = b->buffer[b->readpos]; + b->readpos++; + if (b->readpos >= BUFFER_SIZE) b->readpos = 0; + /* Signal that the buffer is now not full */ + pthread_cond_signal(&b->notfull); + pthread_mutex_unlock(&b->lock); + return data; +} + +/* A test program: one thread inserts integers from 1 to 10000, + the other reads them and prints them. */ + +#define OVER (-1) + +struct prodcons buffer; + +static void * producer(void * data) +{ + int n; + for (n = 0; n < 10000; n++) { + printf("%d --->\n", n); + put(&buffer, n); + } + put(&buffer, OVER); + return NULL; +} + +static void * consumer(void * data) +{ + int d; + while (1) { + d = get(&buffer); + if (d == OVER) break; + printf("---> %d\n", d); + } + return NULL; +} + +int main(void) +{ + pthread_t th_a, th_b; + void * retval; + + init(&buffer); + /* Create the threads */ + pthread_create(&th_a, NULL, producer, 0); + pthread_create(&th_b, NULL, consumer, 0); + /* Wait until producer and consumer finish. */ + pthread_join(th_a, &retval); + pthread_join(th_b, &retval); + return 0; +} diff --git a/test/pthread/ex3.c b/test/pthread/ex3.c new file mode 100644 index 0000000..8ef7797 --- /dev/null +++ b/test/pthread/ex3.c @@ -0,0 +1,152 @@ +/* Multi-thread searching. + Illustrates: thread cancellation, cleanup handlers. */ + +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/types.h> +#include <pthread.h> + +/* Defines the number of searching threads */ +#define NUM_THREADS 5 + +/* Function prototypes */ +void *search(void *); +void print_it(void *); + +/* Global variables */ +pthread_t threads[NUM_THREADS]; +pthread_mutex_t lock; +int tries; +volatile int started; + +int main(int argc, char ** argv) +{ + unsigned long i; + unsigned long pid; + + /* create a number to search for */ + pid = getpid(); + printf("Searching for the number = %ld...\n", pid); + + /* Initialize the mutex lock */ + pthread_mutex_init(&lock, NULL); + + /* Create the searching threads */ + for (started=0; started<NUM_THREADS; started++) + pthread_create(&threads[started], NULL, search, (void *)pid); + + /* Wait for (join) all the searching threads */ + for (i=0; i<NUM_THREADS; i++) + pthread_join(threads[i], NULL); + + printf("It took %d tries to find the number.\n", tries); + + /* Exit the program */ + return 0; +} + +/* This is the cleanup function that is called + when the threads are cancelled */ + +void print_it(void *arg) +{ + int *try = (int *) arg; + pthread_t tid; + + /* Get the calling thread's ID */ + tid = pthread_self(); + + /* Print where the thread was in its search when it was cancelled */ + printf("Thread %lx was canceled on its %d try.\n", tid, *try); +} + +/* This is the search routine that is executed in each thread */ + +void *search(void *arg) +{ + unsigned long num = (unsigned long) arg; + unsigned long i, j, ntries; + pthread_t tid; + + /* get the calling thread ID */ + tid = pthread_self(); + + /* use the thread ID to set the seed for the random number generator */ + /* Since srand and rand are not thread-safe, serialize with lock */ + + /* Try to lock the mutex lock -- + if locked, check to see if the thread has been cancelled + if not locked then continue */ + while (pthread_mutex_trylock(&lock) == EBUSY) + pthread_testcancel(); + + srand((int)tid); + i = rand() & 0xFFFFFF; + pthread_mutex_unlock(&lock); + ntries = 0; + + /* Set the cancellation parameters -- + - Enable thread cancellation + - Defer the action of the cancellation */ + + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); + + while (started < NUM_THREADS) + sched_yield (); + + /* Push the cleanup routine (print_it) onto the thread + cleanup stack. This routine will be called when the + thread is cancelled. Also note that the pthread_cleanup_push + call must have a matching pthread_cleanup_pop call. The + push and pop calls MUST be at the same lexical level + within the code */ + + /* Pass address of `ntries' since the current value of `ntries' is not + the one we want to use in the cleanup function */ + + pthread_cleanup_push(print_it, (void *)&ntries); + + /* Loop forever */ + while (1) { + i = (i + 1) & 0xFFFFFF; + ntries++; + + /* Does the random number match the target number? */ + if (num == i) { + /* Try to lock the mutex lock -- + if locked, check to see if the thread has been cancelled + if not locked then continue */ + while (pthread_mutex_trylock(&lock) == EBUSY) + pthread_testcancel(); + + /* Set the global variable for the number of tries */ + tries = ntries; + printf("Thread %lx found the number!\n", tid); + + /* Cancel all the other threads */ + for (j=0; j<NUM_THREADS; j++) + if (threads[j] != tid) pthread_cancel(threads[j]); + + /* Break out of the while loop */ + break; + } + + /* Every 100 tries check to see if the thread has been cancelled. */ + if (ntries % 100 == 0) { + pthread_testcancel(); + } + } + + /* The only way we can get here is when the thread breaks out + of the while loop. In this case the thread that makes it here + has found the number we are looking for and does not need to run + the thread cleanup function. This is why the pthread_cleanup_pop + function is called with a 0 argument; this will pop the cleanup + function off the stack without executing it */ + + pthread_cleanup_pop(0); + return((void *)0); +} diff --git a/test/pthread/ex4.c b/test/pthread/ex4.c new file mode 100644 index 0000000..cf4cf1d --- /dev/null +++ b/test/pthread/ex4.c @@ -0,0 +1,107 @@ +/* Making a library function that uses static variables thread-safe. + Illustrates: thread-specific data, pthread_once(). */ + +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <pthread.h> + +/* This is a typical example of a library function that uses + static variables to accumulate results between calls. + Here, it just returns the concatenation of all string arguments + that were given to it. */ + +#if 0 + +static char * str_accumulate(char * s) +{ + static char accu[1024] = { 0 }; + strcat(accu, s); + return accu; +} + +#endif + +/* Of course, this cannot be used in a multi-threaded program + because all threads store "accu" at the same location. + So, we'll use thread-specific data to have a different "accu" + for each thread. */ + +/* Key identifying the thread-specific data */ +static pthread_key_t str_key; +/* "Once" variable ensuring that the key for str_alloc will be allocated + exactly once. */ +static pthread_once_t str_alloc_key_once = PTHREAD_ONCE_INIT; + +/* Forward functions */ +static void str_alloc_key(void); +static void str_alloc_destroy_accu(void * accu); + +/* Thread-safe version of str_accumulate */ + +static char * str_accumulate(const char * s) +{ + char * accu; + + /* Make sure the key is allocated */ + pthread_once(&str_alloc_key_once, str_alloc_key); + /* Get the thread-specific data associated with the key */ + accu = (char *) pthread_getspecific(str_key); + /* It's initially NULL, meaning that we must allocate the buffer first. */ + if (accu == NULL) { + accu = malloc(1024); + if (accu == NULL) return NULL; + accu[0] = 0; + /* Store the buffer pointer in the thread-specific data. */ + pthread_setspecific(str_key, (void *) accu); + printf("Thread %lx: allocating buffer at %p\n", pthread_self(), accu); + } + /* Now we can use accu just as in the non thread-safe code. */ + strcat(accu, s); + return accu; +} + +/* Function to allocate the key for str_alloc thread-specific data. */ + +static void str_alloc_key(void) +{ + pthread_key_create(&str_key, str_alloc_destroy_accu); + printf("Thread %lx: allocated key %d\n", pthread_self(), str_key); +} + +/* Function to free the buffer when the thread exits. */ +/* Called only when the thread-specific data is not NULL. */ + +static void str_alloc_destroy_accu(void * accu) +{ + printf("Thread %lx: freeing buffer at %p\n", pthread_self(), accu); + free(accu); +} + +/* Test program */ + +static void * process(void * arg) +{ + char * res; + res = str_accumulate("Result of "); + res = str_accumulate((char *) arg); + res = str_accumulate(" thread"); + printf("Thread %lx: \"%s\"\n", pthread_self(), res); + return NULL; +} + +int main(int argc, char ** argv) +{ + char * res; + pthread_t th1, th2; + + res = str_accumulate("Result of "); + pthread_create(&th1, NULL, process, (void *) "first"); + pthread_create(&th2, NULL, process, (void *) "second"); + res = str_accumulate("initial thread"); + printf("Thread %lx: \"%s\"\n", pthread_self(), res); + pthread_join(th1, NULL); + pthread_join(th2, NULL); + exit(0); +} diff --git a/test/pthread/ex5.c b/test/pthread/ex5.c new file mode 100644 index 0000000..7a293eb --- /dev/null +++ b/test/pthread/ex5.c @@ -0,0 +1,102 @@ +/* The classic producer-consumer example, implemented with semaphores. + All integers between 0 and 9999 should be printed exactly twice, + once to the right of the arrow and once to the left. */ + +#include <stdio.h> +#include "pthread.h" +#include "semaphore.h" + +#define BUFFER_SIZE 16 + +/* Circular buffer of integers. */ + +struct prodcons { + int buffer[BUFFER_SIZE]; /* the actual data */ + int readpos, writepos; /* positions for reading and writing */ + sem_t sem_read; /* number of elements available for reading */ + sem_t sem_write; /* number of locations available for writing */ +}; + +/* Initialize a buffer */ + +static void init(struct prodcons * b) +{ + sem_init(&b->sem_write, 0, BUFFER_SIZE - 1); + sem_init(&b->sem_read, 0, 0); + b->readpos = 0; + b->writepos = 0; +} + +/* Store an integer in the buffer */ + +static void put(struct prodcons * b, int data) +{ + /* Wait until buffer is not full */ + sem_wait(&b->sem_write); + /* Write the data and advance write pointer */ + b->buffer[b->writepos] = data; + b->writepos++; + if (b->writepos >= BUFFER_SIZE) b->writepos = 0; + /* Signal that the buffer contains one more element for reading */ + sem_post(&b->sem_read); +} + +/* Read and remove an integer from the buffer */ + +static int get(struct prodcons * b) +{ + int data; + /* Wait until buffer is not empty */ + sem_wait(&b->sem_read); + /* Read the data and advance read pointer */ + data = b->buffer[b->readpos]; + b->readpos++; + if (b->readpos >= BUFFER_SIZE) b->readpos = 0; + /* Signal that the buffer has now one more location for writing */ + sem_post(&b->sem_write); + return data; +} + +/* A test program: one thread inserts integers from 1 to 10000, + the other reads them and prints them. */ + +#define OVER (-1) + +struct prodcons buffer; + +static void * producer(void * data) +{ + int n; + for (n = 0; n < 10000; n++) { + printf("%d --->\n", n); + put(&buffer, n); + } + put(&buffer, OVER); + return NULL; +} + +static void * consumer(void * data) +{ + int d; + while (1) { + d = get(&buffer); + if (d == OVER) break; + printf("---> %d\n", d); + } + return NULL; +} + +int main(void) +{ + pthread_t th_a, th_b; + void * retval; + + init(&buffer); + /* Create the threads */ + pthread_create(&th_a, NULL, producer, 0); + pthread_create(&th_b, NULL, consumer, 0); + /* Wait until producer and consumer finish. */ + pthread_join(th_a, &retval); + pthread_join(th_b, &retval); + return 0; +} diff --git a/test/pthread/ex6.c b/test/pthread/ex6.c new file mode 100644 index 0000000..ffb6287 --- /dev/null +++ b/test/pthread/ex6.c @@ -0,0 +1,44 @@ +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <pthread.h> +#include <time.h> + +static void * +test_thread (void *v_param) +{ + return NULL; +} + +int +main (void) +{ + unsigned long count; + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = 10 * 1000; + + setvbuf (stdout, NULL, _IONBF, 0); + + for (count = 0; count < 2000; ++count) + { + pthread_t thread; + int status; + + status = pthread_create (&thread, NULL, test_thread, NULL); + if (status != 0) + { + printf ("status = %d, count = %lu: %s\n", status, count, + strerror (errno)); + return 1; + } + else + { + printf ("count = %lu\n", count); + } + /* pthread_detach (thread); */ + pthread_join (thread, NULL); + nanosleep (&ts, NULL); + } + return 0; +} diff --git a/test/pthread/ex7.c b/test/pthread/ex7.c new file mode 100644 index 0000000..8eeb9a2 --- /dev/null +++ b/test/pthread/ex7.c @@ -0,0 +1,106 @@ +/* ex7 + * + * Test case that illustrates a timed wait on a condition variable. + */ + +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <pthread.h> +#include <sys/time.h> +#include <time.h> + +/* Our event variable using a condition variable contruct. */ +typedef struct { + pthread_mutex_t mutex; + pthread_cond_t cond; + int flag; +} event_t; + + +/* Global event to signal main thread the timeout of the child thread. */ +event_t main_event; + + +static void * +test_thread (void *ms_param) +{ + unsigned long status = 0; + event_t foo; + struct timespec timeout; + struct timeval now; + long ms = (long) ms_param; + + /* initialize cond var */ + pthread_cond_init(&foo.cond, NULL); + pthread_mutex_init(&foo.mutex, NULL); + foo.flag = 0; + + /* set the time out value */ + printf("waiting %ld ms ...\n", ms); + gettimeofday(&now, NULL); + timeout.tv_sec = now.tv_sec + ms/1000 + (now.tv_usec + (ms%1000)*1000)/1000000; + timeout.tv_nsec = ((now.tv_usec + (ms%1000)*1000) % 1000000) * 1000; + + /* Just use this to test the time out. The cond var is never signaled. */ + pthread_mutex_lock(&foo.mutex); + while (foo.flag == 0 && status != ETIMEDOUT) { + status = pthread_cond_timedwait(&foo.cond, &foo.mutex, &timeout); + } + pthread_mutex_unlock(&foo.mutex); + + /* post the main event */ + pthread_mutex_lock(&main_event.mutex); + main_event.flag = 1; + pthread_cond_signal(&main_event.cond); + pthread_mutex_unlock(&main_event.mutex); + + /* that's it, bye */ + return (void*) status; +} + +int +main (void) +{ + unsigned long count; + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = 10 * 1000; + + setvbuf (stdout, NULL, _IONBF, 0); + + /* initialize main event cond var */ + pthread_cond_init(&main_event.cond, NULL); + pthread_mutex_init(&main_event.mutex, NULL); + main_event.flag = 0; + + for (count = 0; count < 20; ++count) + { + pthread_t thread; + int status; + + /* pass down the milli-second timeout in the void* param */ + status = pthread_create (&thread, NULL, test_thread, (void*) (count*100)); + if (status != 0) { + printf ("status = %d, count = %lu: %s\n", status, count, + strerror (errno)); + return 1; + } + else { + + /* wait for the event posted by the child thread */ + pthread_mutex_lock(&main_event.mutex); + while (main_event.flag == 0) { + pthread_cond_wait(&main_event.cond, &main_event.mutex); + } + main_event.flag = 0; + pthread_mutex_unlock(&main_event.mutex); + + printf ("count = %lu\n", count); + } + + nanosleep (&ts, NULL); + } + + return 0; +} diff --git a/test/pthread/ex8-mtx-odd.c b/test/pthread/ex8-mtx-odd.c new file mode 100644 index 0000000..791b2c2 --- /dev/null +++ b/test/pthread/ex8-mtx-odd.c @@ -0,0 +1,56 @@ +/* Copyright (C) 2002 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@redhat.com>, 2002. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include <errno.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + + +static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + +static int +do_test (void) +{ + + if (pthread_mutex_lock (&lock) != 0) + { + puts ("mutex_lock failed"); + exit (1); + } + + if (pthread_mutex_unlock (&lock) != 0) + { + puts ("1st mutex_unlock failed"); + exit (1); + } + + if (pthread_mutex_unlock (&lock) != 0) + { + puts ("2nd mutex_unlock failed"); + exit (1); + } + + return 0; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/test/pthread/tst-c99.c b/test/pthread/tst-c99.c new file mode 100644 index 0000000..3cc91b1 --- /dev/null +++ b/test/pthread/tst-c99.c @@ -0,0 +1,2 @@ +#include <pthread.h> +int main(void) { return 0; } diff --git a/test/pthread/tst-join2.c b/test/pthread/tst-join2.c new file mode 100644 index 0000000..6d994f3 --- /dev/null +++ b/test/pthread/tst-join2.c @@ -0,0 +1,103 @@ +/* Copyright (C) 2002 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@redhat.com>, 2002. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#include <errno.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + + +static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + + +static void * +tf (void *arg) +{ + if (pthread_mutex_lock (&lock) != 0) + { + puts ("child: mutex_lock failed"); + return NULL; + } + + return (void *) 42l; +} + + +static int +do_test (void) +{ + pthread_t th; + + if (pthread_mutex_lock (&lock) != 0) + { + puts ("mutex_lock failed"); + exit (1); + } + + if (pthread_create (&th, NULL, tf, NULL) != 0) + { + puts ("mutex_create failed"); + exit (1); + } + + void *status; + int val = pthread_tryjoin_np (th, &status); + if (val == 0) + { + puts ("1st tryjoin succeeded"); + exit (1); + } + else if (val != EBUSY) + { + puts ("1st tryjoin didn't return EBUSY"); + exit (1); + } + + if (pthread_mutex_unlock (&lock) != 0) + { + puts ("mutex_unlock failed"); + exit (1); + } + + while ((val = pthread_tryjoin_np (th, &status)) != 0) + { + if (val != EBUSY) + { + printf ("tryjoin returned %s (%d), expected only 0 or EBUSY\n", + strerror (val), val); + exit (1); + } + + /* Delay minimally. */ + struct timespec ts = { .tv_sec = 0, .tv_nsec = 10000000 }; + nanosleep (&ts, NULL); + } + + if (status != (void *) 42l) + { + printf ("return value %p, expected %p\n", status, (void *) 42l); + exit (1); + } + + return 0; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/test/pthread/tst-join3.c b/test/pthread/tst-join3.c new file mode 100644 index 0000000..7816f4d --- /dev/null +++ b/test/pthread/tst-join3.c @@ -0,0 +1,122 @@ +/* Copyright (C) 2002 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@redhat.com>, 2002. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#include <errno.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/time.h> + + +static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + + +static void * +tf (void *arg) +{ + if (pthread_mutex_lock (&lock) != 0) + { + puts ("child: mutex_lock failed"); + return NULL; + } + + return (void *) 42l; +} + + +static int +do_test (void) +{ + pthread_t th; + + if (pthread_mutex_lock (&lock) != 0) + { + puts ("mutex_lock failed"); + exit (1); + } + + if (pthread_create (&th, NULL, tf, NULL) != 0) + { + puts ("mutex_create failed"); + exit (1); + } + + void *status; + struct timespec ts; + struct timeval tv; + (void) gettimeofday (&tv, NULL); + TIMEVAL_TO_TIMESPEC (&tv, &ts); + ts.tv_nsec += 200000000; + if (ts.tv_nsec >= 1000000000) + { + ts.tv_nsec -= 1000000000; + ++ts.tv_sec; + } + int val = pthread_timedjoin_np (th, &status, &ts); + if (val == 0) + { + puts ("1st timedjoin succeeded"); + exit (1); + } + else if (val != ETIMEDOUT) + { + puts ("1st timedjoin didn't return ETIMEDOUT"); + exit (1); + } + + if (pthread_mutex_unlock (&lock) != 0) + { + puts ("mutex_unlock failed"); + exit (1); + } + + while (1) + { + (void) gettimeofday (&tv, NULL); + TIMEVAL_TO_TIMESPEC (&tv, &ts); + ts.tv_nsec += 200000000; + if (ts.tv_nsec >= 1000000000) + { + ts.tv_nsec -= 1000000000; + ++ts.tv_sec; + } + + val = pthread_timedjoin_np (th, &status, &ts); + if (val == 0) + break; + + if (val != ETIMEDOUT) + { + printf ("timedjoin returned %s (%d), expected only 0 or ETIMEDOUT\n", + strerror (val), val); + exit (1); + } + } + + if (status != (void *) 42l) + { + printf ("return value %p, expected %p\n", status, (void *) 42l); + exit (1); + } + + return 0; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/test/pthread/tst-too-many-cleanups.c b/test/pthread/tst-too-many-cleanups.c new file mode 100644 index 0000000..7828c50 --- /dev/null +++ b/test/pthread/tst-too-many-cleanups.c @@ -0,0 +1,104 @@ +/* + * This illustrates the bug where the cleanup function + * of a thread may be called too many times. + * + * main thread: + * - grab mutex + * - spawn thread1 + * - go to sleep + * thread1: + * - register cleanup handler via pthread_cleanup_push() + * - try to grab mutex and sleep + * main: + * - kill thread1 + * - go to sleep + * thread1 cleanup handler: + * - try to grab mutex and sleep + * main: + * - kill thread1 + * - go to sleep + * thread1 cleanup handler: + * - wrongly called again + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> +#include <assert.h> +#include <unistd.h> + +#define warn(fmt, args...) fprintf(stderr, "[%p] " fmt, (void*)pthread_self(), ## args) +#define warnf(fmt, args...) warn("%s:%i: " fmt, __FUNCTION__, __LINE__, ## args) + +int ok_to_kill_thread; + +static void thread_killed(void *arg); + +static void *KillMeThread(void *thread_par) +{ + pthread_t pthread_id; + + warnf("Starting child thread\n"); + + pthread_id = pthread_self(); + pthread_cleanup_push(thread_killed, (void *)pthread_id); + + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + + /* main code */ + warnf("please kill me now\n"); + while (1) { + ok_to_kill_thread = 1; + sleep(1); + } + + pthread_cleanup_pop(0); + + return 0; +} + +static void thread_killed(void *arg) +{ + static int num_times_called = 0; + + warnf("killing %p [cnt=%i]\n", arg, ++num_times_called); + assert(num_times_called == 1); + + /* pick any cancellation endpoint, sleep() will do just fine */ + while (1) { + warnf("sleeping in cancellation endpoint ...\n"); + sleep(1); + } + + warnf("done cleaning up\n"); +} + +int main(int argc, char *argv[]) +{ + int count = 3; + pthread_t app_pthread_id; + + /* need to tweak this test a bit to play nice with signals and LT */ + return 0; + + ok_to_kill_thread = 0; + + pthread_create(&app_pthread_id, NULL, KillMeThread, NULL); + + warnf("waiting for thread to prepare itself\n"); + while (!ok_to_kill_thread) + sleep(1); + + while (count--) { + warnf("killing thread\n"); + pthread_cancel(app_pthread_id); + sleep(3); + } + + return 0; +} |