Channel Buffering in C

The concept of buffered channels in C can be simulated using a thread-safe queue. We’ll use a simple array-based queue implementation with mutex locks for thread safety.

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>

#define BUFFER_SIZE 2

typedef struct {
    char* data[BUFFER_SIZE];
    int front;
    int rear;
    int count;
    pthread_mutex_t mutex;
} BufferedQueue;

void initQueue(BufferedQueue* queue) {
    queue->front = 0;
    queue->rear = -1;
    queue->count = 0;
    pthread_mutex_init(&queue->mutex, NULL);
}

void enqueue(BufferedQueue* queue, const char* message) {
    pthread_mutex_lock(&queue->mutex);
    if (queue->count < BUFFER_SIZE) {
        queue->rear = (queue->rear + 1) % BUFFER_SIZE;
        queue->data[queue->rear] = strdup(message);
        queue->count++;
    }
    pthread_mutex_unlock(&queue->mutex);
}

char* dequeue(BufferedQueue* queue) {
    char* message = NULL;
    pthread_mutex_lock(&queue->mutex);
    if (queue->count > 0) {
        message = queue->data[queue->front];
        queue->front = (queue->front + 1) % BUFFER_SIZE;
        queue->count--;
    }
    pthread_mutex_unlock(&queue->mutex);
    return message;
}

int main() {
    BufferedQueue messages;
    initQueue(&messages);

    // Because this queue is buffered, we can send these
    // values into the queue without a corresponding
    // concurrent receive.
    enqueue(&messages, "buffered");
    enqueue(&messages, "queue");

    // Later we can receive these two values as usual.
    char* msg1 = dequeue(&messages);
    char* msg2 = dequeue(&messages);

    if (msg1) {
        printf("%s\n", msg1);
        free(msg1);
    }
    if (msg2) {
        printf("%s\n", msg2);
        free(msg2);
    }

    return 0;
}

This C program demonstrates a concept similar to buffered channels using a thread-safe queue. Here’s how it works:

  1. We define a BufferedQueue structure that holds an array of strings, along with front and rear indices, a count, and a mutex for thread safety.

  2. The initQueue function initializes the queue and its mutex.

  3. The enqueue function adds a message to the queue if it’s not full. It uses the mutex to ensure thread safety.

  4. The dequeue function removes and returns a message from the queue if it’s not empty. It also uses the mutex for thread safety.

  5. In the main function, we create a buffered queue that can hold up to 2 messages.

  6. We enqueue two messages into the queue without needing a corresponding receive operation.

  7. Later, we dequeue and print these two messages.

To compile and run this program:

$ gcc -o buffered_queue buffered_queue.c -lpthread
$ ./buffered_queue
buffered
queue

This example demonstrates how to implement a concept similar to buffered channels in C using a thread-safe queue. While C doesn’t have built-in support for such high-level concurrency primitives, we can create similar functionality using lower-level constructs like mutexes and condition variables.