/* * This test demonstrates FreeBSDs non-standard return codes from * POSIX threads routines. The function pthread_cond_timedwait() * should return the error code (errno) and not a failure code (-1). * In order to obtain the error code in FreeBSD, it is necessary * to read it from the thread-safe errno. */ #include #include #include #include #include #include #include #define NUM_THREADS 10 typedef struct { pthread_mutex_t *m; pthread_cond_t *c; int lock_count; int secs; int id; } thread_wait_t; pthread_mutex_t big_lock; volatile int protected_counter = 0; static int signal_received = 0; static void handle_signal (int sig, int code, struct sigcontext *scp) { printf ("Caught signal %d, code %d\n", sig, code); signal_received ++; if (signal_received > 3) exit (1); } static void * timed_wait (void *arg) { thread_wait_t *w = (thread_wait_t *) arg; struct timespec ts; int result = 0; int status = 0; struct sigaction newact; printf (" Thread %d running\n", w->id); newact.sa_handler = (void (*)())handle_signal; sigfillset (&newact.sa_mask); newact.sa_flags = SA_ONSTACK | SA_NODEFER; if ((sigaction (SIGSEGV, &newact, NULL) != 0) || (sigaction (SIGBUS, &newact, NULL) != 0)) { printf ("Unable to register signal handler, errno %d.\n", errno); exit (1); } /* * Take the big lock. */ if (pthread_mutex_lock (&big_lock) != 0) { printf ("Thread %d: unable to acquire big lock.\n", w->id); status = -1; pthread_exit ((void *) status); } protected_counter++; if (protected_counter != 1) printf ("Thread %d: Houston, we have a problem.\n", w->id); else printf ("Thread %d: got the big lock.\n", w->id); /* * Release the big lock. */ if (protected_counter != 1) printf ("Thread %d: Houston, we have a problem.\n", w->id); else printf ("Thread %d: releasing big lock.\n", w->id); protected_counter--; pthread_mutex_unlock (&big_lock); /* * Take the mutex for the condition variable. */ if (pthread_mutex_lock (w->m) != 0) { printf ("Thread %d: unable to acquire thread-specific mutex.\n", w->id); status = -1; pthread_exit ((void *) status); } if (w->lock_count != 0) printf ("Thread %d: acquired thread-specific mutex recursive!\n", w->id); /* * Wait for the signal. */ clock_gettime (CLOCK_REALTIME, &ts); ts.tv_sec = ts.tv_sec + w->secs; result = pthread_cond_timedwait (w->c, w->m, &ts); if (result != 0) printf ("Thread %d: timedout in condition timed wait, " "result %d, errno %d.\n", w->id, result, errno); else printf ("Thread %d: received condition variable signal.\n", w->id); pthread_mutex_unlock (w->m); pthread_exit ((void *) status); return ((void *) status); } int main (int argc, char *argv[]) { pthread_mutexattr_t mattr; pthread_condattr_t cattr; pthread_attr_t pattr; pthread_mutex_t m[NUM_THREADS]; pthread_cond_t c[NUM_THREADS]; pthread_t t[NUM_THREADS]; thread_wait_t w[NUM_THREADS]; int i, status; int ret; /* Set our own priority low. */ pthread_setprio (pthread_self (), 0); /* * Initialize the mutex and condition variable attributes. */ if ((pthread_mutexattr_init (&mattr) != 0) || (pthread_condattr_init (&cattr) != 0)) { printf ("Unable to initialize mutex attribute.\n"); exit (1); } /* Initialize the big lock. */ if (pthread_mutex_init (&big_lock, &mattr) != 0) { printf ("Unable to initialize big lock.\n"); exit (1); } /* * Initialize the mutexes and condition variables. */ for (i = 0; i < NUM_THREADS; i = i + 1) { if ((pthread_mutex_init (&m[i], &mattr) != 0) || (pthread_cond_init (&c[i], &cattr) != 0)) { printf ("Unable to initialize mutex.\n"); exit (1); } } /* * Initialize the thread wait structures so that each thread * will wait on separate condition variables for i seconds, * where i is the thread number (starting at 0). */ for (i = 0; i < NUM_THREADS; i = i + 1) { w[i].m = &m[i]; w[i].c = &c[i]; w[i].id = i; w[i].secs = i; w[i].lock_count = 1; } /* * Create the threads. */ if ((pthread_attr_init (&pattr) != 0) || (pthread_attr_setdetachstate (&pattr, PTHREAD_CREATE_JOINABLE) != 0) || (pthread_attr_setscope(&pattr, PTHREAD_SCOPE_SYSTEM) != 0)) { printf ("Unable to initialize thread attributes.\n"); exit (1); } for (i = 0; i < NUM_THREADS; i = i + 1) { /* Hold the thread about to be created in place. */ pthread_mutex_lock (&big_lock); protected_counter++; if ((ret = pthread_create (&t[i], &pattr, timed_wait, (void *) &w[i])) != 0) { printf ("Unable to create thread, ret %d, errno %d.\n", ret, errno); exit (1); } pthread_setprio (t[i], 126 - (NUM_THREADS - i)); pthread_yield (); printf ("Letting task %d go.\n", i); w[i].lock_count = 0; protected_counter--; pthread_mutex_unlock (&big_lock); /* let the task go */ } /* * Wait for all threads to finish. */ sleep (5); for (i = 0; i < NUM_THREADS; i = i + 1) { pthread_join (t[i], (void **) &status); printf ("Thread %d completed, exit status %d.\n", i, status); } return (0); }