#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
int debug = 1;
pthread_mutex_t prod_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t prod_cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t cons_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cons_cond = PTHREAD_COND_INITIALIZER;
// Define the ring buffer structure
typedef struct {
char *buffer;
size_t capacity;
size_t read_pos;
size_t write_pos;
char full;
char empty;
pthread_mutex_t lock;
} StringRingBuffer;
StringRingBuffer rb;
// Initialize the ring buffer
void init_string_ring_buffer(StringRingBuffer *rb, size_t capacity) {
rb->buffer = malloc(capacity);
if (rb->buffer == NULL) {
perror("malloc");
exit(EXIT_FAILURE);
}
rb->capacity = capacity;
rb->read_pos = 0;
rb->write_pos = 0;
rb->full = 0;
rb->empty = 1;
pthread_mutex_init(&rb->lock, NULL);
}
// Destructor for the ring buffer
void destroy_string_ring_buffer(StringRingBuffer *rb) {
free(rb->buffer);
pthread_mutex_destroy(&rb->lock);
}
// Write data to the ring buffer
ssize_t write_to_string_ring_buffer(StringRingBuffer *rb, const char *data, size_t len)
{
ssize_t written = 0;
size_t write_pos;
size_t read_pos;
size_t space_before_wrap;
size_t available_space;
if (len == 0)
return -1;
pthread_mutex_lock(&rb->lock);
if (rb->full == 1)
{
//is full
goto write_end;
}
//printf("==> write_to_string\n");
write_pos = rb->write_pos;
read_pos = rb->read_pos;
if (write_pos < read_pos)
{
available_space = read_pos - write_pos;
// real len
len = len < available_space ? len : available_space;
memcpy(rb->buffer + write_pos, data, len);
rb->write_pos += len;
written = len;
}
else if (write_pos >= read_pos)
{
available_space = rb->capacity - (write_pos - read_pos);
// real len
len = len < available_space ? len : available_space;
space_before_wrap = rb->capacity - write_pos;
if (len <= space_before_wrap)
{
memcpy(rb->buffer + write_pos, data, len);
rb->write_pos += len;
if(rb->write_pos > rb->capacity)
{
printf("error: in write:%lu rb->write_pos > rb->capacity =======================================\n", rb->write_pos );
exit(-1);
}
if (rb->write_pos >= rb->capacity) {
rb->write_pos = 0;
}
written = len;
}
else
{
// Wrap around
memcpy(rb->buffer + write_pos, data, space_before_wrap);
memcpy(rb->buffer, data + space_before_wrap, len - space_before_wrap);
rb->write_pos = len - space_before_wrap;
written = len;
}
}
if(debug)
printf("[%lu]Wrote %ld bytes, write_pos:%ld read_pos:%ld\n", pthread_self() , written, rb->write_pos, rb->read_pos);
if (rb->write_pos == rb->read_pos)
{
rb->full = 1;
if(debug)
printf("[%lu]full write_pos:%ld-%ld read_pos:%ld-%ld\n", pthread_self() , rb->write_pos,write_pos, rb->read_pos,read_pos);
}
//printf("<== write_to_string\n");
write_end:
rb->empty = 0;
pthread_mutex_unlock(&rb->lock);
return written;
}
// Read data from the ring buffer
ssize_t read_from_string_ring_buffer(StringRingBuffer *rb, char *dest, size_t len)
{
ssize_t read_len = 0;
size_t write_pos;
size_t read_pos;
size_t data_before_wrap;
//size_t data_after_wrap;
size_t available_data;
if (len == 0)
return -1;
pthread_mutex_lock(&rb->lock);
if (rb->empty == 1)
{
goto read_end;
}
//printf("==> read_from_string\n");
write_pos = rb->write_pos;
read_pos = rb->read_pos;
if (write_pos <= read_pos)
{
available_data = rb->capacity - (read_pos - write_pos);
if (len > available_data)
{
//not enough
goto read_end;
}
data_before_wrap = rb->capacity - read_pos;
if (len <= data_before_wrap)
{
memcpy(dest, rb->buffer + read_pos, len);
rb->read_pos += len;
if(rb->read_pos > rb->capacity)
{
printf("error: in read:%lu rb->read_pos > rb->capacity =======================================\n", rb->read_pos);
exit(-1);
}
if (rb->read_pos >= rb->capacity) {
rb->read_pos = 0;
}
read_len = len;
}
else
{
memcpy(dest, rb->buffer + read_pos, data_before_wrap);
memcpy(dest + data_before_wrap, rb->buffer, len - data_before_wrap);
rb->read_pos = len - data_before_wrap;
read_len = len;
}
}
else if (write_pos > read_pos)
{
available_data = write_pos - read_pos;
if (len > available_data)
{
//not enough
goto read_end;
}
memcpy(dest, rb->buffer + read_pos, len);
rb->read_pos += len;
read_len = len;
}
if(debug)
printf("[%lu]Read %ld bytes: '%s', write_pos:%ld read_pos:%ld\n", pthread_self() , read_len, dest, rb->write_pos, rb->read_pos);
if (rb->write_pos == rb->read_pos)
{
rb->empty = 1;
if(debug)
printf("[%lu]empty write_pos:%ld-%ld read_pos:%ld-%ld\n", pthread_self(), rb->write_pos, write_pos, rb->read_pos, read_pos);
}
//printf("<== read_from_string\n");
read_end:
rb->full = 0;
pthread_mutex_unlock(&rb->lock);
return read_len;
}
void *func_producer(void *arg)
{
arg = arg;
struct timespec tv;
// Write some data
ssize_t written;
char test_str[] = "0123456789";
tv.tv_sec = 1;
while(1)
{
pthread_mutex_lock(&prod_mutex);
pthread_cond_timedwait(&prod_cond, &prod_mutex, &tv);
//pthread_cond_wait(&prod_cond, &prod_mutex);
//sleep(1);
while (1)
{
written = write_to_string_ring_buffer(&rb, test_str, strlen(test_str));
if(written <= 0)
{
//pthread_cond_signal(&cons_cond);
pthread_cond_broadcast(&cons_cond);
break;
}
//printf("Wrote %ld bytes\n", written);
}
pthread_mutex_unlock(&prod_mutex);
}
return NULL;
}
void *func_consumer(void *arg)
{
arg = arg;
struct timespec tv;
// Read the data back
ssize_t read_len;
char read_buf[20];
tv.tv_sec = 1;
while(1)
{
//sleep(1);
pthread_mutex_lock(&cons_mutex);
pthread_cond_timedwait(&cons_cond, &cons_mutex, &tv);
//pthread_cond_wait(&cons_cond, &cons_mutex);
while(1)
{
memset(read_buf, 0, sizeof(read_buf));
read_len = read_from_string_ring_buffer(&rb, read_buf, 10);
if(read_len <=0)
{
//pthread_cond_signal(&prod_cond);
pthread_cond_broadcast(&prod_cond);
//sleep(1);
//printf("Read %ld bytes: '%s'\n", read_len, read_buf);
break;
}
//printf("Read %ld bytes: '%s' out\n", read_len, read_buf);
}
pthread_mutex_unlock(&cons_mutex);
}
return NULL;
}
//gcc -g -O0 -std=c99 -Wall -Wextra -pedantic -pthread test.c -o string_ring_buffer
// Example usage
int main() {
int i;
pthread_t t_prod[10];
pthread_t t_cons[10];
init_string_ring_buffer(&rb, 25); // Initialize the ring buffer with capacity 20
for(i=0; i<5; i++)
{
pthread_create(&t_prod[i], NULL, func_producer, NULL);
pthread_create(&t_cons[i], NULL, func_consumer, NULL);
}
// Clean up
destroy_string_ring_buffer(&rb);
for(i=0; i<5; i++)
{
pthread_join(t_prod[i], NULL);
pthread_join(t_cons[i], NULL);
}
return 0;
}