Browse Source

Several test suite updates. The testatexit, teston_exit, and
pthread tests were contributed by Stefan Soucek

Eric Andersen 22 years ago
parent
commit
80e6061ac5

+ 79 - 0
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)
+

+ 39 - 0
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;
+}

+ 113 - 0
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;
+}

+ 152 - 0
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);
+}

+ 107 - 0
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);
+}

+ 102 - 0
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;
+}

+ 41 - 0
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;
+}

+ 1 - 0
test/setjmp/.cvsignore

@@ -0,0 +1 @@
+setjmp_test

+ 103 - 20
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;
+}

+ 72 - 1
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
 
 

+ 74 - 0
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 */
+}
+

+ 75 - 0
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);
+}
+