1/** Broadcast a (POSIX threads) signal to all running threads, where the
2 *  number of threads can be specified on the command line. This test program
3 *  is intended not only to test the correctness of drd but also to test
4 *  whether performance does not degrade too much when the number of threads
5 *  increases.
6 */
7
8
9#include <assert.h>
10#include <pthread.h>
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#include <unistd.h>
15
16
17// Counting semaphore.
18
19struct csema
20{
21  pthread_mutex_t  m_mutex;
22  pthread_cond_t   m_cond;
23  int              m_count;
24};
25
26void csema_ctr(struct csema* p)
27{
28  memset(p, 0, sizeof(*p));
29  pthread_mutex_init(&p->m_mutex, 0);
30  pthread_cond_init(&p->m_cond, 0);
31}
32
33void csema_dtr(struct csema* p)
34{
35  pthread_cond_destroy(&p->m_cond);
36  pthread_mutex_destroy(&p->m_mutex);
37}
38
39void csema_p(struct csema* p, const int n)
40{
41  pthread_mutex_lock(&p->m_mutex);
42  while (p->m_count < n)
43    pthread_cond_wait(&p->m_cond, &p->m_mutex);
44  p->m_count -= n;
45  pthread_cond_signal(&p->m_cond);
46  pthread_mutex_unlock(&p->m_mutex);
47}
48
49void csema_v(struct csema* p)
50{
51  pthread_mutex_lock(&p->m_mutex);
52  p->m_count++;
53  pthread_cond_signal(&p->m_cond);
54  pthread_mutex_unlock(&p->m_mutex);
55}
56
57
58struct cthread
59{
60  pthread_t     m_thread;
61  int           m_threadnum;
62  struct csema* m_sema;
63};
64
65void cthread_ctr(struct cthread* p)
66{
67  p->m_thread = 0;
68  p->m_sema   = 0;
69}
70
71void cthread_dtr(struct cthread* p)
72{ }
73
74
75// Local variables.
76
77static int s_debug = 0;
78static int s_trace = 0;
79static int s_signal_count;
80static pthread_mutex_t s_mutex;
81static pthread_cond_t  s_cond;
82
83
84// Function definitions.
85
86static void thread_func(struct cthread* thread_info)
87{
88  int i;
89
90  pthread_mutex_lock(&s_mutex);
91
92  for (i = 0; i < s_signal_count; i++)
93  {
94    if (s_trace)
95    {
96      printf("thread %d [%d] (1)\n", thread_info->m_threadnum, i);
97    }
98    csema_v(thread_info->m_sema);
99
100    // Wait until the main thread signals us via pthread_cond_broadcast().
101    pthread_cond_wait(&s_cond, &s_mutex);
102    if (s_trace)
103    {
104      printf("thread %d [%d] (2)\n", thread_info->m_threadnum, i);
105    }
106  }
107
108  pthread_mutex_unlock(&s_mutex);
109}
110
111int main(int argc, char** argv)
112{
113  int optchar;
114  int thread_count;
115
116  while ((optchar = getopt(argc, argv, "d")) != EOF)
117  {
118    switch (optchar)
119    {
120    case 'd':
121      s_debug = 1;
122      break;
123    default:
124      assert(0);
125      break;
126    }
127  }
128
129  /* This test should complete in 15s or less. If the test does not complete */
130  /* within that time, abort the test via the signal SIGALRM.                */
131  alarm(100);
132
133  s_signal_count = argc > optind ? atoi(argv[optind]) : 10;
134  thread_count = argc > optind + 1 ? atoi(argv[optind + 1]) : 10;
135
136  if (s_debug)
137    printf("&s_cond = %p\n", &s_cond);
138
139  pthread_mutex_init(&s_mutex, 0);
140  pthread_cond_init(&s_cond, 0);
141  {
142    int i;
143    struct csema sema;
144    struct cthread* p;
145    struct cthread* thread_vec;
146
147    csema_ctr(&sema);
148    thread_vec = malloc(sizeof(struct cthread) * thread_count);
149    for (p = thread_vec; p != thread_vec + thread_count; p++)
150    {
151      cthread_ctr(p);
152      p->m_threadnum = p - thread_vec;
153      p->m_sema = &sema;
154      pthread_create(&p->m_thread, 0,
155                     (void*(*)(void*))thread_func, &*p);
156    }
157    for (i = 0; i < s_signal_count; i++)
158    {
159      if (s_trace)
160        printf("main [%d] (1)\n", i);
161      csema_p(&sema, thread_count);
162      if (s_trace)
163        printf("main [%d] (2)\n", i);
164      pthread_mutex_lock(&s_mutex);
165      pthread_cond_broadcast(&s_cond);
166      pthread_mutex_unlock(&s_mutex);
167      if (s_trace)
168        printf("main [%d] (3)\n", i);
169    }
170    for (i = 0; i < thread_count; i++)
171    {
172      pthread_join(thread_vec[i].m_thread, 0);
173      cthread_dtr(&thread_vec[i]);
174    }
175    free(thread_vec);
176    csema_dtr(&sema);
177  }
178  pthread_cond_destroy(&s_cond);
179  pthread_mutex_destroy(&s_mutex);
180
181  fprintf(stderr, "Done.\n");
182
183  return 0;
184}
185