Task: Rendezvous




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()
}




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)