1/** Trigger two kinds of errors: once that condition variable s_cond is
2 *  associated with two different mutexes (s_mutex1 and s_mutex2), and two
3 *  times that pthread_cond_signal() is called without that the mutex
4 *  associated with the condition variable is locked.
5 */
6
7
8#include <errno.h>     // ETIMEDOUT
9#include <pthread.h>
10#include <semaphore.h>
11#include <stdio.h>
12#include <stdlib.h>    // malloc()
13#include <string.h>    // memset()
14#include <sys/time.h>  // gettimeofday()
15#include <time.h>      // struct timespec
16#include <fcntl.h>     // O_CREAT
17#include <unistd.h>
18#include "../../config.h"
19
20
21#define PTH_CALL(expr)                                  \
22  do                                                    \
23  {                                                     \
24    int err = (expr);                                   \
25    if (! s_quiet && err)				\
26    {                                                   \
27      fprintf(stderr,                                   \
28              "%s:%d %s returned error code %d (%s)\n", \
29              __FILE__,                                 \
30              __LINE__,                                 \
31              #expr,                                    \
32              err,                                      \
33              strerror(err));                           \
34    }                                                   \
35  } while (0)
36
37
38static pthread_cond_t  s_cond;
39static pthread_mutex_t s_mutex1;
40static pthread_mutex_t s_mutex2;
41static sem_t*          s_sem;
42static int             s_quiet;
43
44
45static sem_t* create_semaphore(const char* const name)
46{
47#ifdef VGO_darwin
48  char name_and_pid[32];
49  snprintf(name_and_pid, sizeof(name_and_pid), "%s-%d", name, getpid());
50  sem_t* p = sem_open(name_and_pid, O_CREAT | O_EXCL, 0600, 0);
51  if (p == SEM_FAILED) {
52    perror("sem_open");
53    return NULL;
54  }
55  return p;
56#else
57  sem_t* p = malloc(sizeof(*p));
58  if (p)
59    sem_init(p, 0, 0);
60  return p;
61#endif
62}
63
64static void destroy_semaphore(const char* const name, sem_t* p)
65{
66#ifdef VGO_darwin
67  sem_close(p);
68  sem_unlink(name);
69#else
70  sem_destroy(p);
71  free(p);
72#endif
73}
74
75static void* thread_func(void* mutex)
76{
77  struct timeval now;
78  struct timespec deadline;
79
80  PTH_CALL(pthread_mutex_lock(mutex));
81  sem_post(s_sem);
82  gettimeofday(&now, 0);
83  memset(&deadline, 0, sizeof(deadline));
84  deadline.tv_sec  = now.tv_sec + 2;
85  deadline.tv_nsec = now.tv_usec * 1000;
86  PTH_CALL(pthread_cond_timedwait(&s_cond, mutex, &deadline));
87  PTH_CALL(pthread_mutex_unlock(mutex));
88  return 0;
89}
90
91int main(int argc, char** argv)
92{
93  char semaphore_name[32];
94  int optchar;
95  pthread_t tid1;
96  pthread_t tid2;
97
98  while ((optchar = getopt(argc, argv, "q")) != EOF)
99  {
100    switch (optchar)
101    {
102    case 'q': s_quiet = 1; break;
103    default:
104      fprintf(stderr, "Error: unknown option '%c'.\n", optchar);
105      return 1;
106    }
107  }
108
109  /* Initialize synchronization objects. */
110  snprintf(semaphore_name, sizeof(semaphore_name), "semaphore-%d", getpid());
111  s_sem = create_semaphore(semaphore_name);
112  PTH_CALL(pthread_cond_init(&s_cond, 0));
113  PTH_CALL(pthread_mutex_init(&s_mutex1, 0));
114  PTH_CALL(pthread_mutex_init(&s_mutex2, 0));
115
116  /* Create two threads. */
117  PTH_CALL(pthread_create(&tid1, 0, &thread_func, &s_mutex1));
118  PTH_CALL(pthread_create(&tid2, 0, &thread_func, &s_mutex2));
119
120  /* Wait until both threads have called sem_post(). */
121  sem_wait(s_sem);
122  sem_wait(s_sem);
123  destroy_semaphore(semaphore_name, s_sem);
124  s_sem = 0;
125
126  /* Wait until both threads are waiting inside pthread_cond_wait(). */
127  PTH_CALL(pthread_mutex_lock(&s_mutex1));
128  PTH_CALL(pthread_mutex_lock(&s_mutex2));
129  PTH_CALL(pthread_mutex_unlock(&s_mutex2));
130  PTH_CALL(pthread_mutex_unlock(&s_mutex1));
131
132  /* Signal s_cond twice. */
133  PTH_CALL(pthread_cond_signal(&s_cond));
134  PTH_CALL(pthread_cond_signal(&s_cond));
135
136  /* Join both threads. */
137  PTH_CALL(pthread_join(tid1, 0));
138  PTH_CALL(pthread_join(tid2, 0));
139
140  return 0;
141}
142