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