diff options
Diffstat (limited to 'test/pthread')
-rw-r--r-- | test/pthread/Makefile | 79 | ||||
-rw-r--r-- | test/pthread/ex1.c | 39 | ||||
-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 | 41 |
7 files changed, 633 insertions, 0 deletions
diff --git a/test/pthread/Makefile b/test/pthread/Makefile new file mode 100644 index 000000000..c4b1c9e5e --- /dev/null +++ b/test/pthread/Makefile @@ -0,0 +1,79 @@ +TESTDIR=../ +include $(TESTDIR)/Rules.mak + +LDFLAGS += +#EXTRA_LIBS += -lc -lgcc -lpthread +EXTRA_LIBS += -lpthread + +TARGETS=ex1 ex2 ex3 ex4 ex5 ex6 +all: $(TARGETS) + +ex1: ex1.c Makefile $(TESTDIR)/Config $(TESTDIR)/Rules.mak $(TESTCC) + -@ echo "-------" + -@ echo " " + -@ echo "Compiling vs uClibc: " + -@ echo " " + $(CC) $(CFLAGS) -c $< -o $@.o + $(CC) $(LDFLAGS) $@.o -o $@ $(EXTRA_LIBS) + $(STRIPTOOL) -x -R .note -R .comment $@ + -./$@ + -@ echo " " + +ex2: ex2.c Makefile $(TESTDIR)/Config $(TESTDIR)/Rules.mak $(TESTCC) + -@ echo "-------" + -@ echo " " + -@ echo "Compiling vs uClibc: " + -@ echo " " + $(CC) $(CFLAGS) -c $< -o $@.o + $(CC) $(LDFLAGS) $@.o -o $@ $(EXTRA_LIBS) + $(STRIPTOOL) -x -R .note -R .comment $@ + -./$@ + -@ echo " " + +ex3: ex3.c Makefile $(TESTDIR)/Config $(TESTDIR)/Rules.mak $(TESTCC) + -@ echo "-------" + -@ echo " " + -@ echo "Compiling vs uClibc: " + -@ echo " " + $(CC) $(CFLAGS) -c $< -o $@.o + $(CC) $(LDFLAGS) $@.o -o $@ $(EXTRA_LIBS) + $(STRIPTOOL) -x -R .note -R .comment $@ + -./$@ + -@ echo " " + +ex4: ex4.c Makefile $(TESTDIR)/Config $(TESTDIR)/Rules.mak $(TESTCC) + -@ echo "-------" + -@ echo " " + -@ echo "Compiling vs uClibc: " + -@ echo " " + $(CC) $(CFLAGS) -c $< -o $@.o + $(CC) $(LDFLAGS) $@.o -o $@ $(EXTRA_LIBS) + $(STRIPTOOL) -x -R .note -R .comment $@ + -./$@ + -@ echo " " + +ex5: ex5.c Makefile $(TESTDIR)/Config $(TESTDIR)/Rules.mak $(TESTCC) + -@ echo "-------" + -@ echo " " + -@ echo "Compiling vs uClibc: " + -@ echo " " + $(CC) $(CFLAGS) -c $< -o $@.o + $(CC) $(LDFLAGS) $@.o -o $@ $(EXTRA_LIBS) + $(STRIPTOOL) -x -R .note -R .comment $@ + -./$@ + -@ echo " " + +ex6: ex6.c Makefile $(TESTDIR)/Config $(TESTDIR)/Rules.mak $(TESTCC) + -@ echo "-------" + -@ echo " " + -@ echo "Compiling vs uClibc: " + -@ echo " " + $(CC) $(CFLAGS) -c $< -o $@.o + $(CC) $(LDFLAGS) $@.o -o $@ $(EXTRA_LIBS) + $(STRIPTOOL) -x -R .note -R .comment $@ + -./$@ + -@ echo " " + +clean: + rm -f *.[oa] *~ core $(TARGETS) + diff --git a/test/pthread/ex1.c b/test/pthread/ex1.c new file mode 100644 index 000000000..09e6e3fbd --- /dev/null +++ b/test/pthread/ex1.c @@ -0,0 +1,39 @@ +/* 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" + +void * process(void * arg) +{ + int i; + fprintf(stderr, "Starting process %s\n", (char *) arg); + for (i = 0; i < 10000; i++) { + write(1, (char *) arg, 1); + } + return NULL; +} + +int main(void) +{ + int retcode; + pthread_t th_a, th_b; + void * retval; + + retcode = pthread_create(&th_a, NULL, process, (void *) "a"); + if (retcode != 0) fprintf(stderr, "create a failed %d\n", retcode); + else fprintf(stderr, "create a succeeded %d\n", retcode); + retcode = pthread_create(&th_b, NULL, process, (void *) "b"); + if (retcode != 0) fprintf(stderr, "create b failed %d\n", retcode); + else fprintf(stderr, "create b succeeded %d\n", retcode); + retcode = pthread_join(th_a, &retval); + if (retcode != 0) fprintf(stderr, "join a failed %d\n", retcode); + else fprintf(stderr, "join a succeeded %d\n", retcode); + retcode = pthread_join(th_b, &retval); + if (retcode != 0) fprintf(stderr, "join b failed %d\n", retcode); + else fprintf(stderr, "join b succeeded %d\n", retcode); + return 0; +} diff --git a/test/pthread/ex2.c b/test/pthread/ex2.c new file mode 100644 index 000000000..70cb6b398 --- /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 */ + +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 */ + +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 */ + +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; + +void * producer(void * data) +{ + int n; + for (n = 0; n < 10000; n++) { + printf("%d --->\n", n); + put(&buffer, n); + } + put(&buffer, OVER); + return NULL; +} + +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 000000000..8005200ef --- /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) +{ + int i; + int pid; + + /* create a number to search for */ + pid = getpid(); + printf("Searching for the number = %d...\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) +{ + int num = (int) arg; + int 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 000000000..11a09f013 --- /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 + +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 */ + +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 */ + +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 000000000..475de0e0c --- /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 */ + +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 */ + +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 */ + +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; + +void * producer(void * data) +{ + int n; + for (n = 0; n < 10000; n++) { + printf("%d --->\n", n); + put(&buffer, n); + } + put(&buffer, OVER); + return NULL; +} + +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 000000000..15914ce85 --- /dev/null +++ b/test/pthread/ex6.c @@ -0,0 +1,41 @@ +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <pthread.h> +#include <unistd.h> + +void * +test_thread (void *v_param) +{ + return NULL; +} + +int +main (void) +{ + unsigned long count; + + 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); + usleep (10); + } + return 0; +} |