circular_buffer.c revision d759a835bd647cc543c41cd349aefb181dac5868
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{
620ccdc4a1e148ce89d99a4593b777c0fd9192bce0bart#ifndef HAVE_SEM_INIT
636e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart  sem_t* p = sem_open(name, O_CREAT, 0600, value);
646e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart  return p;
656e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart#else
666e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart  sem_t* p = malloc(sizeof(*p));
676e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart  if (p)
686e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart    sem_init(p, 0, value);
696e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart  return p;
706e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart#endif
716e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart}
726e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart
736e38cb2fe1aac8a8082c2b4139fb8202c95fad7abartstatic void destroy_semaphore(const char* const name, sem_t* p)
746e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart{
750ccdc4a1e148ce89d99a4593b777c0fd9192bce0bart#ifndef HAVE_SEM_INIT
766e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart  sem_close(p);
776e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart  sem_unlink(name);
786e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart#else
796e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart  sem_destroy(p);
806e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart  free(p);
816e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart#endif
826e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart}
836e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart
846e38cb2fe1aac8a8082c2b4139fb8202c95fad7abartstatic void buffer_init(buffer_t * b)
856e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart{
866e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart  b->data = create_semaphore(DATA_SEMAPHORE_NAME, 0);
876e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart  b->free = create_semaphore(FREE_SEMAPHORE_NAME, BUFFER_MAX);
886fd7d7488898e93aceabca60366166e679af9fd7bart
896fd7d7488898e93aceabca60366166e679af9fd7bart  pthread_mutex_init(&b->mutex_in, NULL);
906fd7d7488898e93aceabca60366166e679af9fd7bart  pthread_mutex_init(&b->mutex_out, NULL);
916fd7d7488898e93aceabca60366166e679af9fd7bart
926fd7d7488898e93aceabca60366166e679af9fd7bart  b->in = 0;
936fd7d7488898e93aceabca60366166e679af9fd7bart  b->out = 0;
946fd7d7488898e93aceabca60366166e679af9fd7bart}
956fd7d7488898e93aceabca60366166e679af9fd7bart
966e38cb2fe1aac8a8082c2b4139fb8202c95fad7abartstatic void buffer_recv(buffer_t* b, data_t* d)
976fd7d7488898e93aceabca60366166e679af9fd7bart{
98d94169a1f0259e2da5e6873d205e355248c3a550bart  int out;
996e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart  sem_wait(b->data);
10003225a834a0dd72d96afd20c6d8188450dd08726bart  if (use_locking)
10103225a834a0dd72d96afd20c6d8188450dd08726bart    pthread_mutex_lock(&b->mutex_out);
102d94169a1f0259e2da5e6873d205e355248c3a550bart  out = fetch_and_add(&b->out, 1);
103d94169a1f0259e2da5e6873d205e355248c3a550bart  if (out >= BUFFER_MAX)
104d94169a1f0259e2da5e6873d205e355248c3a550bart  {
105d94169a1f0259e2da5e6873d205e355248c3a550bart    fetch_and_add(&b->out, -BUFFER_MAX);
106d94169a1f0259e2da5e6873d205e355248c3a550bart    out -= BUFFER_MAX;
107d94169a1f0259e2da5e6873d205e355248c3a550bart  }
108d94169a1f0259e2da5e6873d205e355248c3a550bart  *d = b->buffer[out];
10903225a834a0dd72d96afd20c6d8188450dd08726bart  if (use_locking)
11003225a834a0dd72d96afd20c6d8188450dd08726bart    pthread_mutex_unlock(&b->mutex_out);
111d94169a1f0259e2da5e6873d205e355248c3a550bart  if (! quiet)
112d94169a1f0259e2da5e6873d205e355248c3a550bart  {
113d94169a1f0259e2da5e6873d205e355248c3a550bart    printf("received %d from buffer[%d]\n", *d, out);
114d94169a1f0259e2da5e6873d205e355248c3a550bart    fflush(stdout);
115d94169a1f0259e2da5e6873d205e355248c3a550bart  }
1166e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart  sem_post(b->free);
1176fd7d7488898e93aceabca60366166e679af9fd7bart}
1186fd7d7488898e93aceabca60366166e679af9fd7bart
1196e38cb2fe1aac8a8082c2b4139fb8202c95fad7abartstatic void buffer_send(buffer_t* b, data_t* d)
1206fd7d7488898e93aceabca60366166e679af9fd7bart{
121d94169a1f0259e2da5e6873d205e355248c3a550bart  int in;
1226e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart  sem_wait(b->free);
12303225a834a0dd72d96afd20c6d8188450dd08726bart  if (use_locking)
12403225a834a0dd72d96afd20c6d8188450dd08726bart    pthread_mutex_lock(&b->mutex_in);
125d94169a1f0259e2da5e6873d205e355248c3a550bart  in = fetch_and_add(&b->in, 1);
126d94169a1f0259e2da5e6873d205e355248c3a550bart  if (in >= BUFFER_MAX)
127d94169a1f0259e2da5e6873d205e355248c3a550bart  {
128d94169a1f0259e2da5e6873d205e355248c3a550bart    fetch_and_add(&b->in, -BUFFER_MAX);
129d94169a1f0259e2da5e6873d205e355248c3a550bart    in -= BUFFER_MAX;
130d94169a1f0259e2da5e6873d205e355248c3a550bart  }
131d94169a1f0259e2da5e6873d205e355248c3a550bart  b->buffer[in] = *d;
13203225a834a0dd72d96afd20c6d8188450dd08726bart  if (use_locking)
13303225a834a0dd72d96afd20c6d8188450dd08726bart    pthread_mutex_unlock(&b->mutex_in);
134d94169a1f0259e2da5e6873d205e355248c3a550bart  if (! quiet)
135d94169a1f0259e2da5e6873d205e355248c3a550bart  {
136d94169a1f0259e2da5e6873d205e355248c3a550bart    printf("sent %d to buffer[%d]\n", *d, in);
137d94169a1f0259e2da5e6873d205e355248c3a550bart    fflush(stdout);
138d94169a1f0259e2da5e6873d205e355248c3a550bart  }
1396e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart  sem_post(b->data);
1406fd7d7488898e93aceabca60366166e679af9fd7bart}
1416fd7d7488898e93aceabca60366166e679af9fd7bart
1426e38cb2fe1aac8a8082c2b4139fb8202c95fad7abartstatic void buffer_destroy(buffer_t* b)
1436fd7d7488898e93aceabca60366166e679af9fd7bart{
1446e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart  destroy_semaphore(DATA_SEMAPHORE_NAME, b->data);
1456e38cb2fe1aac8a8082c2b4139fb8202c95fad7abart  destroy_semaphore(FREE_SEMAPHORE_NAME, b->free);
1466fd7d7488898e93aceabca60366166e679af9fd7bart
1476fd7d7488898e93aceabca60366166e679af9fd7bart  pthread_mutex_destroy(&b->mutex_in);
1486fd7d7488898e93aceabca60366166e679af9fd7bart  pthread_mutex_destroy(&b->mutex_out);
1496fd7d7488898e93aceabca60366166e679af9fd7bart}
1506fd7d7488898e93aceabca60366166e679af9fd7bart
1516e38cb2fe1aac8a8082c2b4139fb8202c95fad7abartstatic buffer_t b;
1526fd7d7488898e93aceabca60366166e679af9fd7bart
1536e38cb2fe1aac8a8082c2b4139fb8202c95fad7abartstatic void producer(int* id)
1546fd7d7488898e93aceabca60366166e679af9fd7bart{
1556fd7d7488898e93aceabca60366166e679af9fd7bart  buffer_send(&b, id);
1566fd7d7488898e93aceabca60366166e679af9fd7bart  pthread_exit(NULL);
1576fd7d7488898e93aceabca60366166e679af9fd7bart}
1586fd7d7488898e93aceabca60366166e679af9fd7bart
1596fd7d7488898e93aceabca60366166e679af9fd7bart#define MAXSLEEP (100 * 1000)
1606fd7d7488898e93aceabca60366166e679af9fd7bart
1616e38cb2fe1aac8a8082c2b4139fb8202c95fad7abartstatic void consumer(int* id)
1626fd7d7488898e93aceabca60366166e679af9fd7bart{
1636fd7d7488898e93aceabca60366166e679af9fd7bart  int d;
1646fd7d7488898e93aceabca60366166e679af9fd7bart  usleep(rand() % MAXSLEEP);
1656fd7d7488898e93aceabca60366166e679af9fd7bart  buffer_recv(&b, &d);
1666fd7d7488898e93aceabca60366166e679af9fd7bart  if (! quiet)
167d94169a1f0259e2da5e6873d205e355248c3a550bart  {
1686fd7d7488898e93aceabca60366166e679af9fd7bart    printf("%i: %i\n", *id, d);
169d94169a1f0259e2da5e6873d205e355248c3a550bart    fflush(stdout);
170d94169a1f0259e2da5e6873d205e355248c3a550bart  }
1716fd7d7488898e93aceabca60366166e679af9fd7bart  pthread_exit(NULL);
1726fd7d7488898e93aceabca60366166e679af9fd7bart}
1736fd7d7488898e93aceabca60366166e679af9fd7bart
1746fd7d7488898e93aceabca60366166e679af9fd7bart#define THREADS (10)
1756fd7d7488898e93aceabca60366166e679af9fd7bart
1766fd7d7488898e93aceabca60366166e679af9fd7bartint main(int argc, char** argv)
1776fd7d7488898e93aceabca60366166e679af9fd7bart{
1786fd7d7488898e93aceabca60366166e679af9fd7bart  pthread_t producers[THREADS];
1796fd7d7488898e93aceabca60366166e679af9fd7bart  pthread_t consumers[THREADS];
1806fd7d7488898e93aceabca60366166e679af9fd7bart  int thread_arg[THREADS];
1816fd7d7488898e93aceabca60366166e679af9fd7bart  int i;
1826fd7d7488898e93aceabca60366166e679af9fd7bart  int optchar;
1836fd7d7488898e93aceabca60366166e679af9fd7bart
18403225a834a0dd72d96afd20c6d8188450dd08726bart  while ((optchar = getopt(argc, argv, "nq")) != EOF)
1856fd7d7488898e93aceabca60366166e679af9fd7bart  {
1866fd7d7488898e93aceabca60366166e679af9fd7bart    switch (optchar)
1876fd7d7488898e93aceabca60366166e679af9fd7bart    {
18803225a834a0dd72d96afd20c6d8188450dd08726bart    case 'n':
18903225a834a0dd72d96afd20c6d8188450dd08726bart      use_locking = 0;
19003225a834a0dd72d96afd20c6d8188450dd08726bart      break;
1916fd7d7488898e93aceabca60366166e679af9fd7bart    case 'q':
1926fd7d7488898e93aceabca60366166e679af9fd7bart      quiet = 1;
1936fd7d7488898e93aceabca60366166e679af9fd7bart      break;
1946fd7d7488898e93aceabca60366166e679af9fd7bart    }
1956fd7d7488898e93aceabca60366166e679af9fd7bart  }
1966fd7d7488898e93aceabca60366166e679af9fd7bart
1976fd7d7488898e93aceabca60366166e679af9fd7bart  srand(time(NULL));
1986fd7d7488898e93aceabca60366166e679af9fd7bart
1996fd7d7488898e93aceabca60366166e679af9fd7bart  buffer_init(&b);
2006fd7d7488898e93aceabca60366166e679af9fd7bart
2016fd7d7488898e93aceabca60366166e679af9fd7bart  for (i = 0; i < THREADS; ++i)
2026fd7d7488898e93aceabca60366166e679af9fd7bart  {
2036fd7d7488898e93aceabca60366166e679af9fd7bart    thread_arg[i] = i;
2046fd7d7488898e93aceabca60366166e679af9fd7bart    pthread_create(producers + i, NULL,
2056fd7d7488898e93aceabca60366166e679af9fd7bart                   (void * (*)(void *)) producer, &thread_arg[i]);
2066fd7d7488898e93aceabca60366166e679af9fd7bart  }
2076fd7d7488898e93aceabca60366166e679af9fd7bart
2086fd7d7488898e93aceabca60366166e679af9fd7bart  for (i = 0; i < THREADS; ++i)
2096fd7d7488898e93aceabca60366166e679af9fd7bart    pthread_create(consumers + i, NULL,
2106fd7d7488898e93aceabca60366166e679af9fd7bart                   (void * (*)(void *)) consumer, &thread_arg[i]);
2116fd7d7488898e93aceabca60366166e679af9fd7bart
2126fd7d7488898e93aceabca60366166e679af9fd7bart  for (i = 0; i < THREADS; ++i)
2136fd7d7488898e93aceabca60366166e679af9fd7bart  {
2146fd7d7488898e93aceabca60366166e679af9fd7bart    pthread_join(producers[i], NULL);
2156fd7d7488898e93aceabca60366166e679af9fd7bart    pthread_join(consumers[i], NULL);
2166fd7d7488898e93aceabca60366166e679af9fd7bart  }
2176fd7d7488898e93aceabca60366166e679af9fd7bart
2186fd7d7488898e93aceabca60366166e679af9fd7bart  buffer_destroy(&b);
2196fd7d7488898e93aceabca60366166e679af9fd7bart
2206fd7d7488898e93aceabca60366166e679af9fd7bart  return 0;
2216fd7d7488898e93aceabca60366166e679af9fd7bart}
222