From 80e6061ac5deca193759b979d34906bfc9b857ef Mon Sep 17 00:00:00 2001 From: Eric Andersen Date: Wed, 13 Feb 2002 09:32:52 +0000 Subject: Several test suite updates. The testatexit, teston_exit, and pthread tests were contributed by Stefan Soucek --- test/pthread/Makefile | 79 ++++++++++++++++++++++++ test/pthread/ex1.c | 39 ++++++++++++ test/pthread/ex2.c | 113 ++++++++++++++++++++++++++++++++++ test/pthread/ex3.c | 152 ++++++++++++++++++++++++++++++++++++++++++++++ test/pthread/ex4.c | 107 ++++++++++++++++++++++++++++++++ test/pthread/ex5.c | 102 +++++++++++++++++++++++++++++++ test/pthread/ex6.c | 41 +++++++++++++ test/setjmp/.cvsignore | 1 + test/setjmp/setjmp_test.c | 123 +++++++++++++++++++++++++++++++------ test/stdlib/Makefile | 73 +++++++++++++++++++++- test/stdlib/testatexit.c | 74 ++++++++++++++++++++++ test/stdlib/teston_exit.c | 75 +++++++++++++++++++++++ 12 files changed, 958 insertions(+), 21 deletions(-) create mode 100644 test/pthread/Makefile create mode 100644 test/pthread/ex1.c create mode 100644 test/pthread/ex2.c create mode 100644 test/pthread/ex3.c create mode 100644 test/pthread/ex4.c create mode 100644 test/pthread/ex5.c create mode 100644 test/pthread/ex6.c create mode 100644 test/setjmp/.cvsignore create mode 100644 test/stdlib/testatexit.c create mode 100644 test/stdlib/teston_exit.c 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 +#include +#include +#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 +#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 +#include +#include +#include +#include +#include + +/* 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 +#include +#include +#include +#include + +/* 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 +#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 +#include +#include +#include +#include + +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 #include -#include +#include +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 +#include + +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 +#include + +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); +} + -- cgit v1.2.3