Non Blocking Channel Operations in C

Our example demonstrates non-blocking channel operations using a select statement with a default clause. In C, we don’t have built-in channels or select statements, so we’ll simulate this behavior using pthreads and condition variables.

#include <stdio.h>
#include <pthread.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>

#define MAX_MSG_LEN 256

typedef struct {
    char message[MAX_MSG_LEN];
    bool has_message;
    pthread_mutex_t mutex;
    pthread_cond_t cond;
} channel_t;

void channel_init(channel_t *ch) {
    ch->has_message = false;
    pthread_mutex_init(&ch->mutex, NULL);
    pthread_cond_init(&ch->cond, NULL);
}

bool channel_try_receive(channel_t *ch, char *msg) {
    bool received = false;
    pthread_mutex_lock(&ch->mutex);
    if (ch->has_message) {
        strcpy(msg, ch->message);
        ch->has_message = false;
        received = true;
    }
    pthread_mutex_unlock(&ch->mutex);
    return received;
}

bool channel_try_send(channel_t *ch, const char *msg) {
    bool sent = false;
    pthread_mutex_lock(&ch->mutex);
    if (!ch->has_message) {
        strcpy(ch->message, msg);
        ch->has_message = true;
        sent = true;
        pthread_cond_signal(&ch->cond);
    }
    pthread_mutex_unlock(&ch->mutex);
    return sent;
}

int main() {
    channel_t messages, signals;
    channel_init(&messages);
    channel_init(&signals);

    // Non-blocking receive
    char msg[MAX_MSG_LEN];
    if (channel_try_receive(&messages, msg)) {
        printf("received message %s\n", msg);
    } else {
        printf("no message received\n");
    }

    // Non-blocking send
    const char *hi = "hi";
    if (channel_try_send(&messages, hi)) {
        printf("sent message %s\n", hi);
    } else {
        printf("no message sent\n");
    }

    // Multi-way non-blocking select
    if (channel_try_receive(&messages, msg)) {
        printf("received message %s\n", msg);
    } else if (channel_try_receive(&signals, msg)) {
        printf("received signal %s\n", msg);
    } else {
        printf("no activity\n");
    }

    return 0;
}

This C program simulates non-blocking channel operations using a custom channel_t struct and associated functions. Here’s how it works:

  1. We define a channel_t struct that includes a message, a flag indicating if a message is present, a mutex for thread safety, and a condition variable for signaling.

  2. The channel_init function initializes a channel.

  3. channel_try_receive attempts to receive a message from a channel without blocking. If a message is available, it returns true and copies the message.

  4. channel_try_send attempts to send a message to a channel without blocking. If the channel is empty, it sends the message and returns true.

  5. In the main function, we create two channels: messages and signals.

  6. We perform a non-blocking receive on the messages channel. If no message is available, it prints “no message received”.

  7. We attempt a non-blocking send of “hi” to the messages channel. Since the channel is empty, this should succeed.

  8. Finally, we simulate a multi-way non-blocking select by trying to receive from both messages and signals channels. If neither has a message, it prints “no activity”.

To compile and run this program:

$ gcc -o non_blocking_channels non_blocking_channels.c -lpthread
$ ./non_blocking_channels
no message received
sent message hi
received message hi

This example demonstrates how to implement non-blocking operations in C, which can be useful in scenarios where you want to check for data without getting stuck waiting.