diff options
author | Eric Andersen <andersen@codepoet.org> | 2002-02-13 09:32:52 +0000 |
---|---|---|
committer | Eric Andersen <andersen@codepoet.org> | 2002-02-13 09:32:52 +0000 |
commit | 80e6061ac5deca193759b979d34906bfc9b857ef (patch) | |
tree | 51f5c410f9313c6892b50f27321b64db8cfbd737 | |
parent | 98cba6e7dd947aec0d4cf6e61f2f31318ac919e1 (diff) |
Several test suite updates. The testatexit, teston_exit, and
pthread tests were contributed by Stefan Soucek
-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 | ||||
-rw-r--r-- | test/setjmp/.cvsignore | 1 | ||||
-rw-r--r-- | test/setjmp/setjmp_test.c | 123 | ||||
-rw-r--r-- | test/stdlib/Makefile | 73 | ||||
-rw-r--r-- | test/stdlib/testatexit.c | 74 | ||||
-rw-r--r-- | test/stdlib/teston_exit.c | 75 |
12 files changed, 958 insertions, 21 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; +} diff --git a/test/setjmp/.cvsignore b/test/setjmp/.cvsignore new file mode 100644 index 000000000..17550c011 --- /dev/null +++ b/test/setjmp/.cvsignore @@ -0,0 +1 @@ +setjmp_test diff --git a/test/setjmp/setjmp_test.c b/test/setjmp/setjmp_test.c index f371048a5..35d9638ad 100644 --- a/test/setjmp/setjmp_test.c +++ b/test/setjmp/setjmp_test.c @@ -1,35 +1,118 @@ +/* Copyright (C) 1991, 1992, 1997, 1998, 2000 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + 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 <stdio.h> #include <setjmp.h> -#include <unistd.h> +#include <stdlib.h> +static jmp_buf env; +static int last_value = -1, lose = 0; -jmp_buf jb; -int tries=0; +static void +jump (int val) +{ + longjmp (env, val); +} -int main(int argc,char *argv[]) +int +main (void) { - int ret; + int value; - printf("calling setjmp, should return with 0\n"); + value = setjmp (env); + if (value != last_value + 1) + { + fputs("Shouldn't have ", stdout); + lose = 1; + } + last_value = value; + switch (value) + { + case 0: + puts("Saved environment."); + jump (0); + default: + printf ("Jumped to %d.\n", value); + if (value < 10) + jump (value + 1); + } - ret = setjmp(jb); - - printf("setjmp returned %d\n",ret); + if (!lose && value == 10) + { + /* Do a second test, this time without `setjmp' being a macro. + This is not required by ISO C but we have this for compatibility. */ +#undef setjmp + extern int setjmp (jmp_buf); - if(!ret){ - if(tries++>4){ - printf("Hmmm... in loop, must be broken.\n"); - return 0; - } - printf("now calling longjmp, setjmp should return with 1\n"); + last_value = -1; + lose = 0; - longjmp(jb,1); + value = setjmp (env); + if (value != last_value + 1) + { + fputs("Shouldn't have ", stdout); + lose = 1; + } + last_value = value; + switch (value) + { + case 0: + puts("Saved environment."); + jump (0); + default: + printf ("Jumped to %d.\n", value); + if (value < 10) + jump (value + 1); + } + } + + if (!lose && value == 10) + { + /* And again for the `_setjmp' function. */ +#ifndef _setjmp + extern int _setjmp (jmp_buf); +#endif + last_value = -1; + lose = 0; - printf("returned from longjmp, must be broken\n"); - return 0; + value = _setjmp (env); + if (value != last_value + 1) + { + fputs("Shouldn't have ", stdout); + lose = 1; + } + last_value = value; + switch (value) + { + case 0: + puts("Saved environment."); + jump (0); + default: + printf ("Jumped to %d.\n", value); + if (value < 10) + jump (value + 1); } + } - return 0; -} + if (lose || value != 10) + puts ("Test FAILED!"); + else + puts ("Test succeeded!"); + return lose ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/test/stdlib/Makefile b/test/stdlib/Makefile index 4d365609b..209c43b5b 100644 --- a/test/stdlib/Makefile +++ b/test/stdlib/Makefile @@ -25,6 +25,8 @@ TARGETS=testmalloc testmalloc_glibc TARGETS+=mallocbug mallocbug_glibc TARGETS+=teststrtol teststrtol_glibc teststrtol_diff TARGETS+=qsort qsort_glibc qsort_diff +TARGETS+=teston_exit teston_exit_glibc teston_exit_diff +TARGETS+=testatexit testatexit_glibc testatexit_diff all: $(TARGETS) @@ -162,7 +164,76 @@ qsort_diff: qsort_glibc qsort -diff -u qsort_glibc.out qsort.out -@ echo " " +teston_exit: teston_exit.c Makefile $(TESTDIR)/Config $(TESTDIR)/Rules.mak $(CC) + -@ echo "-------" + -@ echo " " + -@ echo "Compiling vs uClibc: " + -@ echo " " + $(CC) $(CFLAGS) -c $< -o $@.o + $(CC) $(LDFLAGS) $@.o -o $@ $(EXTRA_LIBS) + $(STRIPTOOL) -x -R .note -R .comment $@ + -$(LDD) $@ + ls -l $@ + -./$@ > $@.out + -@ echo " " + +teston_exit_glibc: teston_exit.c Makefile + -@ echo "-------" + -@ echo " " + -@ echo "Compiling vs GNU libc: " + -@ echo " " + $(HOST_CC) $(GLIBC_CFLAGS) -c $< -o $@.o + $(HOST_CC) $(GLIBC_LDFLAGS) $@.o -o $@ + $(STRIPTOOL) -x -R .note -R .comment $@ + -$(LDD) $@ + ls -l $@ + -./$@ > $@.out + -@ echo " " + +teston_exit_diff: teston_exit_glibc teston_exit + -@ echo "-------" + -@ echo " " + -@ echo "Diffing output: " + -@ echo " " + -diff -u teston_exit_glibc.out teston_exit.out + -@ echo " " + +testatexit: testatexit.c Makefile $(TESTDIR)/Config $(TESTDIR)/Rules.mak $(CC) + -@ echo "-------" + -@ echo " " + -@ echo "Compiling vs uClibc: " + -@ echo " " + $(CC) $(CFLAGS) -c $< -o $@.o + $(CC) $(LDFLAGS) $@.o -o $@ $(EXTRA_LIBS) + $(STRIPTOOL) -x -R .note -R .comment $@ + -$(LDD) $@ + ls -l $@ + -./$@ > $@.out + -@ echo " " + +testatexit_glibc: testatexit.c Makefile + -@ echo "-------" + -@ echo " " + -@ echo "Compiling vs GNU libc: " + -@ echo " " + $(HOST_CC) $(GLIBC_CFLAGS) -c $< -o $@.o + $(HOST_CC) $(GLIBC_LDFLAGS) $@.o -o $@ + $(STRIPTOOL) -x -R .note -R .comment $@ + -$(LDD) $@ + ls -l $@ + -./$@ > $@.out + -@ echo " " + +testatexit_diff: testatexit_glibc testatexit + -@ echo "-------" + -@ echo " " + -@ echo "Diffing output: " + -@ echo " " + -diff -u testatexit_glibc.out testatexit.out + -@ echo " " + + clean: - rm -f *.[oa] *~ core $(TARGETS) teststrtol_glibc.out teststrtol.out + rm -f *.[oa] *~ core $(TARGETS) *.out diff --git a/test/stdlib/testatexit.c b/test/stdlib/testatexit.c new file mode 100644 index 000000000..374f7b8aa --- /dev/null +++ b/test/stdlib/testatexit.c @@ -0,0 +1,74 @@ +/* + * This test program will register the maximum number of exit functions + * with atexit(). When this program exits, each exit function should get + * called in the reverse order in which it was registered. (If the system + * supports more than 25 exit functions, the function names will loop, but + * the effect will be the same. Feel free to add more functions if desired) + */ +#include <stdio.h> +#include <stdlib.h> + +typedef void (*vfuncp) (void); + +/* All functions call exit(), in order to test that exit functions can call + * exit() without screwing everything up. :) + */ +static void exitfunc0(void) { printf("Executing exitfunc0.\n"); exit(0);} +static void exitfunc1(void) { printf("Executing exitfunc1.\n"); exit(0);} +static void exitfunc2(void) { printf("Executing exitfunc2.\n"); exit(0);} +static void exitfunc3(void) { printf("Executing exitfunc3.\n"); exit(0);} +static void exitfunc4(void) { printf("Executing exitfunc4.\n"); exit(0);} +static void exitfunc5(void) { printf("Executing exitfunc5.\n"); exit(0);} +static void exitfunc6(void) { printf("Executing exitfunc6.\n"); exit(0);} +static void exitfunc7(void) { printf("Executing exitfunc7.\n"); exit(0);} +static void exitfunc8(void) { printf("Executing exitfunc8.\n"); exit(0);} +static void exitfunc9(void) { printf("Executing exitfunc9.\n"); exit(0);} +static void exitfunc10(void) { printf("Executing exitfunc10.\n"); exit(0);} +static void exitfunc11(void) { printf("Executing exitfunc11.\n"); exit(0);} +static void exitfunc12(void) { printf("Executing exitfunc12.\n"); exit(0);} +static void exitfunc13(void) { printf("Executing exitfunc13.\n"); exit(0);} +static void exitfunc14(void) { printf("Executing exitfunc14.\n"); exit(0);} +static void exitfunc15(void) { printf("Executing exitfunc15.\n"); exit(0);} +static void exitfunc16(void) { printf("Executing exitfunc16.\n"); exit(0);} +static void exitfunc17(void) { printf("Executing exitfunc17.\n"); exit(0);} +static void exitfunc18(void) { printf("Executing exitfunc18.\n"); exit(0);} +static void exitfunc19(void) { printf("Executing exitfunc19.\n"); exit(0);} +static void exitfunc20(void) { printf("Executing exitfunc20.\n"); exit(0);} +static void exitfunc21(void) { printf("Executing exitfunc21.\n"); exit(0);} +static void exitfunc22(void) { printf("Executing exitfunc22.\n"); exit(0);} +static void exitfunc23(void) { printf("Executing exitfunc23.\n"); exit(0);} +static void exitfunc24(void) { printf("Executing exitfunc24.\n"); exit(0);} + +static vfuncp func_table[] = + { + exitfunc0, exitfunc1, exitfunc2, exitfunc3, exitfunc4, + exitfunc5, exitfunc6, exitfunc7, exitfunc8, exitfunc9, + exitfunc10, exitfunc11, exitfunc12, exitfunc13, exitfunc14, + exitfunc15, exitfunc16, exitfunc17, exitfunc18, exitfunc19, + exitfunc20, exitfunc21, exitfunc22, exitfunc23, exitfunc24 + }; + +/* glibc dynamically adds exit functions, so it will keep adding until + * it runs out of memory! So this will limit the number of exit functions + * we add in the loop below. uClibc has a set limit (currently 20), so the + * loop will go until it can't add any more (so it should not hit this limit). + */ +#define ATEXIT_LIMIT 20 + +int +main ( void ) +{ + int i = 0; + int count = 0; + int numfuncs = sizeof(func_table)/sizeof(vfuncp); + + /* loop until no more can be added */ + while(count < ATEXIT_LIMIT && atexit(func_table[i]) >= 0) { + printf("Registered exitfunc%d with atexit()\n", i); + count++; + i = (i+1) % numfuncs; + } + printf("%d functions registered with atexit.\n", count); + /* implicit exit */ +} + diff --git a/test/stdlib/teston_exit.c b/test/stdlib/teston_exit.c new file mode 100644 index 000000000..ddfe506a4 --- /dev/null +++ b/test/stdlib/teston_exit.c @@ -0,0 +1,75 @@ +/* + * This test program will register the maximum number of exit functions + * with on_exit(). When this program exits, each exit function should get + * called in the reverse order in which it was registered. (If the system + * supports more than 25 exit functions, the function names will loop, but + * the effect will be the same. Feel free to add more functions if desired) + */ +#include <stdio.h> +#include <stdlib.h> + +typedef void (*efuncp) (int, void *); + +/* All functions call exit(), in order to test that exit functions can call + * exit() without screwing everything up. The value passed in through arg gets + * used as the next exit status. + */ +static void exitfunc0(int status, void *arg) { printf("Executing exitfunc0 (status=%d, arg=%d)\n", status, (int)arg); exit((int)arg);} +static void exitfunc1(int status, void *arg) { printf("Executing exitfunc1 (status=%d, arg=%d)\n", status, (int)arg); exit((int)arg);} +static void exitfunc2(int status, void *arg) { printf("Executing exitfunc2 (status=%d, arg=%d)\n", status, (int)arg); exit((int)arg);} +static void exitfunc3(int status, void *arg) { printf("Executing exitfunc3 (status=%d, arg=%d)\n", status, (int)arg); exit((int)arg);} +static void exitfunc4(int status, void *arg) { printf("Executing exitfunc4 (status=%d, arg=%d)\n", status, (int)arg); exit((int)arg);} +static void exitfunc5(int status, void *arg) { printf("Executing exitfunc5 (status=%d, arg=%d)\n", status, (int)arg); exit((int)arg);} +static void exitfunc6(int status, void *arg) { printf("Executing exitfunc6 (status=%d, arg=%d)\n", status, (int)arg); exit((int)arg);} +static void exitfunc7(int status, void *arg) { printf("Executing exitfunc7 (status=%d, arg=%d)\n", status, (int)arg); exit((int)arg);} +static void exitfunc8(int status, void *arg) { printf("Executing exitfunc8 (status=%d, arg=%d)\n", status, (int)arg); exit((int)arg);} +static void exitfunc9(int status, void *arg) { printf("Executing exitfunc9 (status=%d, arg=%d)\n", status, (int)arg); exit((int)arg);} +static void exitfunc10(int status, void *arg) { printf("Executing exitfunc10 (status=%d, arg=%d)\n", status, (int)arg); exit((int)arg);} +static void exitfunc11(int status, void *arg) { printf("Executing exitfunc11 (status=%d, arg=%d)\n", status, (int)arg); exit((int)arg);} +static void exitfunc12(int status, void *arg) { printf("Executing exitfunc12 (status=%d, arg=%d)\n", status, (int)arg); exit((int)arg);} +static void exitfunc13(int status, void *arg) { printf("Executing exitfunc13 (status=%d, arg=%d)\n", status, (int)arg); exit((int)arg);} +static void exitfunc14(int status, void *arg) { printf("Executing exitfunc14 (status=%d, arg=%d)\n", status, (int)arg); exit((int)arg);} +static void exitfunc15(int status, void *arg) { printf("Executing exitfunc15 (status=%d, arg=%d)\n", status, (int)arg); exit((int)arg);} +static void exitfunc16(int status, void *arg) { printf("Executing exitfunc16 (status=%d, arg=%d)\n", status, (int)arg); exit((int)arg);} +static void exitfunc17(int status, void *arg) { printf("Executing exitfunc17 (status=%d, arg=%d)\n", status, (int)arg); exit((int)arg);} +static void exitfunc18(int status, void *arg) { printf("Executing exitfunc18 (status=%d, arg=%d)\n", status, (int)arg); exit((int)arg);} +static void exitfunc19(int status, void *arg) { printf("Executing exitfunc19 (status=%d, arg=%d)\n", status, (int)arg); exit((int)arg);} +static void exitfunc20(int status, void *arg) { printf("Executing exitfunc20 (status=%d, arg=%d)\n", status, (int)arg); exit((int)arg);} +static void exitfunc21(int status, void *arg) { printf("Executing exitfunc21 (status=%d, arg=%d)\n", status, (int)arg); exit((int)arg);} +static void exitfunc22(int status, void *arg) { printf("Executing exitfunc22 (status=%d, arg=%d)\n", status, (int)arg); exit((int)arg);} +static void exitfunc23(int status, void *arg) { printf("Executing exitfunc23 (status=%d, arg=%d)\n", status, (int)arg); exit((int)arg);} +static void exitfunc24(int status, void *arg) { printf("Executing exitfunc24 (status=%d, arg=%d)\n", status, (int)arg); exit((int)arg);} + +static efuncp func_table[] = + { + exitfunc0, exitfunc1, exitfunc2, exitfunc3, exitfunc4, + exitfunc5, exitfunc6, exitfunc7, exitfunc8, exitfunc9, + exitfunc10, exitfunc11, exitfunc12, exitfunc13, exitfunc14, + exitfunc15, exitfunc16, exitfunc17, exitfunc18, exitfunc19, + exitfunc20, exitfunc21, exitfunc22, exitfunc23, exitfunc24 + }; + +/* glibc dynamically adds exit functions, so it will keep adding until + * it runs out of memory! So this will limit the number of exit functions + * we add in the loop below. uClibc has a set limit (currently 20), so the + * loop will go until it can't add any more (so it should not hit this limit). + */ +#define ON_EXIT_LIMIT 20 + +int +main ( void ) +{ + int i = 0; + int count = 0; + int numfuncs = sizeof(func_table)/sizeof(efuncp); + + /* loop until no more can be added */ + while(count < ON_EXIT_LIMIT && on_exit(func_table[i], (void *)count) >= 0) { + count++; + printf("Registered exitfunc%d with on_exit()\n", i); + i = (i+1) % numfuncs; + } + printf("%d functions registered with on_exit.\n", count); + exit(count); +} + |