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;



        .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(, 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 = ,
    .lines = {
        ,
        ,
        ,
        ,
        
    },
};

poem_t mother_goose = {
    .name = ,
    .lines = {
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        
    },
};

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