Hi, The attached test program for pthread_once uses the following basic POSIX threads functions: pthread_create pthread_join pthread_mutex_init pthread_mutex_lock pthread_mutex_unlock pthread_once pthread_rwlock_init pthread_rwlock_rdlock pthread_rwlock_unlock pthread_rwlock_wrlock
On Linux with glibc 2.8: $ gcc bug.c -O -Wall -lpthread -o bug $ ./bug Starting test_once ... OK The test completes in about 4 seconds. On Cygwin 1.5.25(0.156/4/2): $ gcc bug.c -O -Wall -o bug.exe $ ./bug.exe Starting test_once ... Either it does not terminates (still running after half an hour, eating 100% CPU time on one of the two CPUs), or it crashes rather quickly: $ ./bug.exe Starting test_once ...Segmentation fault (core dumped) This is on a machine with 2 virtual processors: $ cat /proc/cpuinfo processor : 0 vendor_id : GenuineIntel type : primary processor cpu family : 6 model : 12 model name : Intel(R) Atom(TM) CPU N270 @ 1.60GHz stepping : 2 brand id : 0 cpu count : 2 apic id : 0 cpu MHz : 1600 fpu : yes flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat clfl dtes acpi mmx fxsr sse sse2 ss htt tmi pbe pni monitor ds_cpl tm2 est processor : 1 vendor_id : GenuineIntel type : primary processor cpu family : 6 model : 12 model name : Intel(R) Atom(TM) CPU N270 @ 1.60GHz stepping : 2 brand id : 0 cpu count : 2 apic id : 1 cpu MHz : 1599 fpu : yes flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat clfl dtes acpi mmx fxsr sse sse2 ss htt tmi pbe pni monitor ds_cpl tm2 est You can see what the program is doing before it crahes or while it loops, by setting #define ENABLE_DEBUGGING 1 in line 25. I have other test programs which exercise pthread_mutex_*, pthread_rwlock_*, and pthread_join. These work fine. My suspicion therefore lies on pthread_once. Bruno
/* Enable GNU extensions on systems that have them. */ #ifndef _GNU_SOURCE # define _GNU_SOURCE 1 #endif /* Enable threading extensions on Solaris. */ #ifndef _POSIX_PTHREAD_SEMANTICS # define _POSIX_PTHREAD_SEMANTICS 1 #endif /* Enable general extensions on Solaris. */ #ifndef __EXTENSIONS__ # define __EXTENSIONS__ 1 #endif /* Whether to enable locking. Uncomment this to get a test program without locking, to verify that it crashes. */ #define ENABLE_LOCKING 1 /* Whether to help the scheduler through explicit yield(). Uncomment this to see if the operating system has a fair scheduler. */ #define EXPLICIT_YIELD 1 /* Whether to print debugging messages. */ #define ENABLE_DEBUGGING 0 /* Number of simultaneous threads. */ #define THREAD_COUNT 10 /* Number of operations performed in each thread. This is quite high, because with a smaller count, say 5000, we often get an "OK" result even without ENABLE_LOCKING (on Linux/x86). */ #define REPEAT_COUNT 50000 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <stdlib.h> /* Use the POSIX threads library. */ # include <pthread.h> #if ENABLE_DEBUGGING # define dbgprintf printf #else # define dbgprintf if (0) printf #endif # include <pthread.h> # include <sched.h> #if EXPLICIT_YIELD # define yield() sched_yield () #else # define yield() #endif /* Test once-only execution by having several threads attempt to grab a once-only task simultaneously (triggered by releasing a read-write lock). */ static pthread_once_t fresh_once = PTHREAD_ONCE_INIT; static int ready[THREAD_COUNT]; static pthread_mutex_t ready_lock[THREAD_COUNT]; #if ENABLE_LOCKING static pthread_rwlock_t fire_signal[REPEAT_COUNT]; #else static volatile int fire_signal_state; #endif static pthread_once_t once_control; static int performed; static pthread_mutex_t performed_lock = PTHREAD_MUTEX_INITIALIZER; static void once_execute (void) { if (pthread_mutex_lock (&performed_lock)) abort (); performed++; if (pthread_mutex_unlock (&performed_lock)) abort (); } static void * once_contender_thread (void *arg) { int id = (int) (long) arg; int repeat; for (repeat = 0; repeat <= REPEAT_COUNT; repeat++) { /* Tell the main thread that we're ready. */ if (pthread_mutex_lock (&ready_lock[id])) abort (); ready[id] = 1; if (pthread_mutex_unlock (&ready_lock[id])) abort (); if (repeat == REPEAT_COUNT) break; dbgprintf ("Contender %p waiting for signal for round %d\n", (void *) pthread_self (), repeat); #if ENABLE_LOCKING /* Wait for the signal to go. */ if (pthread_rwlock_rdlock (&fire_signal[repeat])) abort (); /* And don't hinder the others (if the scheduler is unfair). */ if (pthread_rwlock_unlock (&fire_signal[repeat])) abort (); #else /* Wait for the signal to go. */ while (fire_signal_state <= repeat) yield (); #endif dbgprintf ("Contender %p got the signal for round %d\n", (void *) pthread_self (), repeat); /* Contend for execution. */ pthread_once (&once_control, once_execute); } return NULL; } void test_once (void) { int i, repeat; pthread_t threads[THREAD_COUNT]; /* Initialize all variables. */ for (i = 0; i < THREAD_COUNT; i++) { ready[i] = 0; if (pthread_mutex_init (&ready_lock[i], NULL)) abort (); } #if ENABLE_LOCKING for (i = 0; i < REPEAT_COUNT; i++) if (pthread_rwlock_init (&fire_signal[i], NULL)) abort (); #else fire_signal_state = 0; #endif /* Block all fire_signals. */ for (i = REPEAT_COUNT-1; i >= 0; i--) if (pthread_rwlock_wrlock (&fire_signal[i])) abort (); /* Spawn the threads. */ for (i = 0; i < THREAD_COUNT; i++) if (pthread_create (&threads[i], NULL, once_contender_thread, (void *) (long) i) != 0) abort (); for (repeat = 0; repeat <= REPEAT_COUNT; repeat++) { /* Wait until every thread is ready. */ dbgprintf ("Main thread before synchonizing for round %d\n", repeat); for (;;) { int ready_count = 0; for (i = 0; i < THREAD_COUNT; i++) { if (pthread_mutex_lock (&ready_lock[i])) abort (); ready_count += ready[i]; if (pthread_mutex_unlock (&ready_lock[i])) abort (); } if (ready_count == THREAD_COUNT) break; yield (); } dbgprintf ("Main thread after synchonizing for round %d\n", repeat); if (repeat > 0) { /* Check that exactly one thread executed the once_execute() function. */ if (performed != 1) abort (); } if (repeat == REPEAT_COUNT) break; /* Preparation for the next round: Initialize once_control. */ memcpy (&once_control, &fresh_once, sizeof (pthread_once_t)); /* Preparation for the next round: Reset the performed counter. */ performed = 0; /* Preparation for the next round: Reset the ready flags. */ for (i = 0; i < THREAD_COUNT; i++) { if (pthread_mutex_lock (&ready_lock[i])) abort (); ready[i] = 0; if (pthread_mutex_unlock (&ready_lock[i])) abort (); } /* Signal all threads simultaneously. */ dbgprintf ("Main thread giving signal for round %d\n", repeat); #if ENABLE_LOCKING if (pthread_rwlock_unlock (&fire_signal[repeat])) abort (); #else fire_signal_state = repeat + 1; #endif } /* Wait for the threads to terminate. */ for (i = 0; i < THREAD_COUNT; i++) { void *retval; if (pthread_join (threads[i], &retval) != 0) abort (); } } int main () { printf ("Starting test_once ..."); fflush (stdout); test_once (); printf (" OK\n"); fflush (stdout); return 0; }
-- Problem reports: http://cygwin.com/problems.html FAQ: http://cygwin.com/faq/ Documentation: http://cygwin.com/docs.html Unsubscribe info: http://cygwin.com/ml/#unsubscribe-simple