1ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/* Test program that performs producer-consumer style communication through
2ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown * a circular buffer. This test program is a slightly modified version of the
3ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown * test program made available by Miguel Ojeda
4ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown * -- see also http://article.gmane.org/gmane.comp.debugging.valgrind/8782.
5ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown */
6ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
7ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
8ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include <stdio.h>
9ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include <string.h>
10ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include <stdlib.h>
11ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include <unistd.h>
12ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include <time.h>
13ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include <pthread.h>
14ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include <semaphore.h>
15ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include <fcntl.h>
16ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include "../../config.h"
17ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
18ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
19ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/** gcc versions 4.1.0 and later have support for atomic builtins. */
20ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
21ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#ifndef HAVE_BUILTIN_ATOMIC
22ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#error Sorry, but this test program can only be compiled by a compiler that\
23ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownhas built-in functions for atomic memory access.
24ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#endif
25ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
26ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
27ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#define BUFFER_MAX (2)
28ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#define DATA_SEMAPHORE_NAME "cb-data-semaphore"
29ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#define FREE_SEMAPHORE_NAME "cb-free-semaphore"
30ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
31ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
32ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Browntypedef int data_t;
33ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
34ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Browntypedef struct {
35ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  /* Counting semaphore representing the number of data items in the buffer. */
36ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  sem_t* data;
37ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  /* Counting semaphore representing the number of free elements. */
38ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  sem_t* free;
39ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  /* Position where a new elements should be written. */
40ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  int in;
41ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  /* Position from where an element can be removed. */
42ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  int out;
43ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  /* Mutex that protects 'in'. */
44ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  pthread_mutex_t mutex_in;
45ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  /* Mutex that protects 'out'. */
46ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  pthread_mutex_t mutex_out;
47ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  /* Data buffer. */
48ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  data_t buffer[BUFFER_MAX];
49ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown} buffer_t;
50ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
51ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic int quiet = 0;
52ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic int use_locking = 1;
53ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
54ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic __inline__
55ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownint fetch_and_add(int* p, int i)
56ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
57ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  return __sync_fetch_and_add(p, i);
58ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
59ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
60ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic sem_t* create_semaphore(const char* const name, const int value)
61ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
62b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov#ifdef VGO_darwin
63b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov  char name_and_pid[32];
64b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov  snprintf(name_and_pid, sizeof(name_and_pid), "%s-%d", name, getpid());
65b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov  sem_t* p = sem_open(name_and_pid, O_CREAT | O_EXCL, 0600, value);
66b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov  if (p == SEM_FAILED) {
67b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov    perror("sem_open");
68b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov    return NULL;
69b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov  }
70ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  return p;
71ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#else
72ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  sem_t* p = malloc(sizeof(*p));
73ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  if (p)
74ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    sem_init(p, 0, value);
75ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  return p;
76ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#endif
77ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
78ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
79ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic void destroy_semaphore(const char* const name, sem_t* p)
80ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
81b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov#ifdef VGO_darwin
82ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  sem_close(p);
83ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  sem_unlink(name);
84ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#else
85ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  sem_destroy(p);
86ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  free(p);
87ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#endif
88ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
89ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
90ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic void buffer_init(buffer_t * b)
91ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
92ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  b->data = create_semaphore(DATA_SEMAPHORE_NAME, 0);
93ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  b->free = create_semaphore(FREE_SEMAPHORE_NAME, BUFFER_MAX);
94ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
95ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  pthread_mutex_init(&b->mutex_in, NULL);
96ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  pthread_mutex_init(&b->mutex_out, NULL);
97ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
98ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  b->in = 0;
99ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  b->out = 0;
100ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
101ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
102ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic void buffer_recv(buffer_t* b, data_t* d)
103ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
104ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  int out;
105ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  sem_wait(b->data);
106ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  if (use_locking)
107ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    pthread_mutex_lock(&b->mutex_out);
108ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  out = fetch_and_add(&b->out, 1);
109ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  if (out >= BUFFER_MAX)
110ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  {
111ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    fetch_and_add(&b->out, -BUFFER_MAX);
112ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    out -= BUFFER_MAX;
113ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  }
114ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  *d = b->buffer[out];
115ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  if (use_locking)
116ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    pthread_mutex_unlock(&b->mutex_out);
117ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  if (! quiet)
118ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  {
119ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    printf("received %d from buffer[%d]\n", *d, out);
120ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    fflush(stdout);
121ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  }
122ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  sem_post(b->free);
123ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
124ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
125ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic void buffer_send(buffer_t* b, data_t* d)
126ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
127ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  int in;
128ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  sem_wait(b->free);
129ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  if (use_locking)
130ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    pthread_mutex_lock(&b->mutex_in);
131ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  in = fetch_and_add(&b->in, 1);
132ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  if (in >= BUFFER_MAX)
133ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  {
134ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    fetch_and_add(&b->in, -BUFFER_MAX);
135ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    in -= BUFFER_MAX;
136ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  }
137ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  b->buffer[in] = *d;
138ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  if (use_locking)
139ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    pthread_mutex_unlock(&b->mutex_in);
140ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  if (! quiet)
141ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  {
142ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    printf("sent %d to buffer[%d]\n", *d, in);
143ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    fflush(stdout);
144ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  }
145ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  sem_post(b->data);
146ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
147ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
148ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic void buffer_destroy(buffer_t* b)
149ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
150ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  destroy_semaphore(DATA_SEMAPHORE_NAME, b->data);
151ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  destroy_semaphore(FREE_SEMAPHORE_NAME, b->free);
152ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
153ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  pthread_mutex_destroy(&b->mutex_in);
154ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  pthread_mutex_destroy(&b->mutex_out);
155ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
156ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
157ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic buffer_t b;
158ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
159ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic void producer(int* id)
160ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
161ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  buffer_send(&b, id);
162ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  pthread_exit(NULL);
163ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
164ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
165ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#define MAXSLEEP (100 * 1000)
166ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
167ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic void consumer(int* id)
168ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
169ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  int d;
170ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  usleep(rand() % MAXSLEEP);
171ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  buffer_recv(&b, &d);
172ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  if (! quiet)
173ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  {
174ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    printf("%i: %i\n", *id, d);
175ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    fflush(stdout);
176ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  }
177ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  pthread_exit(NULL);
178ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
179ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
180ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#define THREADS (10)
181ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
182ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownint main(int argc, char** argv)
183ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
184ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  pthread_t producers[THREADS];
185ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  pthread_t consumers[THREADS];
186ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  int thread_arg[THREADS];
187ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  int i;
188ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  int optchar;
189ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
190ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  while ((optchar = getopt(argc, argv, "nq")) != EOF)
191ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  {
192ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    switch (optchar)
193ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    {
194ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    case 'n':
195ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      use_locking = 0;
196ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      break;
197ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    case 'q':
198ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      quiet = 1;
199ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      break;
200ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    }
201ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  }
202ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
203ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  srand(time(NULL));
204ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
205ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  buffer_init(&b);
206ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
207ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  for (i = 0; i < THREADS; ++i)
208ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  {
209ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    thread_arg[i] = i;
210ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    pthread_create(producers + i, NULL,
211ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown                   (void * (*)(void *)) producer, &thread_arg[i]);
212ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  }
213ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
214ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  for (i = 0; i < THREADS; ++i)
215ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    pthread_create(consumers + i, NULL,
216ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown                   (void * (*)(void *)) consumer, &thread_arg[i]);
217ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
218ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  for (i = 0; i < THREADS; ++i)
219ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  {
220ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    pthread_join(producers[i], NULL);
221ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    pthread_join(consumers[i], NULL);
222ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  }
223ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
224ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  buffer_destroy(&b);
225ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
226ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  return 0;
227ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
228