Task: Rendezvous

C: #include <stdlib.h>
#include <stdio.h>
#include <pthread.h>




typedef struct rendezvous {
    pthread_mutex_t lock;        
    pthread_cond_t cv_entering;  
    pthread_cond_t cv_accepting; 
    pthread_cond_t cv_done;      
    int (*accept_func)(void*);   
    int entering;                
    int accepting;               
    int done;                    
} rendezvous_t;


#define RENDEZVOUS_INITILIZER(accept_function) {   \
        .lock         = PTHREAD_MUTEX_INITIALIZER, \
        .cv_entering  = PTHREAD_COND_INITIALIZER,  \
        .cv_accepting = PTHREAD_COND_INITIALIZER,  \
        .cv_done      = PTHREAD_COND_INITIALIZER,  \
        .accept_func  = accept_function,           \
        .entering     = 0,                         \
        .accepting    = 0,                         \
        .done         = 0,                         \
    }

int enter_rendezvous(rendezvous_t *rv, void* data)
{
    
    pthread_mutex_lock(&rv->lock);

    rv->entering++;
    pthread_cond_signal(&rv->cv_entering);

    while (!rv->accepting) {
        
        pthread_cond_wait(&rv->cv_accepting, &rv->lock);
    }

    
    int ret = rv->accept_func(data);

    
    rv->done = 1;
    pthread_cond_signal(&rv->cv_done);

    rv->entering--;
    rv->accepting = 0;
    pthread_mutex_unlock(&rv->lock);

    return ret;
}

void accept_rendezvous(rendezvous_t *rv)
{
    
    pthread_mutex_lock(&rv->lock);
    rv->accepting = 1;

    while (!rv->entering) {
        
        pthread_cond_wait(&rv->cv_entering, &rv->lock);
    }

    pthread_cond_signal(&rv->cv_accepting);

    while (!rv->done) {
        
        pthread_cond_wait(&rv->cv_done, &rv->lock);
    }
    rv->done = 0;

    rv->accepting = 0;
    pthread_mutex_unlock(&rv->lock);
}



typedef struct printer {
    rendezvous_t rv;
    struct printer *backup;
    int id;
    int remaining_lines;
} printer_t;

typedef struct print_args {
    struct printer *printer;
    const char* line;
} print_args_t;

int print_line(printer_t *printer, const char* line) {
    print_args_t args;
    args.printer = printer;
    args.line = line;
    return enter_rendezvous(&printer->rv, &args);
}

int accept_print(void* data) {
    
    print_args_t *args = (print_args_t*)data;
    printer_t *printer = args->printer;
    const char* line = args->line;

    if (printer->remaining_lines) {
        
        printf("%d: ", printer->id);
        while (*line != '\0') {
            putchar(*line++);
        }
        putchar('\n');
        printer->remaining_lines--;
        return 1;
    }
    else if (printer->backup) {
        
        return print_line(printer->backup, line);
    }
    else {
        
        return -1;
    }
}

printer_t backup_printer = {
    .rv = RENDEZVOUS_INITILIZER(accept_print),
    .backup = NULL,
    .id = 2,
    .remaining_lines = 5,
};

printer_t main_printer = {
    .rv = RENDEZVOUS_INITILIZER(accept_print),
    .backup = &backup_printer,
    .id = 1,
    .remaining_lines = 5,
};

void* printer_thread(void* thread_data) {
    printer_t *printer = (printer_t*) thread_data;
    while (1) {
        accept_rendezvous(&printer->rv);
    }
}

typedef struct poem {
    char* name;
    char* lines[];
} poem_t;

poem_t humpty_dumpty = {
    .name = "Humpty Dumpty",
    .lines = {
        "Humpty Dumpty sat on a wall.",
        "Humpty Dumpty had a great fall.",
        "All the king's horses and all the king's men",
        "Couldn't put Humpty together again.",
        ""
    },
};

poem_t mother_goose = {
    .name = "Mother Goose",
    .lines = {
        "Old Mother Goose",
        "When she wanted to wander,",
        "Would ride through the air",
        "On a very fine gander.",
        "Jack's mother came in,",
        "And caught the goose soon,",
        "And mounting its back,",
        "Flew up to the moon.",
        ""
    },
};

void* poem_thread(void* thread_data) {
    poem_t *poem = (poem_t*)thread_data;

    for (unsigned i = 0; poem->lines[i] != ""; i++) {
        int ret = print_line(&main_printer, poem->lines[i]);
        if (ret < 0) {
            printf("      %s out of ink!\n", poem->name);
            exit(1);
        }
    }
    return NULL;
}

int main(void)
{
    pthread_t threads[4];

    pthread_create(&threads[0], NULL, poem_thread,    &humpty_dumpty);
    pthread_create(&threads[1], NULL, poem_thread,    &mother_goose);
    pthread_create(&threads[2], NULL, printer_thread, &main_printer);
    pthread_create(&threads[3], NULL, printer_thread, &backup_printer);

    pthread_join(threads[0], NULL);
    pthread_join(threads[1], NULL);
    pthread_cancel(threads[2]);
    pthread_cancel(threads[3]);

    return 0;
}







Python: 
from __future__ import annotations

import asyncio
import sys

from typing import Optional
from typing import TextIO


class OutOfInkError(Exception):
    


class Printer:
    def __init__(self, name: str, backup: Optional[Printer]):
        self.name = name
        self.backup = backup

        self.ink_level: int = 5
        self.output_stream: TextIO = sys.stdout

    async def print(self, msg):
        if self.ink_level <= 0:
            if self.backup:
                await self.backup.print(msg)
            else:
                raise OutOfInkError(self.name)
        else:
            self.ink_level -= 1
            self.output_stream.write(f"({self.name}): {msg}\n")


async def main():
    reserve = Printer("reserve", None)
    main = Printer("main", reserve)

    humpty_lines = [
        "Humpty Dumpty sat on a wall.",
        "Humpty Dumpty had a great fall.",
        "All the king's horses and all the king's men,",
        "Couldn't put Humpty together again.",
    ]

    goose_lines = [
        "Old Mother Goose,",
        "When she wanted to wander,",
        "Would ride through the air,",
        "On a very fine gander.",
        "Jack's mother came in,",
        "And caught the goose soon,",
        "And mounting its back,",
        "Flew up to the moon.",
    ]

    async def print_humpty():
        for line in humpty_lines:
            try:
                task = asyncio.Task(main.print(line))
                await task
            except OutOfInkError:
                print("\t Humpty Dumpty out of ink!")
                break

    async def print_goose():
        for line in goose_lines:
            try:
                task = asyncio.Task(main.print(line))
                await task
            except OutOfInkError:
                print("\t Mother Goose out of ink!")
                break

    await asyncio.gather(print_goose(), print_humpty())


if __name__ == "__main__":
    asyncio.run(main(), debug=True)