diff options
Diffstat (limited to 'linuxthreads/Examples')
-rw-r--r-- | linuxthreads/Examples/Makefile | 15 | ||||
-rw-r--r-- | linuxthreads/Examples/ex1.c | 42 | ||||
-rw-r--r-- | linuxthreads/Examples/ex10.c | 108 | ||||
-rw-r--r-- | linuxthreads/Examples/ex11.c | 154 | ||||
-rw-r--r-- | linuxthreads/Examples/ex12.c | 47 | ||||
-rw-r--r-- | linuxthreads/Examples/ex13.c | 112 | ||||
-rw-r--r-- | linuxthreads/Examples/ex14.c | 133 | ||||
-rw-r--r-- | linuxthreads/Examples/ex15.c | 58 | ||||
-rw-r--r-- | linuxthreads/Examples/ex16.c | 26 | ||||
-rw-r--r-- | linuxthreads/Examples/ex17.c | 112 | ||||
-rw-r--r-- | linuxthreads/Examples/ex18.c | 113 | ||||
-rw-r--r-- | linuxthreads/Examples/ex2.c | 124 | ||||
-rw-r--r-- | linuxthreads/Examples/ex3.c | 152 | ||||
-rw-r--r-- | linuxthreads/Examples/ex4.c | 115 | ||||
-rw-r--r-- | linuxthreads/Examples/ex5.c | 114 | ||||
-rw-r--r-- | linuxthreads/Examples/ex6.c | 46 | ||||
-rw-r--r-- | linuxthreads/Examples/ex7.c | 45 | ||||
-rw-r--r-- | linuxthreads/Examples/ex8.c | 101 | ||||
-rw-r--r-- | linuxthreads/Examples/ex9.c | 98 | ||||
-rw-r--r-- | linuxthreads/Examples/tststatic.c | 1 |
20 files changed, 1716 insertions, 0 deletions
diff --git a/linuxthreads/Examples/Makefile b/linuxthreads/Examples/Makefile new file mode 100644 index 0000000000..c68b3676a4 --- /dev/null +++ b/linuxthreads/Examples/Makefile @@ -0,0 +1,15 @@ +CC=gcc +CFLAGS=-g -O -Wall -I.. -D_REENTRANT +LIBPTHREAD=../libpthread.a + +PROGS=ex1 ex2 ex3 ex4 ex5 proxy + +all: $(PROGS) + +.c: + $(CC) $(CFLAGS) -o $* $*.c $(LIBPTHREAD) + +$(PROGS): + +clean: + rm -f $(PROGS) diff --git a/linuxthreads/Examples/ex1.c b/linuxthreads/Examples/ex1.c new file mode 100644 index 0000000000..29138cf761 --- /dev/null +++ b/linuxthreads/Examples/ex1.c @@ -0,0 +1,42 @@ +/* Creates two threads, one printing 10000 "a"s, the other printing + 10000 "b"s. + Illustrates: thread creation, thread joining. */ + +#include <stddef.h> +#include <stdio.h> +#include <unistd.h> +#include "pthread.h" + +static void * +process (void *arg) +{ + int i; + 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); + retcode = pthread_create (&th_b, NULL, process, (void *) "b"); + if (retcode != 0) + fprintf (stderr, "create b failed %d\n", retcode); + retcode = pthread_join (th_a, &retval); + if (retcode != 0) + fprintf (stderr, "join a failed %d\n", retcode); + retcode = pthread_join (th_b, &retval); + if (retcode != 0) + fprintf (stderr, "join b failed %d\n", retcode); + return 0; +} diff --git a/linuxthreads/Examples/ex10.c b/linuxthreads/Examples/ex10.c new file mode 100644 index 0000000000..f3ad517283 --- /dev/null +++ b/linuxthreads/Examples/ex10.c @@ -0,0 +1,108 @@ +/* Tests for pthread_mutex_timedlock function. + Copyright (C) 2000, 2001, 2002 Free Software Foundation, Inc. + Contributed by Kaz Kylheku <kaz@ashi.footprints.net>, 2000. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include <errno.h> +#include <error.h> +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> +#include <time.h> + +#define NUM_THREADS 10 +#define NUM_ITERS 50 +#define TIMEOUT_NS 100000000L + +static void *thread (void *) __attribute__ ((__noreturn__)); +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + +int +main (void) +{ + pthread_t th; + int i; + + for (i = 0; i < NUM_THREADS; i++) + { + if (pthread_create (&th, NULL, thread, NULL) != 0) + error (EXIT_FAILURE, 0, "cannot create thread"); + } + + (void) thread (NULL); + /* notreached */ + return 0; +} + + +static void * +thread (void *arg) +{ + int i; + pthread_t self = pthread_self (); + static int linecount; /* protected by flockfile(stdout) */ + + for (i = 0; i < NUM_ITERS; i++) + { + struct timespec ts; + + for (;;) + { + int err; + + clock_gettime (CLOCK_REALTIME, &ts); + + ts.tv_nsec += TIMEOUT_NS; + + if (ts.tv_nsec >= 1000000000L) { + ts.tv_sec++; + ts.tv_nsec -= 1000000000L; + } + + switch ((err = pthread_mutex_timedlock (&mutex, &ts))) + { + case 0: + flockfile (stdout); + printf ("%04d: thread %lu got mutex\n", ++linecount, + (unsigned long) self); + funlockfile (stdout); + break; + case ETIMEDOUT: + flockfile (stdout); + printf ("%04d: thread %lu timed out on mutex\n", ++linecount, + (unsigned long) self); + funlockfile (stdout); + continue; + default: + error (EXIT_FAILURE, err, "pthread_mutex_timedlock failure"); + } + break; + } + + ts.tv_sec = 0; + ts.tv_nsec = TIMEOUT_NS; + nanosleep (&ts, NULL); + + flockfile (stdout); + printf ("%04d: thread %lu releasing mutex\n", ++linecount, + (unsigned long) self); + funlockfile (stdout); + pthread_mutex_unlock (&mutex); + } + + pthread_exit (NULL); +} diff --git a/linuxthreads/Examples/ex11.c b/linuxthreads/Examples/ex11.c new file mode 100644 index 0000000000..abb5b5385a --- /dev/null +++ b/linuxthreads/Examples/ex11.c @@ -0,0 +1,154 @@ +/* Test program for timedout read/write lock functions. + Copyright (C) 2000 Free Software Foundation, Inc. + Contributed by Ulrich Drepper <drepper@redhat.com>, 2000. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include <errno.h> +#include <error.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> + + +#define NWRITERS 15 +#define WRITETRIES 10 +#define NREADERS 15 +#define READTRIES 15 + +#define TIMEOUT 1000000 +#define DELAY 1000000 + +static pthread_rwlock_t lock = PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP; + + +static void * +writer_thread (void *nr) +{ + struct timespec ts; + struct timespec delay; + int n; + + ts.tv_sec = 0; + ts.tv_nsec = TIMEOUT; + + delay.tv_sec = 0; + delay.tv_nsec = DELAY; + + for (n = 0; n < WRITETRIES; ++n) + { + do + { + clock_gettime (CLOCK_REALTIME, &ts); + + ts.tv_nsec += 2 * TIMEOUT; + + printf ("writer thread %ld tries again\n", (long int) nr); + } + //while (pthread_rwlock_wrlock (&lock), 0); + while (pthread_rwlock_timedwrlock (&lock, &ts) == ETIMEDOUT); + + printf ("writer thread %ld succeeded\n", (long int) nr); + + nanosleep (&delay, NULL); + + pthread_rwlock_unlock (&lock); + + printf ("writer thread %ld released\n", (long int) nr); + } + + return NULL; +} + + +static void * +reader_thread (void *nr) +{ + struct timespec ts; + struct timespec delay; + int n; + + delay.tv_sec = 0; + delay.tv_nsec = DELAY; + + for (n = 0; n < READTRIES; ++n) + { + do + { + clock_gettime (CLOCK_REALTIME, &ts); + + ts.tv_nsec += TIMEOUT; + + printf ("reader thread %ld tries again\n", (long int) nr); + } + //while (pthread_rwlock_rdlock (&lock), 0); + while (pthread_rwlock_timedrdlock (&lock, &ts) == ETIMEDOUT); + + printf ("reader thread %ld succeeded\n", (long int) nr); + + nanosleep (&delay, NULL); + + pthread_rwlock_unlock (&lock); + + printf ("reader thread %ld released\n", (long int) nr); + } + + return NULL; +} + + +int +main (void) +{ + pthread_t thwr[NWRITERS]; + pthread_t thrd[NREADERS]; + int n; + void *res; + + /* Make standard error the same as standard output. */ + dup2 (1, 2); + + /* Make sure we see all message, even those on stdout. */ + setvbuf (stdout, NULL, _IONBF, 0); + + for (n = 0; n < NWRITERS; ++n) + { + int err = pthread_create (&thwr[n], NULL, writer_thread, + (void *) (long int) n); + + if (err != 0) + error (EXIT_FAILURE, err, "cannot create writer thread"); + } + + for (n = 0; n < NREADERS; ++n) + { + int err = pthread_create (&thrd[n], NULL, reader_thread, + (void *) (long int) n); + + if (err != 0) + error (EXIT_FAILURE, err, "cannot create reader thread"); + } + + /* Wait for all the threads. */ + for (n = 0; n < NWRITERS; ++n) + pthread_join (thwr[n], &res); + for (n = 0; n < NREADERS; ++n) + pthread_join (thrd[n], &res); + + return 0; +} diff --git a/linuxthreads/Examples/ex12.c b/linuxthreads/Examples/ex12.c new file mode 100644 index 0000000000..e986fec97f --- /dev/null +++ b/linuxthreads/Examples/ex12.c @@ -0,0 +1,47 @@ +/* Variant of ex6, but this time we use pthread_exit (). */ +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <pthread.h> +#include <unistd.h> + +static void * +__attribute__ ((noreturn)) +test_thread (void *v_param) +{ + pthread_exit (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); */ + if (pthread_join (thread, NULL) != 0) + { + printf ("join failed, count %lu\n", count); + return 2; + } + usleep (10); + } + return 0; +} diff --git a/linuxthreads/Examples/ex13.c b/linuxthreads/Examples/ex13.c new file mode 100644 index 0000000000..14add6c773 --- /dev/null +++ b/linuxthreads/Examples/ex13.c @@ -0,0 +1,112 @@ +/* Test for Pthreads/mutexes. + Copyright (C) 2000, 2001, 2002, 2003 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Kurt Garloff <garloff@suse.de>, 2000. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include <errno.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +static void *thread_start (void *ptr) __attribute__ ((__noreturn__)); + + +struct thr_ctrl +{ + pthread_mutex_t mutex; + pthread_cond_t cond; + int retval; +}; + +static void +dump_mut (pthread_mutex_t * mut) +{ + size_t i; + for (i = 0; i < sizeof (*mut); i++) + printf (" %02x", *((unsigned char *) mut + i)); + printf ("\n"); +}; + +/* Helper, the opposite of pthread_cond_wait (cond, mut). */ +static void +pthr_cond_signal_mutex (pthread_cond_t * cond, pthread_mutex_t * mut) +{ + int err; + err = pthread_mutex_lock (mut); + if (err) + printf ("mutex_lock : %s\n", strerror (err)); + err = pthread_cond_signal (cond); + if (err) + printf ("cond_signal : %s\n", strerror (err)); + err = pthread_mutex_unlock (mut); + if (err) + printf ("mutex_unlock: %s\n", strerror (err)); +} + +static void * +thread_start (void *ptr) +{ + struct thr_ctrl *tc = ptr; + /* Do initialization. */ + /* ... */ + /* Signal that we are ready. */ + pthr_cond_signal_mutex (&tc->cond, &tc->mutex); + sleep (2); + pthr_cond_signal_mutex (&tc->cond, &tc->mutex); + tc->retval = 0; + pthread_exit (&tc->retval); +} + +int +main (void) +{ + struct thr_ctrl threadctrl; + pthread_t thread; + int err; + void *res = &threadctrl.retval; + pthread_mutexattr_t mutattr; + pthread_mutexattr_init (&mutattr); + pthread_mutex_init (&threadctrl.mutex, &mutattr); + pthread_cond_init (&threadctrl.cond, NULL); + err = pthread_mutex_lock (&threadctrl.mutex); + if (err) + printf ("mutex_lock : %s\n", strerror (err)); + dump_mut (&threadctrl.mutex); + pthread_create (&thread, NULL, thread_start, &threadctrl); + /* Wait until it's ready. */ + err = pthread_cond_wait (&threadctrl.cond, &threadctrl.mutex); + if (err) + printf ("cond_wait : %s\n", strerror (err)); + /* Now, we should have acquired the mutex again! */ + dump_mut (&threadctrl.mutex); + sleep (1); + dump_mut (&threadctrl.mutex); + err = pthread_cond_wait (&threadctrl.cond, &threadctrl.mutex); + if (err) + { + printf ("cond_wait : %s\n", strerror (err)); + printf ("ERROR\n"); + abort (); + }; + dump_mut (&threadctrl.mutex); + pthread_join (thread, &res); + printf ("OK\n"); + return 0; +} diff --git a/linuxthreads/Examples/ex14.c b/linuxthreads/Examples/ex14.c new file mode 100644 index 0000000000..406e03f346 --- /dev/null +++ b/linuxthreads/Examples/ex14.c @@ -0,0 +1,133 @@ +/* Test of POSIX barriers. */ + +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define NTHREADS 20 + +#define ROUNDS 20 + +static pthread_barrier_t barriers[NTHREADS]; + +static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; +static int counters[NTHREADS]; +static int serial[NTHREADS]; + +static void * +worker (void *arg) +{ + void *result = NULL; + int nr = (long int) arg; + int i; + + for (i = 0; i < ROUNDS; ++i) + { + int j; + int retval; + + if (nr == 0) + { + memset (counters, '\0', sizeof (counters)); + memset (serial, '\0', sizeof (serial)); + } + + retval = pthread_barrier_wait (&barriers[NTHREADS - 1]); + if (retval != 0 && retval != PTHREAD_BARRIER_SERIAL_THREAD) + { + printf ("thread %d failed to wait for all the others\n", nr); + result = (void *) 1; + } + + for (j = nr; j < NTHREADS; ++j) + { + /* Increment the counter for this round. */ + pthread_mutex_lock (&lock); + ++counters[j]; + pthread_mutex_unlock (&lock); + + /* Wait for the rest. */ + retval = pthread_barrier_wait (&barriers[j]); + + /* Test the result. */ + if (nr == 0 && counters[j] != j + 1) + { + printf ("barrier in round %d released but count is %d\n", + j, counters[j]); + result = (void *) 1; + } + + if (retval != 0) + { + if (retval != PTHREAD_BARRIER_SERIAL_THREAD) + { + printf ("thread %d in round %d has nonzero return value != PTHREAD_BARRIER_SERIAL_THREAD\n", + nr, j); + result = (void *) 1; + } + else + { + pthread_mutex_lock (&lock); + ++serial[j]; + pthread_mutex_unlock (&lock); + } + } + + /* Wait for the rest again. */ + retval = pthread_barrier_wait (&barriers[j]); + + /* Now we can check whether exactly one thread was serializing. */ + if (nr == 0 && serial[j] != 1) + { + printf ("not exactly one serial thread in round %d\n", j); + result = (void *) 1; + } + } + } + + return result; +} + + +#define TEST_FUNCTION do_test () +#define TIMEOUT 60 +static int +do_test (void) +{ + pthread_t threads[NTHREADS]; + int i; + void *res; + int result = 0; + + /* Initialized the barrier variables. */ + for (i = 0; i < NTHREADS; ++i) + if (pthread_barrier_init (&barriers[i], NULL, i + 1) != 0) + { + printf ("Failed to initialize barrier %d\n", i); + exit (1); + } + + /* Start the threads. */ + for (i = 0; i < NTHREADS; ++i) + if (pthread_create (&threads[i], NULL, worker, (void *) (long int) i) != 0) + { + printf ("Failed to start thread %d\n", i); + exit (1); + } + + /* And wait for them. */ + for (i = 0; i < NTHREADS; ++i) + if (pthread_join (threads[i], &res) != 0 || res != NULL) + { + printf ("thread %d returned a failure\n", i); + result = 1; + } + + if (result == 0) + puts ("all OK"); + + return result; +} + +#include "../test-skeleton.c" diff --git a/linuxthreads/Examples/ex15.c b/linuxthreads/Examples/ex15.c new file mode 100644 index 0000000000..e953b231d7 --- /dev/null +++ b/linuxthreads/Examples/ex15.c @@ -0,0 +1,58 @@ +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <pthread.h> +#include <unistd.h> + +static void *worker (void *dummy) __attribute__ ((__noreturn__)); + +static void * +worker (void *dummy) +{ + exit (26); +} + +#define TEST_FUNCTION do_test () +#define TIMEOUT 10 +static int +do_test (void) +{ + pthread_t th; + pid_t pid; + int status; + + switch ((pid = fork ())) + { + case -1: + puts ("Could not fork"); + exit (1); + case 0: + if (pthread_create(&th, NULL, worker, NULL) != 0) + { + puts ("Failed to start thread"); + exit (1); + } + for (;;); + exit (1); + default: + break; + } + + if (waitpid (pid, &status, 0) != pid) + { + puts ("waitpid failed"); + exit (1); + } + + if (!WIFEXITED (status) || WEXITSTATUS (status) != 26) + { + printf ("Wrong exit code %d\n", status); + exit (1); + } + + puts ("All OK"); + return 0; +} + +#include "../../test-skeleton.c" diff --git a/linuxthreads/Examples/ex16.c b/linuxthreads/Examples/ex16.c new file mode 100644 index 0000000000..6509ae4515 --- /dev/null +++ b/linuxthreads/Examples/ex16.c @@ -0,0 +1,26 @@ +/* Tst case by Jakub Jelinek <jakub@redhat.com>. */ +#include <stdlib.h> +#include <unistd.h> +#include <pthread.h> + +static void * +task (void *p) +{ + sleep (30); + return NULL; +} + +int +main (void) +{ + pthread_t t; + int status; + + status = pthread_create (&t, NULL, task, NULL); + if (status) + exit (status); + + status = pthread_detach (t); + pthread_kill_other_threads_np (); + return status; +} diff --git a/linuxthreads/Examples/ex17.c b/linuxthreads/Examples/ex17.c new file mode 100644 index 0000000000..1bc09a5bda --- /dev/null +++ b/linuxthreads/Examples/ex17.c @@ -0,0 +1,112 @@ +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <pthread.h> +#include <unistd.h> +#include <limits.h> +#include <sys/mman.h> + +static pthread_mutex_t synch = PTHREAD_MUTEX_INITIALIZER; + +static void * +test_thread (void *v_param) +{ + pthread_mutex_lock (&synch); + return NULL; +} + +#define STACKSIZE 0x100000 + +int +main (void) +{ + pthread_t thread; + pthread_attr_t attr; + int status; + void *stack, *stack2; + size_t stacksize; + + pthread_attr_init (&attr); + stack = mmap (NULL, STACKSIZE, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + + if (stack == MAP_FAILED) + { + perror ("mmap failed"); + return 1; + } + + status = pthread_attr_setstack (&attr, stack, STACKSIZE); + if (status != 0) + { + printf ("pthread_attr_setstack failed: %s\n", strerror (status)); + return 1; + } + + status = pthread_attr_getstack (&attr, &stack2, &stacksize); + if (status != 0) + { + printf ("pthread_attr_getstack failed: %s\n", strerror (status)); + return 1; + } + + if (stack2 != stack || stacksize != STACKSIZE) + { + printf ("first pthread_attr_getstack returned different stack (%p,%zx)\n" + "than was set by setstack (%p,%x)\n", + stack2, stacksize, stack, STACKSIZE); + return 2; + } + + status = pthread_mutex_lock (&synch); + if (status != 0) + { + printf ("cannot get lock: %s\n", strerror (status)); + return 1; + } + + status = pthread_create (&thread, &attr, test_thread, NULL); + if (status != 0) + { + printf ("pthread_create failed: %s\n", strerror (status)); + return 1; + } + + status = pthread_getattr_np (thread, &attr); + if (status != 0) + { + printf ("pthread_getattr_np failed: %s\n", strerror (status)); + return 1; + } + + status = pthread_attr_getstack (&attr, &stack2, &stacksize); + if (status != 0) + { + printf ("pthread_attr_getstack failed: %s\n", strerror (status)); + return 1; + } + + if (stack2 != stack || stacksize != STACKSIZE) + { + printf ("second pthread_attr_getstack returned different stack (%p,%zx)\n" + "than was set by setstack (%p,%x)\n", + stack2, stacksize, stack, STACKSIZE); + return 3; + } + + status = pthread_mutex_unlock (&synch); + if (status != 0) + { + printf ("cannot release lock: %s\n", strerror (status)); + return 1; + } + + /* pthread_detach (thread); */ + if (pthread_join (thread, NULL) != 0) + { + printf ("join failed\n"); + return 1; + } + return 0; +} diff --git a/linuxthreads/Examples/ex18.c b/linuxthreads/Examples/ex18.c new file mode 100644 index 0000000000..283396bede --- /dev/null +++ b/linuxthreads/Examples/ex18.c @@ -0,0 +1,113 @@ +/* + * Beat up the pthread_key_create and pthread_key_delete + * functions. + */ + +#if 0 +#define CHATTY +#endif + +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include <pthread.h> +#include <unistd.h> + +const int beatup_iterations = 10000; +const int num_threads = 30; +const int max_keys = 500; + +struct key_list { + struct key_list *next; + pthread_key_t key; +}; + +struct key_list *key_list; +pthread_mutex_t key_lock = PTHREAD_MUTEX_INITIALIZER; + +/* + * Create a new key and put it at the tail of a linked list. + * If the linked list grows to a certain length, delete a key from the + * head of * the list. + */ + +static void +beat_up(void) +{ + struct key_list *new = malloc(sizeof *new); + struct key_list **iter, *old_key = 0; + int key_count = 0; + + if (new == 0) { + fprintf(stderr, "malloc failed\n"); + abort(); + } + + new->next = 0; + + if (pthread_key_create(&new->key, 0) != 0) { + fprintf(stderr, "pthread_key_create failed\n"); + abort(); + } + + if (pthread_getspecific(new->key) != 0) { + fprintf(stderr, "new pthread_key_t resolves to non-null value\n"); + abort(); + } + + pthread_setspecific(new->key, (void *) 1); + +#ifdef CHATTY + printf("created key\n"); +#endif + + pthread_mutex_lock(&key_lock); + + for (iter = &key_list; *iter != 0; iter = &(*iter)->next) + key_count++; + + *iter = new; + + if (key_count > max_keys) { + old_key = key_list; + key_list = key_list->next; + } + + pthread_mutex_unlock(&key_lock); + + if (old_key != 0) { +#ifdef CHATTY + printf("deleting key\n"); +#endif + pthread_key_delete(old_key->key); + } +} + +static void * +thread(void *arg) +{ + int i; + for (i = 0; i < beatup_iterations; i++) + beat_up(); + return 0; +} + +int +main(void) +{ + int i; + pthread_attr_t detached_thread; + + pthread_attr_init(&detached_thread); + pthread_attr_setdetachstate(&detached_thread, PTHREAD_CREATE_DETACHED); + + for (i = 0; i < num_threads; i++) { + pthread_t thread_id; + while (pthread_create(&thread_id, &detached_thread, thread, 0) == EAGAIN) { + /* let some threads die, so system can breathe. :) */ + sleep(1); + } + } + + pthread_exit(0); +} diff --git a/linuxthreads/Examples/ex2.c b/linuxthreads/Examples/ex2.c new file mode 100644 index 0000000000..f2556a4206 --- /dev/null +++ b/linuxthreads/Examples/ex2.c @@ -0,0 +1,124 @@ +/* The classic producer-consumer example. + Illustrates mutexes and conditions. + All integers between 0 and 9999 should be printed exactly twice, + once to the right of the arrow and once to the left. */ + +#include <stdio.h> +#include "pthread.h" + +#define BUFFER_SIZE 16 + +/* Circular buffer of integers. */ + +struct prodcons +{ + int buffer[BUFFER_SIZE]; /* the actual data */ + pthread_mutex_t lock; /* mutex ensuring exclusive access to buffer */ + int readpos, writepos; /* positions for reading and writing */ + pthread_cond_t notempty; /* signaled when buffer is not empty */ + pthread_cond_t notfull; /* signaled when buffer is not full */ +}; + +/* Initialize a buffer */ +static void +init (struct prodcons *b) +{ + pthread_mutex_init (&b->lock, NULL); + pthread_cond_init (&b->notempty, NULL); + pthread_cond_init (&b->notfull, NULL); + b->readpos = 0; + b->writepos = 0; +} + +/* Store an integer in the buffer */ +static void +put (struct prodcons *b, int data) +{ + pthread_mutex_lock (&b->lock); + /* Wait until buffer is not full */ + while ((b->writepos + 1) % BUFFER_SIZE == b->readpos) + { + pthread_cond_wait (&b->notfull, &b->lock); + /* pthread_cond_wait reacquired b->lock before returning */ + } + /* Write the data and advance write pointer */ + b->buffer[b->writepos] = data; + b->writepos++; + if (b->writepos >= BUFFER_SIZE) + b->writepos = 0; + /* Signal that the buffer is now not empty */ + pthread_cond_signal (&b->notempty); + pthread_mutex_unlock (&b->lock); +} + +/* Read and remove an integer from the buffer */ +static int +get (struct prodcons *b) +{ + int data; + pthread_mutex_lock (&b->lock); + /* Wait until buffer is not empty */ + while (b->writepos == b->readpos) + { + pthread_cond_wait (&b->notempty, &b->lock); + } + /* Read the data and advance read pointer */ + data = b->buffer[b->readpos]; + b->readpos++; + if (b->readpos >= BUFFER_SIZE) + b->readpos = 0; + /* Signal that the buffer is now not full */ + pthread_cond_signal (&b->notfull); + pthread_mutex_unlock (&b->lock); + return data; +} + +/* A test program: one thread inserts integers from 1 to 10000, + the other reads them and prints them. */ + +#define OVER (-1) + +struct prodcons buffer; + +static void * +producer (void *data) +{ + int n; + for (n = 0; n < 10000; n++) + { + printf ("%d --->\n", n); + put (&buffer, n); + } + put (&buffer, OVER); + return NULL; +} + +static void * +consumer (void *data) +{ + int d; + while (1) + { + d = get (&buffer); + if (d == OVER) + break; + printf ("---> %d\n", d); + } + return NULL; +} + +int +main (void) +{ + pthread_t th_a, th_b; + void *retval; + + init (&buffer); + /* Create the threads */ + pthread_create (&th_a, NULL, producer, 0); + pthread_create (&th_b, NULL, consumer, 0); + /* Wait until producer and consumer finish. */ + pthread_join (th_a, &retval); + pthread_join (th_b, &retval); + return 0; +} diff --git a/linuxthreads/Examples/ex3.c b/linuxthreads/Examples/ex3.c new file mode 100644 index 0000000000..b80b323a33 --- /dev/null +++ b/linuxthreads/Examples/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 *) (long int) 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 = (long 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/linuxthreads/Examples/ex4.c b/linuxthreads/Examples/ex4.c new file mode 100644 index 0000000000..5c8b929e22 --- /dev/null +++ b/linuxthreads/Examples/ex4.c @@ -0,0 +1,115 @@ +/* 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 */ + +static char * +str_accumulate (const char *s) +{ + char *accu; + + /* Make sure the key is allocated */ + pthread_once (&str_alloc_key_once, str_alloc_key); + /* Get the thread-specific data associated with the key */ + accu = (char *) pthread_getspecific (str_key); + /* It's initially NULL, meaning that we must allocate the buffer first. */ + if (accu == NULL) + { + accu = malloc (1024); + if (accu == NULL) + return NULL; + accu[0] = 0; + /* Store the buffer pointer in the thread-specific data. */ + pthread_setspecific (str_key, (void *) accu); + printf ("Thread %lx: allocating buffer at %p\n", pthread_self (), accu); + } + /* Now we can use accu just as in the non thread-safe code. */ + strcat (accu, s); + return accu; +} + +/* Function to allocate the key for str_alloc thread-specific data. */ + +static void +str_alloc_key (void) +{ + pthread_key_create (&str_key, str_alloc_destroy_accu); + printf ("Thread %lx: allocated key %d\n", pthread_self (), str_key); +} + +/* Function to free the buffer when the thread exits. */ +/* Called only when the thread-specific data is not NULL. */ + +static void +str_alloc_destroy_accu (void *accu) +{ + printf ("Thread %lx: freeing buffer at %p\n", pthread_self (), accu); + free (accu); +} + +/* Test program */ + +static void * +process (void *arg) +{ + char *res; + res = str_accumulate ("Result of "); + res = str_accumulate ((char *) arg); + res = str_accumulate (" thread"); + printf ("Thread %lx: \"%s\"\n", pthread_self (), res); + return NULL; +} + +int +main (int argc, char **argv) +{ + char *res; + pthread_t th1, th2; + + res = str_accumulate ("Result of "); + pthread_create (&th1, NULL, process, (void *) "first"); + pthread_create (&th2, NULL, process, (void *) "second"); + res = str_accumulate ("initial thread"); + printf ("Thread %lx: \"%s\"\n", pthread_self (), res); + pthread_join (th1, NULL); + pthread_join (th2, NULL); + return 0; +} diff --git a/linuxthreads/Examples/ex5.c b/linuxthreads/Examples/ex5.c new file mode 100644 index 0000000000..d39d487603 --- /dev/null +++ b/linuxthreads/Examples/ex5.c @@ -0,0 +1,114 @@ +/* The classic producer-consumer example, implemented with semaphores. + All integers between 0 and 9999 should be printed exactly twice, + once to the right of the arrow and once to the left. */ + +#include <stdio.h> +#include "pthread.h" +#include "semaphore.h" + +#define BUFFER_SIZE 16 + +/* Circular buffer of integers. */ + +struct prodcons +{ + int buffer[BUFFER_SIZE]; /* the actual data */ + int readpos, writepos; /* positions for reading and writing */ + sem_t sem_read; /* number of elements available for reading */ + sem_t sem_write; /* number of locations available for writing */ +}; + +/* Initialize a buffer */ + +static void +init (struct prodcons *b) +{ + sem_init (&b->sem_write, 0, BUFFER_SIZE - 1); + sem_init (&b->sem_read, 0, 0); + b->readpos = 0; + b->writepos = 0; +} + +/* Store an integer in the buffer */ + +static void +put (struct prodcons *b, int data) +{ + /* Wait until buffer is not full */ + sem_wait (&b->sem_write); + /* Write the data and advance write pointer */ + b->buffer[b->writepos] = data; + b->writepos++; + if (b->writepos >= BUFFER_SIZE) + b->writepos = 0; + /* Signal that the buffer contains one more element for reading */ + sem_post (&b->sem_read); +} + +/* Read and remove an integer from the buffer */ + +static int +get (struct prodcons *b) +{ + int data; + /* Wait until buffer is not empty */ + sem_wait (&b->sem_read); + /* Read the data and advance read pointer */ + data = b->buffer[b->readpos]; + b->readpos++; + if (b->readpos >= BUFFER_SIZE) + b->readpos = 0; + /* Signal that the buffer has now one more location for writing */ + sem_post (&b->sem_write); + return data; +} + +/* A test program: one thread inserts integers from 1 to 10000, + the other reads them and prints them. */ + +#define OVER (-1) + +struct prodcons buffer; + +static void * +producer (void *data) +{ + int n; + for (n = 0; n < 10000; n++) + { + printf ("%d --->\n", n); + put (&buffer, n); + } + put (&buffer, OVER); + return NULL; +} + +static void * +consumer (void *data) +{ + int d; + while (1) + { + d = get (&buffer); + if (d == OVER) + break; + printf ("---> %d\n", d); + } + return NULL; +} + +int +main (void) +{ + pthread_t th_a, th_b; + void *retval; + + init (&buffer); + /* Create the threads */ + pthread_create (&th_a, NULL, producer, 0); + pthread_create (&th_b, NULL, consumer, 0); + /* Wait until producer and consumer finish. */ + pthread_join (th_a, &retval); + pthread_join (th_b, &retval); + return 0; +} diff --git a/linuxthreads/Examples/ex6.c b/linuxthreads/Examples/ex6.c new file mode 100644 index 0000000000..9a0266828b --- /dev/null +++ b/linuxthreads/Examples/ex6.c @@ -0,0 +1,46 @@ +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <pthread.h> +#include <unistd.h> + +static 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); */ + int err = pthread_join (thread, NULL); + if (err != 0) + { + printf ("join failed (%s), count %lu\n", strerror (err), count); + return 2; + } + usleep (10); + } + return 0; +} diff --git a/linuxthreads/Examples/ex7.c b/linuxthreads/Examples/ex7.c new file mode 100644 index 0000000000..d9db33c5cd --- /dev/null +++ b/linuxthreads/Examples/ex7.c @@ -0,0 +1,45 @@ +/* This is a test of the special shutdown that occurs + when all threads, including the main one, call + pthread_exit(). It demonstrates that atexit + handlers are properly called, and that the + output is properly flushed even when stdout is + redirected to a file, and therefore fully buffered. */ + +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> + +#define NTHREADS 20 /* number of threads */ + +static void * +thread (void *arg) +{ + printf ("thread terminating\n"); + return 0; +} + +static void +cleanup (void) +{ + printf ("atexit handler called\n"); +} + +int +main (void) +{ + int i; + + atexit (cleanup); + + for (i = 0; i < NTHREADS; i++) + { + pthread_t id; + if (pthread_create (&id, 0, thread, 0) != 0) + { + fprintf (stderr, "pthread_create failed\n"); + abort (); + } + } + + pthread_exit (0); +} diff --git a/linuxthreads/Examples/ex8.c b/linuxthreads/Examples/ex8.c new file mode 100644 index 0000000000..bda81b9c61 --- /dev/null +++ b/linuxthreads/Examples/ex8.c @@ -0,0 +1,101 @@ +/* Tests for fork in multi-threaded environment. + Copyright (C) 2000 Free Software Foundation, Inc. + Contributed by Ulrich Drepper <drepper@cygnus.com>, 2000. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include <errno.h> +#include <error.h> +#include <stdlib.h> +#include <pthread.h> +#include <unistd.h> +#include <sys/wait.h> + +enum +{ + PREPARE_BIT = 1, + PARENT_BIT = 2, + CHILD_BIT = 4 +}; + +static int var; + +static void +prepare (void) +{ + var |= PREPARE_BIT; +} + +static void +parent (void) +{ + var |= PARENT_BIT; +} + +static void +child (void) +{ + var |= CHILD_BIT; +} + + +static void *thread (void *arg); + + +int +main (void) +{ + pthread_t th; + void *res; + + pthread_atfork (prepare, parent, child); + + if (pthread_create (&th, NULL, thread, NULL) != 0) + error (EXIT_FAILURE, 0, "cannot create thread"); + + pthread_join (th, &res); + + return (int) (long int) res; +} + + +static void * +thread (void *arg) +{ + int status; + pid_t pid; + + pid = fork (); + if (pid == 0) + { + /* We check whether the `prepare' and `child' function ran. */ + exit (var != (PREPARE_BIT | CHILD_BIT)); + } + else if (pid == (pid_t) -1) + error (EXIT_FAILURE, errno, "cannot fork"); + + if (waitpid (pid, &status, 0) != pid) + error (EXIT_FAILURE, errno, "wrong child"); + + if (WTERMSIG (status) != 0) + error (EXIT_FAILURE, 0, "Child terminated incorrectly"); + status = WEXITSTATUS (status); + + if (status == 0) + status = var != (PREPARE_BIT | PARENT_BIT); + + return (void *) (long int) status; +} diff --git a/linuxthreads/Examples/ex9.c b/linuxthreads/Examples/ex9.c new file mode 100644 index 0000000000..3c8b8142e1 --- /dev/null +++ b/linuxthreads/Examples/ex9.c @@ -0,0 +1,98 @@ +/* Tests for pthread_barrier_* functions. + Copyright (C) 2000, 2001, 2002 Free Software Foundation, Inc. + Contributed by Kaz Kylheku <kaz@ashi.footprints.net>, 2000. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include <errno.h> +#include <error.h> +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> + +#define NUM_THREADS 10 +#define NUM_ITERS 500 + +static void *thread (void *) __attribute__ ((__noreturn__)); +static pthread_barrier_t barrier; + +int +main (void) +{ + pthread_t thread_list[NUM_THREADS]; + int i; + + if (pthread_barrier_init (&barrier, NULL, NUM_THREADS + 1) != 0) + error (EXIT_FAILURE, 0, "cannot initialize barrier"); + + for (i = 0; i < NUM_THREADS; i++) + { + if (pthread_create (&thread_list[i], NULL, thread, NULL) != 0) + error (EXIT_FAILURE, 0, "cannot create thread"); + } + + (void) thread (NULL); + + for (i = 0; i < NUM_THREADS; i++) + { + pthread_join(thread_list[i], NULL); + } + + return 0; +} + + +static void * +thread (void *arg) +{ + int i; + pthread_t self = pthread_self (); + static pthread_t last_serial_thread; + static int linecount; /* protected by flockfile(stdout) */ + + for (i = 0; i < NUM_ITERS; i++) + { + switch (pthread_barrier_wait (&barrier)) + { + case 0: + flockfile (stdout); + printf ("%04d: non-serial thread %lu\n", ++linecount, + (unsigned long) self); + funlockfile (stdout); + break; + case PTHREAD_BARRIER_SERIAL_THREAD: + flockfile (stdout); + printf ("%04d: serial thread %lu\n", ++linecount, + (unsigned long) self); + funlockfile (stdout); + last_serial_thread = self; + break; + default: + /* Huh? */ + error (EXIT_FAILURE, 0, "unexpected return value from barrier wait"); + } + } + + if (pthread_equal (self, last_serial_thread)) + { + flockfile (stdout); + printf ("%04d: last serial thread %lu terminating process\n", + ++linecount, (unsigned long) self); + funlockfile (stdout); + } + + pthread_exit(NULL); +} diff --git a/linuxthreads/Examples/tststatic.c b/linuxthreads/Examples/tststatic.c new file mode 100644 index 0000000000..421011a3c4 --- /dev/null +++ b/linuxthreads/Examples/tststatic.c @@ -0,0 +1 @@ +#include "ex1.c" |