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;
}




Go: package main

import (
    "errors"
    "fmt"
    "strings"
    "sync"
)

var hdText = `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.`

var mgText = `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.`

func main() {
    reservePrinter := startMonitor(newPrinter(5), nil)
    mainPrinter := startMonitor(newPrinter(5), reservePrinter)
    var busy sync.WaitGroup
    busy.Add(2)
    go writer(mainPrinter, "hd", hdText, &busy)
    go writer(mainPrinter, "mg", mgText, &busy)
    busy.Wait()
}




type printer func(string) error





func newPrinter(ink int) printer {
    return func(line string) error {
        if ink == 0 {
            return eOutOfInk
        }
        for _, c := range line {
            fmt.Printf("%c", c)
        }
        fmt.Println()
        ink--
        return nil
    }
}

var eOutOfInk = errors.New("out of ink")















type rSync struct {
    call     chan string
    response chan error
}






func (r *rSync) print(data string) error {
    r.call <- data      
    return <-r.response 
}




func monitor(hardPrint printer, entry, reserve *rSync) {
    for {
        
        
        data := <-entry.call
        
        
        

        
        switch err := hardPrint(data); {

        
        case err == nil:
            entry.response <- nil 

        case err == eOutOfInk && reserve != nil:
            
            
            
            
            entry.response <- reserve.print(data)

        default:
            entry.response <- err 
        }
        
    }
}






func startMonitor(p printer, reservePrinter *rSync) *rSync {
    entry := &rSync{make(chan string), make(chan error)}
    go monitor(p, entry, reservePrinter)
    return entry
}






func writer(printMonitor *rSync, id, text string, busy *sync.WaitGroup) {
    for _, line := range strings.Split(text, "\n") {
        if err := printMonitor.print(line); err != nil {
            fmt.Printf("**** writer task %q terminated: %v ****\n", id, err)
            break
        }
    }
    busy.Done()
}