Select in C

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

// Structure to simulate a channel
typedef struct {
    char* message;
    pthread_mutex_t mutex;
    pthread_cond_t cond;
    int ready;
} channel;

// Function to initialize a channel
void channel_init(channel* c) {
    c->message = NULL;
    pthread_mutex_init(&c->mutex, NULL);
    pthread_cond_init(&c->cond, NULL);
    c->ready = 0;
}

// Function to send a message on a channel
void channel_send(channel* c, char* msg) {
    pthread_mutex_lock(&c->mutex);
    c->message = msg;
    c->ready = 1;
    pthread_cond_signal(&c->cond);
    pthread_mutex_unlock(&c->mutex);
}

// Function to receive a message from a channel
char* channel_receive(channel* c) {
    pthread_mutex_lock(&c->mutex);
    while (!c->ready) {
        pthread_cond_wait(&c->cond, &c->mutex);
    }
    char* msg = c->message;
    c->ready = 0;
    pthread_mutex_unlock(&c->mutex);
    return msg;
}

// Thread function for sending on channel 1
void* send_one(void* arg) {
    channel* c = (channel*)arg;
    sleep(1);
    channel_send(c, "one");
    return NULL;
}

// Thread function for sending on channel 2
void* send_two(void* arg) {
    channel* c = (channel*)arg;
    sleep(2);
    channel_send(c, "two");
    return NULL;
}

int main() {
    // Create two channels
    channel c1, c2;
    channel_init(&c1);
    channel_init(&c2);

    // Create threads to simulate concurrent operations
    pthread_t thread1, thread2;
    pthread_create(&thread1, NULL, send_one, &c1);
    pthread_create(&thread2, NULL, send_two, &c2);

    // Use select-like behavior to wait on multiple channels
    for (int i = 0; i < 2; i++) {
        fd_set readfds;
        FD_ZERO(&readfds);
        FD_SET(STDIN_FILENO, &readfds);

        struct timeval tv = {0, 0};
        select(1, &readfds, NULL, NULL, &tv);

        if (c1.ready) {
            char* msg = channel_receive(&c1);
            printf("received %s\n", msg);
        } else if (c2.ready) {
            char* msg = channel_receive(&c2);
            printf("received %s\n", msg);
        } else {
            i--;  // Try again if neither channel is ready
        }
    }

    // Clean up
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    return 0;
}

This C program simulates the behavior of the original example using POSIX threads and a custom channel implementation. Here’s an explanation of the key components:

  1. We define a channel struct to simulate channels, along with functions to initialize, send, and receive messages.

  2. Two threads are created to simulate the concurrent operations that send messages after specific delays.

  3. In the main function, we use a select-like approach to wait on multiple channels. The select function is used with a zero timeout to check if any channel is ready without blocking.

  4. The program loops twice, each time checking if either channel has a message ready. If a channel is ready, it receives the message and prints it.

  5. If neither channel is ready, the loop decrements the counter to try again.

To compile and run this program:

$ gcc -o select_example select_example.c -lpthread
$ ./select_example
received one
received two

Note that the total execution time should be around 2 seconds, as both the 1 and 2 second sleeps execute concurrently in separate threads.

This C implementation provides similar functionality to the original example, demonstrating how to wait on multiple concurrent operations and handle their results as they become available.