SERVER CONCORRENTE MULTITHREADED CON POOL DI THREAD Si richiede di realizzare un server concorrente che sfrutta un pool di thread precostituito. Il server accetta stringhe provenienti dai client e le stampa a video. Richiede il numero di porta su cui mettersi in ascolto come parametro da linea di comando. Il thread main prealloca un pool di thread gestori. Dopodiché rimane perennemente in attesa di nuove connessioni e le smista al pool di thread che hanno il compito di gestire le richieste. Il thread main assegna ad un thread gestore (libero) del pool ogni nuova connessione che riceve. Il numero di thread del pool è specificato a tempo di compilazione (è pari alla costante NUM_THREAD e non varia durante l’esecuzione del programma). Possiamo schematizzare lo schema di funzionamento del server nel seguente modo: 1. il thread main crea NUM_THREAD thread gestori; 2. il thread main si mette in attesa delle connessioni in ingresso; 3. quando riceve una connessione in ingresso, il thread main: a. controlla il numero di thread occupati (ovvero quelli che stanno attualmente eseguendo una richiesta); b. se tutti i thread del pool sono occupati si blocca in attesa che uno diventi libero; c. se c’è almeno un thread libero ne sceglie uno (senza nessuna politica particolare) e lo attiva; 4. il thread gestore contiene un ciclo infinito: a. si blocca finché non gli viene assegnata una richiesta; b. riceve il comando da un client; c. esegue la richiesta del client; d. indica di essere libero; Suggerimenti: 1. si consideri un array di semafori di sincronizzazione (uno per ciascun thread del pool): ogni thread gestore si blocca inizialmente sul suo semaforo; al sopraggiungere di una richiesta, il thread main individua un thread gestore che può soddisfarla e lo sblocca. 2. si consideri un array di interi in cui l'intero i-esimo mi dice se il thread con indice i è occupato (0) o libero (1): il main scorrerà questa struttura dati per individuare i thread attualmente occupati. Ovviamente quando una richiesta viene assegnata ad un thread gestore, tale thread viene indicato come occupato; al termine delle operazioni da parte del thread gestore, questo deve comunicare di essere libero. 3. si consideri un array di interi in cui l'intero i-esimo contiene l'id del connected socket che il thread gestore con indice i deve usare. In pratica, nel momento in cui il main accetta una richiesta, il thread main pone nella struttura dati, in posizione i, l'id del connected socket che l'i-esimo thread gestore deve utilizzare per comunicare con il client. 4. Se tutti i thread sono occupati il thread main si blocca, in attesa che un thread si liberi. Serve pertanto un apposito meccanismo (un semaforo o una variabile condition) per memorizzare il numero di thread liberi (inizialmente sono tutti disponibili) e che permetta al thread di bloccarsi quando non ci sono thread liberi. 5. DA RICORDARE: ogni risorsa condivisa (acceduta da più thread) deve essere protetta da opportuni meccanismi semaforici!