16fd7d7488898e93aceabca60366166e679af9fd7bart/* Test program that performs producer-consumer style communication through
26fd7d7488898e93aceabca60366166e679af9fd7bart * a circular buffer. This test program is a slightly modified version of the
36fd7d7488898e93aceabca60366166e679af9fd7bart * test program made available by Miguel Ojeda
46fd7d7488898e93aceabca60366166e679af9fd7bart * -- see also http://article.gmane.org/gmane.comp.debugging.valgrind/8782.
56fd7d7488898e93aceabca60366166e679af9fd7bart */
66fd7d7488898e93aceabca60366166e679af9fd7bart
76fd7d7488898e93aceabca60366166e679af9fd7bart
86fd7d7488898e93aceabca60366166e679af9fd7bart#include <stdio.h>
96fd7d7488898e93aceabca60366166e679af9fd7bart#include <string.h>
106fd7d7488898e93aceabca60366166e679af9fd7bart#include <stdlib.h>
116fd7d7488898e93aceabca60366166e679af9fd7bart#include <unistd.h>
126fd7d7488898e93aceabca60366166e679af9fd7bart#include <time.h>
136fd7d7488898e93aceabca60366166e679af9fd7bart#include <pthread.h>
146fd7d7488898e93aceabca60366166e679af9fd7bart#include <semaphore.h>
15d759a835bd647cc543c41cd349aefb181dac5868tom#include <fcntl.h>
16d94169a1f0259e2da5e6873d205e355248c3a550bart#include "../../config.h"
17d94169a1f0259e2da5e6873d205e355248c3a550bart
18d94169a1f0259e2da5e6873d205e355248c3a550bart
19d94169a1f0259e2da5e6873d205e355248c3a550bart/** gcc versions 4.1.0 and later have support for atomic builtins. */
20d45d99553c15a361bb797d21ec6afb9bad22d2d4bart
21d45d99553c15a361bb797d21ec6afb9bad22d2d4bart#ifndef HAVE_BUILTIN_ATOMIC
22d45d99553c15a361bb797d21ec6afb9bad22d2d4bart#error Sorry, but this test program can only be compiled by a compiler that\
23d45d99553c15a361bb797d21ec6afb9bad22d2d4barthas built-in functions for atomic memory access.
24d45d99553c15a361bb797d21ec6afb9bad22d2d4bart#endif
25d94169a1f0259e2da5e6873d205e355248c3a550bart
266fd7d7488898e93aceabca60366166e679af9fd7bart
276fd7d7488898e93aceabca60366166e679af9fd7bart#define BUFFER_MAX (2)
286e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart#define DATA_SEMAPHORE_NAME "cb-data-semaphore"
296e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart#define FREE_SEMAPHORE_NAME "cb-free-semaphore"
306fd7d7488898e93aceabca60366166e679af9fd7bart
31d94169a1f0259e2da5e6873d205e355248c3a550bart
326fd7d7488898e93aceabca60366166e679af9fd7barttypedef int data_t;
336fd7d7488898e93aceabca60366166e679af9fd7bart
346fd7d7488898e93aceabca60366166e679af9fd7barttypedef struct {
356fd7d7488898e93aceabca60366166e679af9fd7bart  /* Counting semaphore representing the number of data items in the buffer. */
366e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart  sem_t* data;
376fd7d7488898e93aceabca60366166e679af9fd7bart  /* Counting semaphore representing the number of free elements. */
386e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart  sem_t* free;
396fd7d7488898e93aceabca60366166e679af9fd7bart  /* Position where a new elements should be written. */
40d94169a1f0259e2da5e6873d205e355248c3a550bart  int in;
416fd7d7488898e93aceabca60366166e679af9fd7bart  /* Position from where an element can be removed. */
42d94169a1f0259e2da5e6873d205e355248c3a550bart  int out;
436fd7d7488898e93aceabca60366166e679af9fd7bart  /* Mutex that protects 'in'. */
446fd7d7488898e93aceabca60366166e679af9fd7bart  pthread_mutex_t mutex_in;
456fd7d7488898e93aceabca60366166e679af9fd7bart  /* Mutex that protects 'out'. */
466fd7d7488898e93aceabca60366166e679af9fd7bart  pthread_mutex_t mutex_out;
476fd7d7488898e93aceabca60366166e679af9fd7bart  /* Data buffer. */
486fd7d7488898e93aceabca60366166e679af9fd7bart  data_t buffer[BUFFER_MAX];
496fd7d7488898e93aceabca60366166e679af9fd7bart} buffer_t;
506fd7d7488898e93aceabca60366166e679af9fd7bart
516fd7d7488898e93aceabca60366166e679af9fd7bartstatic int quiet = 0;
5203225a834a0dd72d96afd20c6d8188450dd08726bartstatic int use_locking = 1;
536fd7d7488898e93aceabca60366166e679af9fd7bart
54d94169a1f0259e2da5e6873d205e355248c3a550bartstatic __inline__
55d94169a1f0259e2da5e6873d205e355248c3a550bartint fetch_and_add(int* p, int i)
56d94169a1f0259e2da5e6873d205e355248c3a550bart{
57d94169a1f0259e2da5e6873d205e355248c3a550bart  return __sync_fetch_and_add(p, i);
58d94169a1f0259e2da5e6873d205e355248c3a550bart}
59d94169a1f0259e2da5e6873d205e355248c3a550bart
606e38cb2fe1aac8a8082c2b4139fb8202c95fad7abartstatic sem_t* create_semaphore(const char* const name, const int value)
616fd7d7488898e93aceabca60366166e679af9fd7bart{
623e7c40292141549eee08a9db6db60a6cf3195f91bart#ifdef VGO_darwin
638c7213fc97157614ca53f70353b9cd2d7a9326d9bart  char name_and_pid[32];
648c7213fc97157614ca53f70353b9cd2d7a9326d9bart  snprintf(name_and_pid, sizeof(name_and_pid), "%s-%d", name, getpid());
658c7213fc97157614ca53f70353b9cd2d7a9326d9bart  sem_t* p = sem_open(name_and_pid, O_CREAT | O_EXCL, 0600, value);
668c7213fc97157614ca53f70353b9cd2d7a9326d9bart  if (p == SEM_FAILED) {
678c7213fc97157614ca53f70353b9cd2d7a9326d9bart    perror("sem_open");
688c7213fc97157614ca53f70353b9cd2d7a9326d9bart    return NULL;
698c7213fc97157614ca53f70353b9cd2d7a9326d9bart  }
706e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart  return p;
716e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart#else
726e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart  sem_t* p = malloc(sizeof(*p));
736e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart  if (p)
746e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart    sem_init(p, 0, value);
756e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart  return p;
766e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart#endif
776e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart}
786e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart
796e38cb2fe1aac8a8082c2b4139fb8202c95fad7abartstatic void destroy_semaphore(const char* const name, sem_t* p)
806e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart{
813e7c40292141549eee08a9db6db60a6cf3195f91bart#ifdef VGO_darwin
826e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart  sem_close(p);
836e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart  sem_unlink(name);
846e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart#else
856e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart  sem_destroy(p);
866e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart  free(p);
876e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart#endif
886e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart}
896e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart
906e38cb2fe1aac8a8082c2b4139fb8202c95fad7abartstatic void buffer_init(buffer_t * b)
916e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart{
926e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart  b->data = create_semaphore(DATA_SEMAPHORE_NAME, 0);
936e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart  b->free = create_semaphore(FREE_SEMAPHORE_NAME, BUFFER_MAX);
946fd7d7488898e93aceabca60366166e679af9fd7bart
956fd7d7488898e93aceabca60366166e679af9fd7bart  pthread_mutex_init(&b->mutex_in, NULL);
966fd7d7488898e93aceabca60366166e679af9fd7bart  pthread_mutex_init(&b->mutex_out, NULL);
976fd7d7488898e93aceabca60366166e679af9fd7bart
986fd7d7488898e93aceabca60366166e679af9fd7bart  b->in = 0;
996fd7d7488898e93aceabca60366166e679af9fd7bart  b->out = 0;
1006fd7d7488898e93aceabca60366166e679af9fd7bart}
1016fd7d7488898e93aceabca60366166e679af9fd7bart
1026e38cb2fe1aac8a8082c2b4139fb8202c95fad7abartstatic void buffer_recv(buffer_t* b, data_t* d)
1036fd7d7488898e93aceabca60366166e679af9fd7bart{
104d94169a1f0259e2da5e6873d205e355248c3a550bart  int out;
1056e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart  sem_wait(b->data);
10603225a834a0dd72d96afd20c6d8188450dd08726bart  if (use_locking)
10703225a834a0dd72d96afd20c6d8188450dd08726bart    pthread_mutex_lock(&b->mutex_out);
108d94169a1f0259e2da5e6873d205e355248c3a550bart  out = fetch_and_add(&b->out, 1);
109d94169a1f0259e2da5e6873d205e355248c3a550bart  if (out >= BUFFER_MAX)
110d94169a1f0259e2da5e6873d205e355248c3a550bart  {
111d94169a1f0259e2da5e6873d205e355248c3a550bart    fetch_and_add(&b->out, -BUFFER_MAX);
112d94169a1f0259e2da5e6873d205e355248c3a550bart    out -= BUFFER_MAX;
113d94169a1f0259e2da5e6873d205e355248c3a550bart  }
114d94169a1f0259e2da5e6873d205e355248c3a550bart  *d = b->buffer[out];
11503225a834a0dd72d96afd20c6d8188450dd08726bart  if (use_locking)
11603225a834a0dd72d96afd20c6d8188450dd08726bart    pthread_mutex_unlock(&b->mutex_out);
117d94169a1f0259e2da5e6873d205e355248c3a550bart  if (! quiet)
118d94169a1f0259e2da5e6873d205e355248c3a550bart  {
119d94169a1f0259e2da5e6873d205e355248c3a550bart    printf("received %d from buffer[%d]\n", *d, out);
120d94169a1f0259e2da5e6873d205e355248c3a550bart    fflush(stdout);
121d94169a1f0259e2da5e6873d205e355248c3a550bart  }
1226e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart  sem_post(b->free);
1236fd7d7488898e93aceabca60366166e679af9fd7bart}
1246fd7d7488898e93aceabca60366166e679af9fd7bart
1256e38cb2fe1aac8a8082c2b4139fb8202c95fad7abartstatic void buffer_send(buffer_t* b, data_t* d)
1266fd7d7488898e93aceabca60366166e679af9fd7bart{
127d94169a1f0259e2da5e6873d205e355248c3a550bart  int in;
1286e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart  sem_wait(b->free);
12903225a834a0dd72d96afd20c6d8188450dd08726bart  if (use_locking)
13003225a834a0dd72d96afd20c6d8188450dd08726bart    pthread_mutex_lock(&b->mutex_in);
131d94169a1f0259e2da5e6873d205e355248c3a550bart  in = fetch_and_add(&b->in, 1);
132d94169a1f0259e2da5e6873d205e355248c3a550bart  if (in >= BUFFER_MAX)
133d94169a1f0259e2da5e6873d205e355248c3a550bart  {
134d94169a1f0259e2da5e6873d205e355248c3a550bart    fetch_and_add(&b->in, -BUFFER_MAX);
135d94169a1f0259e2da5e6873d205e355248c3a550bart    in -= BUFFER_MAX;
136d94169a1f0259e2da5e6873d205e355248c3a550bart  }
137d94169a1f0259e2da5e6873d205e355248c3a550bart  b->buffer[in] = *d;
13803225a834a0dd72d96afd20c6d8188450dd08726bart  if (use_locking)
13903225a834a0dd72d96afd20c6d8188450dd08726bart    pthread_mutex_unlock(&b->mutex_in);
140d94169a1f0259e2da5e6873d205e355248c3a550bart  if (! quiet)
141d94169a1f0259e2da5e6873d205e355248c3a550bart  {
142d94169a1f0259e2da5e6873d205e355248c3a550bart    printf("sent %d to buffer[%d]\n", *d, in);
143d94169a1f0259e2da5e6873d205e355248c3a550bart    fflush(stdout);
144d94169a1f0259e2da5e6873d205e355248c3a550bart  }
1456e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart  sem_post(b->data);
1466fd7d7488898e93aceabca60366166e679af9fd7bart}
1476fd7d7488898e93aceabca60366166e679af9fd7bart
1486e38cb2fe1aac8a8082c2b4139fb8202c95fad7abartstatic void buffer_destroy(buffer_t* b)
1496fd7d7488898e93aceabca60366166e679af9fd7bart{
1506e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart  destroy_semaphore(DATA_SEMAPHORE_NAME, b->data);
1516e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart  destroy_semaphore(FREE_SEMAPHORE_NAME, b->free);
1526fd7d7488898e93aceabca60366166e679af9fd7bart
1536fd7d7488898e93aceabca60366166e679af9fd7bart  pthread_mutex_destroy(&b->mutex_in);
1546fd7d7488898e93aceabca60366166e679af9fd7bart  pthread_mutex_destroy(&b->mutex_out);
1556fd7d7488898e93aceabca60366166e679af9fd7bart}
1566fd7d7488898e93aceabca60366166e679af9fd7bart
1576e38cb2fe1aac8a8082c2b4139fb8202c95fad7abartstatic buffer_t b;
1586fd7d7488898e93aceabca60366166e679af9fd7bart
1596e38cb2fe1aac8a8082c2b4139fb8202c95fad7abartstatic void producer(int* id)
1606fd7d7488898e93aceabca60366166e679af9fd7bart{
1616fd7d7488898e93aceabca60366166e679af9fd7bart  buffer_send(&b, id);
1626fd7d7488898e93aceabca60366166e679af9fd7bart  pthread_exit(NULL);
1636fd7d7488898e93aceabca60366166e679af9fd7bart}
1646fd7d7488898e93aceabca60366166e679af9fd7bart
1656fd7d7488898e93aceabca60366166e679af9fd7bart#define MAXSLEEP (100 * 1000)
1666fd7d7488898e93aceabca60366166e679af9fd7bart
1676e38cb2fe1aac8a8082c2b4139fb8202c95fad7abartstatic void consumer(int* id)
1686fd7d7488898e93aceabca60366166e679af9fd7bart{
1696fd7d7488898e93aceabca60366166e679af9fd7bart  int d;
1706fd7d7488898e93aceabca60366166e679af9fd7bart  usleep(rand() % MAXSLEEP);
1716fd7d7488898e93aceabca60366166e679af9fd7bart  buffer_recv(&b, &d);
1726fd7d7488898e93aceabca60366166e679af9fd7bart  if (! quiet)
173d94169a1f0259e2da5e6873d205e355248c3a550bart  {
1746fd7d7488898e93aceabca60366166e679af9fd7bart    printf("%i: %i\n", *id, d);
175d94169a1f0259e2da5e6873d205e355248c3a550bart    fflush(stdout);
176d94169a1f0259e2da5e6873d205e355248c3a550bart  }
1776fd7d7488898e93aceabca60366166e679af9fd7bart  pthread_exit(NULL);
1786fd7d7488898e93aceabca60366166e679af9fd7bart}
1796fd7d7488898e93aceabca60366166e679af9fd7bart
1806fd7d7488898e93aceabca60366166e679af9fd7bart#define THREADS (10)
1816fd7d7488898e93aceabca60366166e679af9fd7bart
1826fd7d7488898e93aceabca60366166e679af9fd7bartint main(int argc, char** argv)
1836fd7d7488898e93aceabca60366166e679af9fd7bart{
1846fd7d7488898e93aceabca60366166e679af9fd7bart  pthread_t producers[THREADS];
1856fd7d7488898e93aceabca60366166e679af9fd7bart  pthread_t consumers[THREADS];
1866fd7d7488898e93aceabca60366166e679af9fd7bart  int thread_arg[THREADS];
1876fd7d7488898e93aceabca60366166e679af9fd7bart  int i;
1886fd7d7488898e93aceabca60366166e679af9fd7bart  int optchar;
1896fd7d7488898e93aceabca60366166e679af9fd7bart
19003225a834a0dd72d96afd20c6d8188450dd08726bart  while ((optchar = getopt(argc, argv, "nq")) != EOF)
1916fd7d7488898e93aceabca60366166e679af9fd7bart  {
1926fd7d7488898e93aceabca60366166e679af9fd7bart    switch (optchar)
1936fd7d7488898e93aceabca60366166e679af9fd7bart    {
19403225a834a0dd72d96afd20c6d8188450dd08726bart    case 'n':
19503225a834a0dd72d96afd20c6d8188450dd08726bart      use_locking = 0;
19603225a834a0dd72d96afd20c6d8188450dd08726bart      break;
1976fd7d7488898e93aceabca60366166e679af9fd7bart    case 'q':
1986fd7d7488898e93aceabca60366166e679af9fd7bart      quiet = 1;
1996fd7d7488898e93aceabca60366166e679af9fd7bart      break;
2006fd7d7488898e93aceabca60366166e679af9fd7bart    }
2016fd7d7488898e93aceabca60366166e679af9fd7bart  }
2026fd7d7488898e93aceabca60366166e679af9fd7bart
2036fd7d7488898e93aceabca60366166e679af9fd7bart  srand(time(NULL));
2046fd7d7488898e93aceabca60366166e679af9fd7bart
2056fd7d7488898e93aceabca60366166e679af9fd7bart  buffer_init(&b);
2066fd7d7488898e93aceabca60366166e679af9fd7bart
2076fd7d7488898e93aceabca60366166e679af9fd7bart  for (i = 0; i < THREADS; ++i)
2086fd7d7488898e93aceabca60366166e679af9fd7bart  {
2096fd7d7488898e93aceabca60366166e679af9fd7bart    thread_arg[i] = i;
2106fd7d7488898e93aceabca60366166e679af9fd7bart    pthread_create(producers + i, NULL,
2116fd7d7488898e93aceabca60366166e679af9fd7bart                   (void * (*)(void *)) producer, &thread_arg[i]);
2126fd7d7488898e93aceabca60366166e679af9fd7bart  }
2136fd7d7488898e93aceabca60366166e679af9fd7bart
2146fd7d7488898e93aceabca60366166e679af9fd7bart  for (i = 0; i < THREADS; ++i)
2156fd7d7488898e93aceabca60366166e679af9fd7bart    pthread_create(consumers + i, NULL,
2166fd7d7488898e93aceabca60366166e679af9fd7bart                   (void * (*)(void *)) consumer, &thread_arg[i]);
2176fd7d7488898e93aceabca60366166e679af9fd7bart
2186fd7d7488898e93aceabca60366166e679af9fd7bart  for (i = 0; i < THREADS; ++i)
2196fd7d7488898e93aceabca60366166e679af9fd7bart  {
2206fd7d7488898e93aceabca60366166e679af9fd7bart    pthread_join(producers[i], NULL);
2216fd7d7488898e93aceabca60366166e679af9fd7bart    pthread_join(consumers[i], NULL);
2226fd7d7488898e93aceabca60366166e679af9fd7bart  }
2236fd7d7488898e93aceabca60366166e679af9fd7bart
2246fd7d7488898e93aceabca60366166e679af9fd7bart  buffer_destroy(&b);
2256fd7d7488898e93aceabca60366166e679af9fd7bart
2266fd7d7488898e93aceabca60366166e679af9fd7bart  return 0;
2276fd7d7488898e93aceabca60366166e679af9fd7bart}
228