Using 32-bit cygwin that I set up yesterday. I found that a call to clock_gettime(CLOCK_MONOTONIC, ..) has a one-time initialization that is not thread-safe. If two threads call this at the same time, they will race. The results I am seeing are typically that one of the two callers get a timespec structure with zero values, and no error return code from the call.
I prepared a small test that exposes this issue. Given it is a race condition, you have to run the test in a loop for it to happen. I have attached the Makefile and source code. If you run "make test" it will expose the issue: $ make test cc -c -o cyg_hires_clock_race.o cyg_hires_clock_race.c cc cyg_hires_clock_race.o -o cyg_hires_clock_race ERROR: one of the timespec structures was zero: main thread: tv_sec = 356242 tv_nsec = 376075900 2nd thread: tv_sec = 0 tv_nsec = 0 ERROR: one of the timespec structures was zero: main thread: tv_sec = 356242 tv_nsec = 519016800 2nd thread: tv_sec = 0 tv_nsec = 0 ERROR: one of the timespec structures was zero: main thread: tv_sec = 356242 tv_nsec = 734794100 2nd thread: tv_sec = 0 tv_nsec = 0 ERROR: one of the timespec structures was zero: main thread: tv_sec = 356243 tv_nsec = 463632400 2nd thread: tv_sec = 0 tv_nsec = 0 make: *** [Makefile:6: test] Error 1 Relevant cygwin version information: Cygwin DLL version info: DLL version: 2.11.2 DLL epoch: 19 DLL old termios: 5 DLL malloc env: 28 Cygwin conv: 181 API major: 0 API minor: 329 Shared data: 5 DLL identifier: cygwin1 Mount registry: 3 Cygwin registry name: Cygwin Installations name: Installations Cygdrive default prefix: Build date: Shared id: cygwin1S5
/* * Copyright (C) 2018 James E. King III * * Exposes a race condition in hires clock initialization * If two threads call ::clock_gettime(CLOCK_MONOTONIC) they * will race to perform one-time global initialization. * During this race it is possible to see the timespec filled * in by ::clock_gettime can have tv_sec == 0 && tv_nsec == 0 * * As this is a race you have to run the test in a loop to catch it. */ #include <assert.h> #include <pthread.h> #include <stdio.h> #include <time.h> void *fill_ts(void *vts) { struct timespec *pts = (struct timespec *)vts; assert(!clock_gettime(CLOCK_MONOTONIC, pts)); return NULL; } int main() { struct timespec main_ts; struct timespec thread_ts; pthread_t thr; assert(!pthread_create(&thr, NULL, &fill_ts, &thread_ts)); (void)fill_ts(&main_ts); assert(!pthread_join(thr, NULL)); int failed = (( main_ts.tv_sec == 0 && main_ts.tv_nsec == 0) || (thread_ts.tv_sec == 0 && thread_ts.tv_nsec == 0)); if (failed) { fprintf(stderr, "ERROR: one of the timespec structures was zero:\n"); fprintf(stderr, "main thread: tv_sec = %10u tv_nsec = %10u\n", main_ts.tv_sec, main_ts.tv_nsec); fprintf(stderr, " 2nd thread: tv_sec = %10u tv_nsec = %10u\n", thread_ts.tv_sec, thread_ts.tv_nsec); return 1; } return 0; }
Makefile
Description: Binary data
-- 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