| /** Trigger two kinds of errors: once that condition variable s_cond is |
| * associated with two different mutexes (s_mutex1 and s_mutex2), and two |
| * times that pthread_cond_signal() is called without that the mutex |
| * associated with the condition variable is locked. |
| */ |
| |
| |
| #include <errno.h> // ETIMEDOUT |
| #include <pthread.h> |
| #include <semaphore.h> |
| #include <stdio.h> |
| #include <stdlib.h> // malloc() |
| #include <string.h> // memset() |
| #include <sys/time.h> // gettimeofday() |
| #include <time.h> // struct timespec |
| #include <fcntl.h> // O_CREAT |
| #include <unistd.h> |
| #include "../../config.h" |
| |
| |
| #define PTH_CALL(expr) \ |
| do \ |
| { \ |
| int err = (expr); \ |
| if (! s_quiet && err) \ |
| { \ |
| fprintf(stderr, \ |
| "%s:%d %s returned error code %d (%s)\n", \ |
| __FILE__, \ |
| __LINE__, \ |
| #expr, \ |
| err, \ |
| strerror(err)); \ |
| } \ |
| } while (0) |
| |
| |
| static pthread_cond_t s_cond; |
| static pthread_mutex_t s_mutex1; |
| static pthread_mutex_t s_mutex2; |
| static sem_t* s_sem; |
| static int s_quiet; |
| |
| |
| static sem_t* create_semaphore(const char* const name) |
| { |
| #ifdef VGO_darwin |
| char name_and_pid[32]; |
| snprintf(name_and_pid, sizeof(name_and_pid), "%s-%d", name, getpid()); |
| sem_t* p = sem_open(name_and_pid, O_CREAT | O_EXCL, 0600, 0); |
| if (p == SEM_FAILED) { |
| perror("sem_open"); |
| return NULL; |
| } |
| return p; |
| #else |
| sem_t* p = malloc(sizeof(*p)); |
| if (p) |
| sem_init(p, 0, 0); |
| return p; |
| #endif |
| } |
| |
| static void destroy_semaphore(const char* const name, sem_t* p) |
| { |
| #ifdef VGO_darwin |
| sem_close(p); |
| sem_unlink(name); |
| #else |
| sem_destroy(p); |
| free(p); |
| #endif |
| } |
| |
| static void* thread_func(void* mutex) |
| { |
| struct timeval now; |
| struct timespec deadline; |
| |
| PTH_CALL(pthread_mutex_lock(mutex)); |
| sem_post(s_sem); |
| gettimeofday(&now, 0); |
| memset(&deadline, 0, sizeof(deadline)); |
| deadline.tv_sec = now.tv_sec + 2; |
| deadline.tv_nsec = now.tv_usec * 1000; |
| PTH_CALL(pthread_cond_timedwait(&s_cond, mutex, &deadline)); |
| PTH_CALL(pthread_mutex_unlock(mutex)); |
| return 0; |
| } |
| |
| int main(int argc, char** argv) |
| { |
| char semaphore_name[32]; |
| int optchar; |
| pthread_t tid1; |
| pthread_t tid2; |
| |
| while ((optchar = getopt(argc, argv, "q")) != EOF) |
| { |
| switch (optchar) |
| { |
| case 'q': s_quiet = 1; break; |
| default: |
| fprintf(stderr, "Error: unknown option '%c'.\n", optchar); |
| return 1; |
| } |
| } |
| |
| /* Initialize synchronization objects. */ |
| snprintf(semaphore_name, sizeof(semaphore_name), "semaphore-%ld", |
| (long) getpid()); |
| s_sem = create_semaphore(semaphore_name); |
| PTH_CALL(pthread_cond_init(&s_cond, 0)); |
| PTH_CALL(pthread_mutex_init(&s_mutex1, 0)); |
| PTH_CALL(pthread_mutex_init(&s_mutex2, 0)); |
| |
| /* Create two threads. */ |
| PTH_CALL(pthread_create(&tid1, 0, &thread_func, &s_mutex1)); |
| PTH_CALL(pthread_create(&tid2, 0, &thread_func, &s_mutex2)); |
| |
| /* Wait until both threads have called sem_post(). */ |
| sem_wait(s_sem); |
| sem_wait(s_sem); |
| destroy_semaphore(semaphore_name, s_sem); |
| s_sem = 0; |
| |
| /* Wait until both threads are waiting inside pthread_cond_wait(). */ |
| PTH_CALL(pthread_mutex_lock(&s_mutex1)); |
| PTH_CALL(pthread_mutex_lock(&s_mutex2)); |
| PTH_CALL(pthread_mutex_unlock(&s_mutex2)); |
| PTH_CALL(pthread_mutex_unlock(&s_mutex1)); |
| |
| /* Signal s_cond twice. */ |
| PTH_CALL(pthread_cond_signal(&s_cond)); |
| PTH_CALL(pthread_cond_signal(&s_cond)); |
| |
| /* Join both threads. */ |
| PTH_CALL(pthread_join(tid1, 0)); |
| PTH_CALL(pthread_join(tid2, 0)); |
| |
| return 0; |
| } |