Un programma multi-thread simula il funzionamento di un ristorante. Il
ristorante ha N2 tavoli da 2 posti (tipo T2), N4 tavoli da 4 posti
(tipo T4) e N6 tavoli da 6 posti (tipo T6). Inizialmente tutti i
tavoli sono liberi. Ogni thread simula un gruppo di avventori, e ha il
seguente codice:

void *customers(void *arg)
{
	int n = ....;               // Numero di clienti nel gruppo, da 2 a 6
	int k = request_table(n);
	...
	release_table(k);
	return 0;
}

La funzione request_table() prende come parametro il numero di clienti
nel gruppo, e ritorna il tipo di tavolo allocato (secondo la
convenzione T2 = 0; T4 = 1; T6 = 2). La funzione è bloccante se non ci
sono tavoli liberi in grado di contenere tutti i clienti del
gruppo. Un gruppo non si può dividere in 2 tavoli, e un tavolo può
contenere al più un gruppo.  Un gruppo di 2 persone può sedersi in
qualunque tavolo, i gruppi di 3 o 4 persone si possono sedere solo in
un T4 o T6, i gruppi di 5 o 6 persone solo in un tavolo
T6. Naturalmente, se arriva un gruppo di 2 persone, queste si
siederanno preferibilmente in un tavolo T2, se non ce ne sono liberi
in un T4, e solo in ultima istanza in un T6. Idem, per i gruppi di 3 o
4 persone.  Se ci sono thread bloccati, quando si libera un tavolo
verranno svegliati preferibilmente i gruppi con un numero di avventori
simile alla tipologia di tavolo. Ad esempio, se si libera un T4, verrà
svegliato un gruppo di 3 o 4 persone, e solo se non ci sono gruppi di
questo tipo, verrà svegliato un gruppo da 2 persone.

Scrivere la struttura dati, e le funzioni request_table() e
release_table().
