Compare commits
10 Commits
cbef01f7be
...
032faead63
| Author | SHA1 | Date | |
|---|---|---|---|
| 032faead63 | |||
| 6fef3a3c7c | |||
| 11879cf252 | |||
| f228ee5462 | |||
| 8511272a65 | |||
| 7a1ad2a61c | |||
| db82cf8a76 | |||
| 4283d5450e | |||
| 27be55993c | |||
| a1e7c2e995 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -169,4 +169,5 @@ flycheck_*.el
|
||||
*.aux
|
||||
*.log
|
||||
*.synctex.gz
|
||||
*.toc
|
||||
*.toc
|
||||
*.pdf
|
||||
1
Makefile
1
Makefile
@ -154,7 +154,6 @@ test3: all
|
||||
@echo "backlog = 100" >> $(BUILD_DIR)/config.ini
|
||||
$(BUILD_DIR)/server $(BUILD_DIR)/config.ini &
|
||||
bash scripts/test3.sh &
|
||||
@echo ""
|
||||
@echo "waiting for 30 seconds"
|
||||
@sleep 30
|
||||
pkill --signal SIGINT -f $(BUILD_DIR)/server
|
||||
|
||||
178
README.md
178
README.md
@ -1,3 +1,177 @@
|
||||
# Progetto
|
||||
# Introduzione
|
||||
|
||||
Progetto del Laboratorio di Sistemi Operativi
|
||||
Il progetto realizzato consiste in un file storage gestito da un server
|
||||
multithreaded e un client che possono comunicarsi attraverso le funzioni
|
||||
implementate dall'API. Il codice è presente sulla repository privata al
|
||||
link: [tautocrono.it](http://tautocrono.it).\
|
||||
Si può accedere usando le credenziali:
|
||||
|
||||
::: center
|
||||
Username Password
|
||||
---------- -----------------
|
||||
Guest
|
||||
:::
|
||||
|
||||
Il progetto è compilato in modo automatico tramite makefile con i
|
||||
target: all, test1, test2, test3. Il target all fa partire un'altro
|
||||
sottoprocesso make che eseguirà il target multi con degli argomenti
|
||||
aggiuntivi, tipo `-j` in modo da rendere la compilazione multiprocesso.
|
||||
Ci sono anche i target clean e cleanall che rimuovono gli eseguibili
|
||||
creati con i precedenti target.
|
||||
|
||||
Sono inclusi anche dei file per testare le funzionalità del client e del
|
||||
server nella cartella *testFiles*.
|
||||
|
||||
Gli oggetti vengono creati nella directory *obj/*, mentre gli eseguibili
|
||||
vengono creati nella directory *build/*. La directory *src/* contiene il
|
||||
codice che fa parte del main del server e del client; il resto del
|
||||
codice è contenuto nella directory *lib/*.
|
||||
|
||||
È stata implementata sia la politica di rimpiazzamento FIFO che la
|
||||
politica LRU.
|
||||
|
||||
# Server
|
||||
|
||||
Il server prende come unico argomento il path al file di configurazione.
|
||||
Un esempio di file di configurazione è `config.ini` presente nella root
|
||||
del progetto. La struttura del file è analoga a quella di un file
|
||||
[INI](https://en.wikipedia.org/wiki/INI_file), quindi l'ordine degli
|
||||
argomenti dentro a sezione è irrilevante. Il file di configurazione
|
||||
viene letto da una libreria di \"*terza parte*\" ottenuta da
|
||||
[rxi/ini](https://github.com/rxi/ini). Si sarebbe potuto usare la
|
||||
libreria [Glib](https://gitlab.gnome.org/GNOME/glib) tuttavia si è
|
||||
riscontrata una memory leak con valgrind.
|
||||
|
||||
La libreria threadpool e la libreria conn sono state tratte dalla
|
||||
soluzione dell'esercitazione 11 pubblicata sulla pagina del corso. Si è
|
||||
preferito l'utilizzo di `strsep` invece di `strtok` o `strtok_r` sia nel
|
||||
programma server che client. Il codice è stato adattato leggermente da
|
||||
quello presente nella libreria C GNU
|
||||
([strsep](https://sourceware.org/git/?p=glibc.git;a=blob;f=string/strsep.c;h=b534a1ec17fd2e91087af04abe1c3f1ac3e74ce0;hb=HEAD))
|
||||
in modo da utilizzare la funzione omonima su piattaforme BSD e MacOS.
|
||||
|
||||
## Main
|
||||
|
||||
Il thread principale accetta le connessioni in arrivo da i client su un
|
||||
socket di tipo `AF_UNIX` con nome di default *socket*. Viene interrotto
|
||||
dal thread `sighandler_thread` se ricevuto un segnale fra: `SIGHUP`,
|
||||
`SIGINT` o `SIGQUIT`.
|
||||
|
||||
## serverWorker
|
||||
|
||||
Il thread main dopo aver accettato la connessione di un client crea un
|
||||
thread \"*worker*\" che aggiunge alla threadpool (definita nei file
|
||||
omonimi). Ogni worker serve una singola richiesta da parte di un
|
||||
qualsiasi client connesso. Se le richieste contemporanee dei client
|
||||
fossero maggiori della dimensione della threadpool, le richieste vengono
|
||||
accodate in una *pending queue* di dimensione stabilita nel file di
|
||||
configurazione del server. La singola richiesta viene letta dal worker
|
||||
thread e viene poi eseguita la funzione associata alla richiesta
|
||||
presente nella libreria *apiFile*. Alcune di queste operazioni possono
|
||||
richiedere la presa di una lock associata a un file, quindi per gestire
|
||||
queste richieste si è fatto uso di una struttura di dati interna
|
||||
`waiting_t`. La richiesta accodata non è quindi gestita dal thread
|
||||
corrente, ma dal thread che rilascerà la lock al file associato. Si
|
||||
evitano quindi *polling* o l'utilizzo di *condition*.
|
||||
|
||||
## sighandler_thread
|
||||
|
||||
Il thread gestisce i segnali in arrivo al server. Se il segnale ricevuto
|
||||
è di tipo `SIGINT` o `SIGQUIT`, il server termina prima possibile. Se il
|
||||
segnale ricevuto è del tipo `SIGHUP` invece si attende che ogni worker
|
||||
abbiano servito le richieste dei client precedentemente connessi.
|
||||
|
||||
# Storage
|
||||
|
||||
Il server gestisce la memorizzazione dei file tramite la libreria
|
||||
*fileQueue*. Nel header file *fileQueue.h* la costante `ALGORITHM`
|
||||
imposta la politica di rimpiazzamento. Le funzioni che differiscono in
|
||||
comportamento sono: `dequeueN`, `lockFileInQueue`, `unlockFileInQueue`,
|
||||
`openFileInQueue`, `find`, `request`, `searchFile`. Infatti l'elemento
|
||||
che richiedono come input viene accodato alla fine della struttura nella
|
||||
politica LRU. Le altre funzioni invece non modificano l'ordine degli
|
||||
elementi.
|
||||
|
||||
La struttura principale è una linked list chiamata `nodeT` che contiene
|
||||
i dati del file associato e un puntatore al prossimo elemento. Dato che
|
||||
l'operazione di scrittura di un file in memoria non è atomica, si è
|
||||
deciso di usare due misure dell'occupazione della memoria di un file:
|
||||
`size` e `valid`. L'unico momento in cui potrebbero essere differenti è
|
||||
quando il client ha richiesto la scrittura di dati, comunicando la
|
||||
dimensione, quindi il server \"alloca\" tale dimensione come `size`,
|
||||
lasciando invariato la dimensione `valid`. Dopo che il client ha inviato
|
||||
i dati da inserire nel file, le dimensioni dovrebbero di nuovo
|
||||
combaciare. Una singola operazione di scrittura può richiedere più di un
|
||||
file rimosso per aver abbastanza memoria libera; questa operazione viene
|
||||
attuata atomicamente e viene considerata come un'unica operazione di
|
||||
rimpiazzamento.
|
||||
|
||||
# Logging
|
||||
|
||||
Il server usa la libreria \"`taglialegna`\" per scrivere i messaggi di
|
||||
log su un file specificato nel file di configurazione. Le scritture
|
||||
vengono sincronizzate tramite lock. Sono state scritte due funzioni che
|
||||
permettono la scrittura: `taglia_write`, `taglia_log`. `taglia_log`
|
||||
scrive anche un timestamp prima del messaggio di log. All'inizio
|
||||
dell'esecuzione del server vengono stampate alcune informazioni lette
|
||||
dal file di configurazione, come ad esempio la dimensione del threadpool
|
||||
o la dimensione massima della struttura di cache del server. Al termine
|
||||
vengono invece scritti alcuni dati di sunto delle operazioni, ad esempio
|
||||
il numero massimo di file memorizzati o la dimensione massima raggiunta.
|
||||
|
||||
# Client
|
||||
|
||||
Oltre alle funzioni di API richieste dalla specifica è stata
|
||||
implementata la funzione `setDirectory`, perchè si è interpretata come
|
||||
se le opzioni `-d` e `-D` dovessero poter essere associate a più di un
|
||||
comando. Tuttavia questa funzionalità non è presente nel client in modo
|
||||
diretto dato che vengono inserite `-d`/`-D /dev/null` fra due opzioni
|
||||
`-w`/`-W` o `-r`/`-R` successive. L'opzione `-d /dev/null` o
|
||||
`-D /dev/null` è stata ottimizzata leggermente in modo da non fare
|
||||
chiamate di sistema.\
|
||||
Il client esegue prima l'opzione `-f`, in seguito l'opzione `-t`, poi in
|
||||
ordine tutti gli altri argomenti. In caso di errore durante la scrittura
|
||||
su un file dopo la creazione, il client richiede la rimozione del file.\
|
||||
Per la comunicazione con il server si è optato per un protocollo
|
||||
ispirato a quello html o ftp, costituito da due cifre, la prima denota
|
||||
il risultato (positive completion/transient negative
|
||||
completion/permanent negative completion), la seconda invece è una
|
||||
specifica della prima. Non tutte le combinazioni sono state usate.
|
||||
|
||||
::: center
|
||||
Number Meaning
|
||||
---------------------------- ------------------------------------------------------------
|
||||
2[\*]{style="color: gray"} positive completion reply
|
||||
4[\*]{style="color: gray"} transient negative completion reply (command not executed)
|
||||
5[\*]{style="color: gray"} permanent negative completion reply (errors)
|
||||
[\*]{style="color: gray"}0 syntax
|
||||
[\*]{style="color: gray"}1 information
|
||||
[\*]{style="color: gray"}2 connection
|
||||
[\*]{style="color: gray"}5 file system
|
||||
:::
|
||||
|
||||
Di seguito una tabella in cui si associa ogni numero al codice usato:
|
||||
|
||||
::: center
|
||||
Number Meaning Code
|
||||
--------------------------- --------------------------------- ----------------------------
|
||||
20 OK `MEOK`
|
||||
[21]{style="color: gray"} [not used]{style="color: gray"} [`-`]{style="color: gray"}
|
||||
[22]{style="color: gray"} [not used]{style="color: gray"} [`-`]{style="color: gray"}
|
||||
25 file purged `MEFP`
|
||||
[40]{style="color: gray"} [not used]{style="color: gray"} [`-`]{style="color: gray"}
|
||||
[41]{style="color: gray"} [not used]{style="color: gray"} [`-`]{style="color: gray"}
|
||||
42 shutting down `MESD`
|
||||
45 requested file action not taken `MENT`
|
||||
50 syntax error `MESY`
|
||||
[51]{style="color: gray"} [not used]{style="color: gray"} [`-`]{style="color: gray"}
|
||||
[52]{style="color: gray"} [not used]{style="color: gray"} [`-`]{style="color: gray"}
|
||||
55 server error `MESE`
|
||||
:::
|
||||
|
||||
Il client invia per primo la dimensione del messaggio come `long`, poi
|
||||
il messaggio. Il server risponde prima con due caratteri che
|
||||
simboleggiano is response number, seguito dal errno se si è riscontrato
|
||||
un errore, oppure i file richiesti o espulsi. Il server per inviare i
|
||||
file richiesti o espulsi, prima invia il numero di file, poi per ogni
|
||||
file la dimensione come `int64_t` e poi successivamente il file.
|
||||
|
||||
BIN
Relazione/Relazione.pdf
Normal file
BIN
Relazione/Relazione.pdf
Normal file
Binary file not shown.
@ -14,14 +14,14 @@
|
||||
\usepackage{listings} %% code block
|
||||
|
||||
\lstset{
|
||||
language=C,
|
||||
showspaces=false,
|
||||
basicstyle=\small\ttfamily,
|
||||
numbers=left,
|
||||
numberstyle=\tiny,
|
||||
breaklines=true,
|
||||
postbreak=\mbox{\textcolor{red}{$\hookrightarrow$}\space},
|
||||
backgroundcolor = \color{lightgray},
|
||||
language=C,
|
||||
showspaces=false,
|
||||
basicstyle=\small\ttfamily,
|
||||
numbers=left,
|
||||
numberstyle=\tiny,
|
||||
breaklines=true,
|
||||
postbreak=\mbox{\textcolor{red}{$\hookrightarrow$}\space},
|
||||
backgroundcolor = \color{lightgray},
|
||||
}
|
||||
|
||||
\usepackage{enumitem} %% for lists and enumerating
|
||||
@ -61,16 +61,16 @@
|
||||
%% usage: \ieb{Class:} for simple item
|
||||
%% \ieb[4cm]{Class:} for specific size of box
|
||||
\newcommand{\ieb}[2][2cm]{
|
||||
\makebox[#1][l]{\emph{#2}}
|
||||
\makebox[#1][l]{\emph{#2}}
|
||||
} %% TODO: replace with description environment (? maybe)
|
||||
|
||||
%---------------------------------%
|
||||
|
||||
\title{Progetto di Laboratorio di Sistemi Operativi}
|
||||
\author{
|
||||
Elvis Rossi - Matricola 561394
|
||||
Elvis Rossi - Matricola 561394
|
||||
}
|
||||
\date{\today}
|
||||
\date{May 20, 2022}
|
||||
|
||||
%---------------------------------%
|
||||
|
||||
@ -85,14 +85,14 @@
|
||||
|
||||
\section{Introduzione}
|
||||
Il progetto realizzato consiste in un file storage gestito da un server multithreaded e un client che possono comunicarsi attraverso le funzioni implementate dall'API.
|
||||
Il codice è presente sulla repository privata al link: \href{http://google.com}{TODO}.\\
|
||||
Il codice è presente sulla repository privata al link: \href{http://tautocrono.it}{tautocrono.it}.\\
|
||||
Si può accedere usando le credenziali:
|
||||
|
||||
\begin{center}
|
||||
\begin{tabular}{||c|c||}
|
||||
\hline
|
||||
\hline
|
||||
Username & Password \\ \hline
|
||||
Guest & CNs7Vbx3vLJQ2aK \\ \hline
|
||||
Guest & \\ \hline
|
||||
\end{tabular}
|
||||
\end{center}
|
||||
|
||||
@ -101,15 +101,21 @@ Ci sono anche i target clean e cleanall che rimuovono gli eseguibili creati con
|
||||
|
||||
Sono inclusi anche dei file per testare le funzionalità del client e del server nella cartella \textit{testFiles}.
|
||||
|
||||
Gli oggetti vengono creati nella directory \textit{obj/}, mentre gli eseguibili vengono creati nella directory \textit{build/}.
|
||||
La directory \textit{src/} contiene il codice che fa parte del main del server e del client; il resto del codice è contenuto nella directory \textit{lib/}.
|
||||
|
||||
È stata implementata sia la politica di rimpiazzamento FIFO che la politica LRU.
|
||||
|
||||
|
||||
%% ------------------------- %%
|
||||
|
||||
\section{Server}
|
||||
Il server prende come unico argomento il path al file di configurazione. Un esempio di file di configurazione è \texttt{config.ini} presente nella root del progetto. La struttura del file è analoga a quella di un file \href{https://en.wikipedia.org/wiki/INI_file}{INI}, quindi l'ordine degli argomenti è irrilevante.
|
||||
Il file di configurazione viene letto da una libreria di "\textit{terza parte}" ottenuta da \href{https://github.com/rxi/ini}{rxi/ini}.
|
||||
Il server prende come unico argomento il path al file di configurazione. Un esempio di file di configurazione è \texttt{config.ini} presente nella root del progetto. La struttura del file è analoga a quella di un file \href{https://en.wikipedia.org/wiki/INI_file}{INI}, quindi l'ordine degli argomenti dentro a sezione è irrilevante.
|
||||
Il file di configurazione viene letto da una libreria di "\textit{terza parte}" ottenuta da \href{https://github.com/rxi/ini}{rxi/ini}. Si sarebbe potuto usare la libreria \href{https://gitlab.gnome.org/GNOME/glib}{Glib} tuttavia si è riscontrata una memory leak con valgrind.
|
||||
|
||||
La libreria threadpool e la libreria conn sono state tratte dalla soluzione dell'esercitazione 11 pubblicata sulla pagina del corso.
|
||||
Si è preferito l'utilizzo di \texttt{strsep} invece di \texttt{strtok} o \texttt{strtok\_r} sia nel programma server che client.
|
||||
Il codice è stato adattato leggermente da quello presente nella libreria C GNU (\href{https://sourceware.org/git/?p=glibc.git;a=blob;f=string/strsep.c;h=b534a1ec17fd2e91087af04abe1c3f1ac3e74ce0;hb=HEAD}{strsep}) in modo da utilizzare la funzione omonima su piattaforme BSD.
|
||||
Il codice è stato adattato leggermente da quello presente nella libreria C GNU (\href{https://sourceware.org/git/?p=glibc.git;a=blob;f=string/strsep.c;h=b534a1ec17fd2e91087af04abe1c3f1ac3e74ce0;hb=HEAD}{strsep}) in modo da utilizzare la funzione omonima su piattaforme BSD e MacOS.
|
||||
|
||||
|
||||
\subsection{Main}
|
||||
@ -134,8 +140,10 @@ Se il segnale ricevuto è del tipo \texttt{SIGHUP} invece si attende che ogni wo
|
||||
\section{Storage}
|
||||
|
||||
Il server gestisce la memorizzazione dei file tramite la libreria \textit{fileQueue}.
|
||||
Nel header file \textit{fileQueue.h} la costante \texttt{ALGORITHM} imposta la politica di rimpiazzamento. Le funzioni che differiscono in comportamento sono: \texttt{dequeueN}, \texttt{lockFileInQueue}, \texttt{unlockFileInQueue}, \texttt{openFileInQueue}, \texttt{find}, \texttt{request}, \texttt{searchFile}. Infatti l'elemento che richiedono come input viene accodato alla fine della struttura nella politica LRU. Le altre funzioni invece non modificano l'ordine degli elementi.
|
||||
|
||||
La struttura principale è una linked list chiamata \texttt{nodeT} che contiene i dati del file associato e un puntatore al prossimo elemento.
|
||||
Dato che l'operazione di scrittura di un file in memoria non è atomica, si è deciso di usare due misure dell'occupazione della memoria da un file: \texttt{size} e \texttt{valid}.
|
||||
Dato che l'operazione di scrittura di un file in memoria non è atomica, si è deciso di usare due misure dell'occupazione della memoria di un file: \texttt{size} e \texttt{valid}.
|
||||
L'unico momento in cui potrebbero essere differenti è quando il client ha richiesto la scrittura di dati, comunicando la dimensione, quindi il server "alloca" tale dimensione come \texttt{size}, lasciando invariato la dimensione \texttt{valid}. Dopo che il client ha inviato i dati da inserire nel file, le dimensioni dovrebbero di nuovo combaciare.
|
||||
Una singola operazione di scrittura può richiedere più di un file rimosso per aver abbastanza memoria libera; questa operazione viene attuata atomicamente e viene considerata come un'unica operazione di rimpiazzamento.
|
||||
|
||||
@ -149,7 +157,7 @@ Le scritture vengono sincronizzate tramite lock.
|
||||
Sono state scritte due funzioni che permettono la scrittura: \texttt{taglia\_write}, \texttt{taglia\_log}.
|
||||
\texttt{taglia\_log} scrive anche un timestamp prima del messaggio di log.
|
||||
All'inizio dell'esecuzione del server vengono stampate alcune informazioni lette dal file di configurazione, come ad esempio la dimensione del threadpool o la dimensione massima della struttura di cache del server.
|
||||
Al termine vengono invece scritti alcuni dati di sunto delle operazioni, come ad esempio il numero massimo di file memorizzati o la dimensione massima raggiunta.
|
||||
Al termine vengono invece scritti alcuni dati di sunto delle operazioni, ad esempio il numero massimo di file memorizzati o la dimensione massima raggiunta.
|
||||
|
||||
|
||||
%% ------------------------- %%
|
||||
@ -158,6 +166,11 @@ Al termine vengono invece scritti alcuni dati di sunto delle operazioni, come ad
|
||||
|
||||
Oltre alle funzioni di API richieste dalla specifica è stata implementata la funzione \texttt{setDirectory}, perchè si è interpretata come se le opzioni \texttt{-d} e \texttt{-D} dovessero poter essere associate a più di un comando. Tuttavia questa funzionalità non è presente nel client in modo diretto dato che vengono inserite \texttt{-d}/\texttt{-D /dev/null} fra due opzioni \texttt{-w}/\texttt{-W} o \texttt{-r}/\texttt{-R} successive.
|
||||
L'opzione \texttt{-d /dev/null} o \texttt{-D /dev/null} è stata ottimizzata leggermente in modo da non fare chiamate di sistema.
|
||||
\\
|
||||
|
||||
Il client esegue prima l'opzione \texttt{-f}, in seguito l'opzione \texttt{-t}, poi in ordine tutti gli altri argomenti.
|
||||
In caso di errore durante la scrittura su un file dopo la creazione, il client richiede la rimozione del file.
|
||||
\\
|
||||
|
||||
Per la comunicazione con il server si è optato per un protocollo ispirato a quello html o ftp, costituito da due cifre, la prima denota il risultato (positive completion/transient negative completion/permanent negative completion), la seconda invece è una specifica della prima. Non tutte le combinazioni sono state usate.
|
||||
|
||||
@ -180,23 +193,23 @@ Di seguito una tabella in cui si associa ogni numero al codice usato:
|
||||
|
||||
\begin{center}
|
||||
\begin{tabular}{|c|l|c|}
|
||||
\hline
|
||||
Number & Meaning & Code \\ \hline
|
||||
\hline
|
||||
\hline
|
||||
Number & Meaning & Code \\ \hline
|
||||
\hline
|
||||
20 & OK & \texttt{MEOK} \\ \hline
|
||||
\gry{21} & \gry{not used} & \gry{\texttt{-}} \\ \hline
|
||||
\gry{22} & \gry{not used} & \gry{\texttt{-}} \\ \hline
|
||||
25 & file purged & \texttt{MEFP} \\ \hline
|
||||
\hline
|
||||
\gry{40} & \gry{not used} & \gry{\texttt{-}} \\ \hline
|
||||
\gry{41} & \gry{not used} & \gry{\texttt{-}} \\ \hline
|
||||
42 & shutting down & \texttt{MESD} \\ \hline
|
||||
45 & requested file action not taken & \texttt{MENT} \\ \hline
|
||||
\hline
|
||||
50 & syntax error & \texttt{MESY} \\ \hline
|
||||
\gry{51} & \gry{not used} & \gry{\texttt{-}} \\ \hline
|
||||
\gry{52} & \gry{not used} & \gry{\texttt{-}} \\ \hline
|
||||
55 & server error & \texttt{MESE} \\ \hline
|
||||
\gry{21} & \gry{not used} & \gry{\texttt{-}} \\ \hline
|
||||
\gry{22} & \gry{not used} & \gry{\texttt{-}} \\ \hline
|
||||
25 & file purged & \texttt{MEFP} \\ \hline
|
||||
\hline
|
||||
\gry{40} & \gry{not used} & \gry{\texttt{-}} \\ \hline
|
||||
\gry{41} & \gry{not used} & \gry{\texttt{-}} \\ \hline
|
||||
42 & shutting down & \texttt{MESD} \\ \hline
|
||||
45 & requested file action not taken & \texttt{MENT} \\ \hline
|
||||
\hline
|
||||
50 & syntax error & \texttt{MESY} \\ \hline
|
||||
\gry{51} & \gry{not used} & \gry{\texttt{-}} \\ \hline
|
||||
\gry{52} & \gry{not used} & \gry{\texttt{-}} \\ \hline
|
||||
55 & server error & \texttt{MESE} \\ \hline
|
||||
\end{tabular}
|
||||
\end{center}
|
||||
|
||||
@ -205,55 +218,7 @@ Il server risponde prima con due caratteri che simboleggiano is response number,
|
||||
Il server per inviare i file richiesti o espulsi, prima invia il numero di file, poi per ogni file la dimensione come \texttt{int64\_t} e poi successivamente il file.
|
||||
|
||||
|
||||
|
||||
%% ------------------------- %%
|
||||
|
||||
|
||||
\end{document}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -666,6 +666,15 @@ int writeFile(const char* pathname, const char* dirname) {
|
||||
}
|
||||
}
|
||||
}
|
||||
freeResponse(res);
|
||||
free(res);
|
||||
res = calloc(1, sizeof(response_t));
|
||||
if(!res){
|
||||
perror("writeFile: calloc");
|
||||
free(content);
|
||||
free(cmd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* send file to server */
|
||||
if (writen(fd_skt, content, size) == -1) {
|
||||
@ -677,7 +686,6 @@ int writeFile(const char* pathname, const char* dirname) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/* reciveData(, 0) because if we get a MEOK we don't expect files */
|
||||
if(reciveData(res, 0) < 0) {
|
||||
/* errno is set by reciveData */
|
||||
|
||||
@ -381,7 +381,7 @@ void readFile(char *filepath, queueT *q, long fd_c, taglia_t *taglia) {
|
||||
}
|
||||
|
||||
if(f->open == 0) { /* file not already open */
|
||||
n += snprintf(tmp_buf+n, m-n, "Client %ld ha richiesto una readFile sul file \"%s\" e' terminata con errore, file non aperto\n",
|
||||
n += snprintf(tmp_buf+n, m-n, "Client %ld ha richiesto una readFile sul file \"%s\" e' terminata con errore, file non precedentemente aperto\n",
|
||||
fd_c,
|
||||
filepath);
|
||||
errno = EPERM;
|
||||
@ -417,32 +417,33 @@ void readNFiles(int num, queueT *q, long fd_c, taglia_t *taglia) {
|
||||
|
||||
int oldNum = num;
|
||||
int64_t ntosend = (num<=0 || num>getLen(q))? getLen(q) : num;
|
||||
fileT **toSend = calloc(ntosend, sizeof(fileT *));
|
||||
fileT **toSend = NULL;
|
||||
|
||||
n += snprintf(tmp_buf+n, m-n, "Client %ld ha richiesto una readNFile (n = %d) e' terminata con successo. File inviati:\n",
|
||||
fd_c,
|
||||
num);
|
||||
/* extract n files, we dont check if the client can actually modify
|
||||
* the files since we add them back immediatly */
|
||||
/* extract n files, but dont actually dequeue them */
|
||||
toSend = request(q, (int) ntosend);
|
||||
if(toSend == NULL) {
|
||||
n = 0;
|
||||
n += snprintf(tmp_buf+n, m-n, "Client %ld ha richiesto una readNFiles (n = %d) e' terminato con errore del server\n",
|
||||
fd_c,
|
||||
oldNum);
|
||||
perror("readNFiles: request");
|
||||
serror(MESE, fd_c, taglia, tmp_buf);
|
||||
return;
|
||||
}
|
||||
|
||||
size_t tot = 0;
|
||||
for(int i=0;i<ntosend;++i) {
|
||||
toSend[i] = dequeue(q);
|
||||
if(!toSend[i]) {
|
||||
if(toSend[i] == NULL) {
|
||||
n = 0;
|
||||
n += snprintf(tmp_buf+n, m-n, "Client %ld ha richiesto una readNFiles (n = %d) e' terminato con errore del server\n",
|
||||
n += snprintf(tmp_buf+n, m-n, "Client %ld ha richiesto una readNFiles (n = %d) e' terminato con errore del server (file estratti [%d] inferiori a file richiesti [%ld])\n",
|
||||
fd_c,
|
||||
oldNum);
|
||||
perror("readNFiles: dequeue");
|
||||
serror(MESE, fd_c, taglia, tmp_buf);
|
||||
return;
|
||||
}
|
||||
int tmp = enqueue(q, toSend[i]);
|
||||
if(tmp<0) {
|
||||
n = 0;
|
||||
n += snprintf(tmp_buf+n, m-n, "Client %ld ha richiesto una readNFiles (n = %d) e' terminato con errore del server\n",
|
||||
fd_c,
|
||||
oldNum);
|
||||
perror("readNFiles: enqueue");
|
||||
oldNum,
|
||||
i,
|
||||
ntosend);
|
||||
perror("readNFiles: request");
|
||||
serror(MESE, fd_c, taglia, tmp_buf);
|
||||
return;
|
||||
}
|
||||
@ -452,7 +453,7 @@ void readNFiles(int num, queueT *q, long fd_c, taglia_t *taglia) {
|
||||
toSend[i]->filepath,
|
||||
toSend[i]->valid);
|
||||
}
|
||||
n += snprintf(tmp_buf+n, m-n, "readNFile dimensione totale = %ld\n",
|
||||
n += snprintf(tmp_buf+n, m-n, "readNFile dimensione totale = %ld B\n",
|
||||
tot);
|
||||
|
||||
sendMessageFileN(MEOK, toSend, ntosend, fd_c, taglia, tmp_buf);
|
||||
|
||||
@ -129,7 +129,7 @@ int enqueue(queueT *q, fileT* data) {
|
||||
goto _end_enqueue;
|
||||
}
|
||||
|
||||
/* insert the element */
|
||||
/* insert the element at the end */
|
||||
nodeT *newNode = malloc(sizeof(nodeT));
|
||||
if (newNode == NULL) {
|
||||
perror("enqueue: malloc");
|
||||
@ -178,6 +178,7 @@ fileT* dequeue(queueT *q) {
|
||||
|
||||
fileT *data = (q->head)->data;
|
||||
|
||||
/* dequeue the first in the list */
|
||||
nodeT *tmp = NULL;
|
||||
tmp = q->head;
|
||||
q->head = (q->head)->next;
|
||||
@ -198,7 +199,6 @@ _end_dequeue:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* dequeue until we have s free space and expand the file by s */
|
||||
fileT ** dequeueN(queueT *q, char *filepath, size_t s) {
|
||||
if(!q) {
|
||||
@ -211,22 +211,33 @@ fileT ** dequeueN(queueT *q, char *filepath, size_t s) {
|
||||
|
||||
LOCK_RETURN(&q->m, NULL); /* begin me */
|
||||
|
||||
fileT **returnList = NULL; /* list of removed files */
|
||||
|
||||
if (q->head == NULL || q->len == 0) { /* empty queue */
|
||||
errno = ENOENT;
|
||||
goto _end_dequeueN;
|
||||
}
|
||||
|
||||
if(q->maxSize > s) {
|
||||
if(q->maxSize < s) {
|
||||
errno = EINVAL;
|
||||
goto _end_dequeueN;
|
||||
}
|
||||
|
||||
/* try to find the file */
|
||||
nodeT *tmp = q->head;
|
||||
/* if LRU keep track of previous */
|
||||
#if ALGORITHM == 1
|
||||
nodeT *prev = NULL;
|
||||
#endif
|
||||
|
||||
while (tmp) {
|
||||
if (strcmp(filepath, (tmp->data)->filepath) == 0) { /* found */
|
||||
break;
|
||||
}
|
||||
/* if LRU keep track of previous */
|
||||
#if ALGORITHM == 1
|
||||
prev = tmp;
|
||||
#endif
|
||||
tmp = tmp->next;
|
||||
}
|
||||
|
||||
@ -235,36 +246,80 @@ fileT ** dequeueN(queueT *q, char *filepath, size_t s) {
|
||||
goto _end_dequeueN;
|
||||
}
|
||||
|
||||
fileT **returnList = NULL; /* list of removed files */
|
||||
tmp = NULL;
|
||||
nodeT* file = tmp;
|
||||
|
||||
returnList = calloc(1, sizeof(fileT*));
|
||||
returnList[0] = NULL;
|
||||
/* if LRU put the file at the end */
|
||||
#if ALGORITHM == 1
|
||||
if(prev==NULL && tmp->next==NULL) {
|
||||
q->head = tmp;
|
||||
q->tail = tmp;
|
||||
} else if(prev==NULL) {
|
||||
q->head = tmp->next;
|
||||
q->tail->next = tmp;
|
||||
q->tail = tmp;
|
||||
tmp->next = NULL;
|
||||
} else if(tmp->next!=NULL) {
|
||||
prev->next = tmp->next;
|
||||
q->tail->next = tmp;
|
||||
q->tail = tmp;
|
||||
tmp->next = NULL;
|
||||
}
|
||||
#endif /* ALGORITHM == 1 */
|
||||
|
||||
tmp = q->head;
|
||||
|
||||
returnList = calloc(1, sizeof(*returnList));
|
||||
if(!returnList) {
|
||||
perror("dequeueN: calloc");
|
||||
goto _end_dequeueN;
|
||||
}
|
||||
|
||||
int purged = 0;
|
||||
int addFileAtEnd = 0;
|
||||
while(q->size + s > q->maxSize) {
|
||||
purged++;
|
||||
if (q->head == NULL) { /* queue becomes empty */
|
||||
q->tail = NULL;
|
||||
break; /* we eliminated everything so we must have enought space */
|
||||
}
|
||||
if(q->head == file) { /* dont delete the file to expand */
|
||||
q->head = q->head->next;
|
||||
addFileAtEnd = 1;
|
||||
}
|
||||
|
||||
tmp = q->head;
|
||||
q->head = q->head->next;
|
||||
|
||||
++purged;
|
||||
returnList = realloc(returnList, purged * sizeof(fileT*));
|
||||
if(!returnList) {
|
||||
perror("dequeueN: realloc");
|
||||
goto _end_dequeueN;
|
||||
}
|
||||
tmp = q->head;
|
||||
q->head = (q->head)->next;
|
||||
|
||||
returnList[purged-1] = tmp->data;
|
||||
--q->len;
|
||||
q->size -= tmp->data->size;
|
||||
|
||||
if (q->head == NULL) { /* queue becomes empty */
|
||||
q->tail = NULL;
|
||||
break; /* we eliminated everything so we must have enought space */
|
||||
free(tmp); /* free of the node */
|
||||
}
|
||||
if(addFileAtEnd == 1) {
|
||||
/* if LRU, we know already that this code is unrechable
|
||||
* and that q->head must be NULL if rechable */
|
||||
if(q->head == NULL) {
|
||||
q->head = file;
|
||||
q->head->next = NULL;
|
||||
} else {
|
||||
file->next = q->head;
|
||||
q->head = file;
|
||||
}
|
||||
}
|
||||
|
||||
returnList = realloc(returnList, (purged+1) * sizeof(fileT*));
|
||||
if(!returnList) {
|
||||
perror("dequeueN: realloc");
|
||||
goto _end_dequeueN;
|
||||
}
|
||||
returnList[purged] = NULL; /* null terminated */
|
||||
|
||||
tmp->data->size += s;
|
||||
file->data->size += s;
|
||||
q->size += s;
|
||||
|
||||
UNLOCK_RETURN(&q->m, NULL); /* end me */
|
||||
@ -272,6 +327,8 @@ fileT ** dequeueN(queueT *q, char *filepath, size_t s) {
|
||||
|
||||
_end_dequeueN:
|
||||
UNLOCK_RETURN(&q->m, NULL);
|
||||
if(returnList)
|
||||
free(returnList);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -351,11 +408,19 @@ int lockFileInQueue(queueT *q, char *filepath, int owner) {
|
||||
|
||||
/* find file */
|
||||
nodeT *tmp = q->head;
|
||||
/* if LRU keep track of previous */
|
||||
#if ALGORITHM == 1
|
||||
nodeT *prev = NULL;
|
||||
#endif
|
||||
|
||||
while (tmp) {
|
||||
if (strcmp(filepath, (tmp->data)->filepath) == 0) { /* found */
|
||||
break;
|
||||
}
|
||||
/* if LRU keep track of previous */
|
||||
#if ALGORITHM == 1
|
||||
prev = tmp;
|
||||
#endif
|
||||
tmp = tmp->next;
|
||||
}
|
||||
|
||||
@ -374,6 +439,25 @@ int lockFileInQueue(queueT *q, char *filepath, int owner) {
|
||||
(tmp->data)->O_LOCK = 1;
|
||||
(tmp->data)->owner = owner;
|
||||
|
||||
/* if LRU put the file at the end */
|
||||
#if ALGORITHM == 1
|
||||
if(prev==NULL && tmp->next==NULL) {
|
||||
q->head = tmp;
|
||||
q->tail = tmp;
|
||||
} else if(prev==NULL) {
|
||||
q->head = tmp->next;
|
||||
q->tail->next = tmp;
|
||||
q->tail = tmp;
|
||||
tmp->next = NULL;
|
||||
} else if(tmp->next!=NULL) {
|
||||
prev->next = tmp->next;
|
||||
q->tail->next = tmp;
|
||||
q->tail = tmp;
|
||||
tmp->next = NULL;
|
||||
}
|
||||
#endif /* ALGORITHM == 1 */
|
||||
|
||||
|
||||
UNLOCK_RETURN(&q->m, -1); /* end me */
|
||||
return 0;
|
||||
|
||||
@ -449,10 +533,19 @@ int openFileInQueue(queueT *q, char *filepath, int O_LOCK, int owner) {
|
||||
|
||||
/* find the file */
|
||||
nodeT *tmp = q->head;
|
||||
/* if LRU keep track of previous */
|
||||
#if ALGORITHM == 1
|
||||
nodeT *prev = NULL;
|
||||
#endif
|
||||
|
||||
while (tmp) {
|
||||
if (strcmp(filepath, (tmp->data)->filepath) == 0) { /* found */
|
||||
break;
|
||||
}
|
||||
/* if LRU keep track of previous */
|
||||
#if ALGORITHM == 1
|
||||
prev = tmp;
|
||||
#endif
|
||||
tmp = tmp->next;
|
||||
}
|
||||
|
||||
@ -474,6 +567,24 @@ int openFileInQueue(queueT *q, char *filepath, int O_LOCK, int owner) {
|
||||
(tmp->data)->owner = owner;
|
||||
}
|
||||
|
||||
/* if LRU put the file at the end */
|
||||
#if ALGORITHM == 1
|
||||
if(prev==NULL && tmp->next==NULL) {
|
||||
q->head = tmp;
|
||||
q->tail = tmp;
|
||||
} else if(prev==NULL) {
|
||||
q->head = tmp->next;
|
||||
q->tail->next = tmp;
|
||||
q->tail = tmp;
|
||||
tmp->next = NULL;
|
||||
} else if(tmp->next!=NULL) {
|
||||
prev->next = tmp->next;
|
||||
q->tail->next = tmp;
|
||||
q->tail = tmp;
|
||||
tmp->next = NULL;
|
||||
}
|
||||
#endif /* ALGORITHM == 1 */
|
||||
|
||||
UNLOCK_RETURN(&q->m, -1); /* end me */
|
||||
return 0;
|
||||
|
||||
@ -545,11 +656,6 @@ int writeFileInQueue(queueT *q, char *filepath, void *data, size_t size, int own
|
||||
goto _end_write_file_queue;
|
||||
}
|
||||
|
||||
if (q->size + size > q->maxSize) { /* not enough space */
|
||||
errno = EFBIG;
|
||||
goto _end_write_file_queue;
|
||||
}
|
||||
|
||||
/* find the file */
|
||||
nodeT *tmp = q->head;
|
||||
while (tmp) {
|
||||
@ -564,6 +670,11 @@ int writeFileInQueue(queueT *q, char *filepath, void *data, size_t size, int own
|
||||
goto _end_write_file_queue;
|
||||
}
|
||||
|
||||
if (q->size - tmp->data->size + size > q->maxSize) { /* not enough space */
|
||||
errno = EFBIG;
|
||||
goto _end_write_file_queue;
|
||||
}
|
||||
|
||||
if ((tmp->data)->open == 0 || ((tmp->data)->O_LOCK && (tmp->data)->owner != owner)) {
|
||||
/* file is closed or the client does not hold the lock */
|
||||
errno = EPERM;
|
||||
@ -605,11 +716,6 @@ int appendFileInQueue(queueT *q, char *filepath, void *data, size_t size, int ow
|
||||
goto _end_append_file_queue;
|
||||
}
|
||||
|
||||
if (q->size + size > q->maxSize) { /* not enough space */
|
||||
errno = EFBIG;
|
||||
goto _end_append_file_queue;
|
||||
}
|
||||
|
||||
/* find the file */
|
||||
nodeT *tmp = q->head;
|
||||
while (tmp) {
|
||||
@ -624,6 +730,11 @@ int appendFileInQueue(queueT *q, char *filepath, void *data, size_t size, int ow
|
||||
goto _end_append_file_queue;
|
||||
}
|
||||
|
||||
if (q->size - tmp->data->size + size > q->maxSize) { /* not enough space */
|
||||
errno = EFBIG;
|
||||
goto _end_append_file_queue;
|
||||
}
|
||||
|
||||
if ((tmp->data)->open == 0 || ((tmp->data)->O_LOCK && (tmp->data)->owner != owner)) {
|
||||
/* file is closed or the client does not hold the lock */
|
||||
errno = EPERM;
|
||||
@ -732,11 +843,19 @@ fileT* find(queueT *q, char *filepath) {
|
||||
|
||||
fileT *res = NULL;
|
||||
nodeT *tmp = q->head;
|
||||
/* if LRU keep track of previous */
|
||||
#if ALGORITHM == 1
|
||||
nodeT *prev = NULL;
|
||||
#endif
|
||||
|
||||
while (tmp) {
|
||||
if (strcmp(filepath, (tmp->data)->filepath) == 0) { /* trovato */
|
||||
break;
|
||||
}
|
||||
/* if LRU keep track of previous */
|
||||
#if ALGORITHM == 1
|
||||
prev = tmp;
|
||||
#endif
|
||||
tmp = tmp->next;
|
||||
}
|
||||
|
||||
@ -757,6 +876,24 @@ fileT* find(queueT *q, char *filepath) {
|
||||
goto _end_find_in_queue;
|
||||
}
|
||||
|
||||
/* if LRU put the file at the end */
|
||||
#if ALGORITHM == 1
|
||||
if(prev==NULL && tmp->next==NULL) {
|
||||
q->head = tmp;
|
||||
q->tail = tmp;
|
||||
} else if(prev==NULL) {
|
||||
q->head = tmp->next;
|
||||
q->tail->next = tmp;
|
||||
q->tail = tmp;
|
||||
tmp->next = NULL;
|
||||
} else if(tmp->next!=NULL) {
|
||||
prev->next = tmp->next;
|
||||
q->tail->next = tmp;
|
||||
q->tail = tmp;
|
||||
tmp->next = NULL;
|
||||
}
|
||||
#endif /* ALGORITHM == 1 */
|
||||
|
||||
UNLOCK_RETURN(&q->m, NULL); /* end me */
|
||||
return res;
|
||||
|
||||
@ -765,6 +902,65 @@ _end_find_in_queue:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* return n files */
|
||||
fileT ** request(queueT *q, int n) {
|
||||
if(!q) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
if(n<=0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LOCK_RETURN(&q->m, NULL); /* begin me */
|
||||
|
||||
fileT **returnList = NULL; /* list of requested files */
|
||||
|
||||
if (q->head == NULL || q->len == 0) { /* empty queue */
|
||||
errno = ENOENT;
|
||||
goto _end_request;
|
||||
}
|
||||
|
||||
if(q->size < n) {
|
||||
errno = EINVAL;
|
||||
goto _end_request;
|
||||
}
|
||||
|
||||
returnList = calloc(n+1, sizeof(*returnList));
|
||||
if(!returnList) {
|
||||
perror("dequeueN: calloc");
|
||||
goto _end_request;
|
||||
}
|
||||
returnList[n] = NULL;
|
||||
|
||||
nodeT *tmp = q->head;
|
||||
for(int i=0; i<n; ++i) {
|
||||
if(tmp == NULL) {
|
||||
errno = ENOENT;
|
||||
goto _end_request;
|
||||
}
|
||||
returnList[i] = tmp->data;
|
||||
tmp = tmp->next;
|
||||
}
|
||||
|
||||
/* if LRU put the files at the end */
|
||||
#if ALGORITHM == 1
|
||||
q->tail->next = q->head;
|
||||
q->head = tmp->next;
|
||||
tmp->next = NULL;
|
||||
q->tail = tmp;
|
||||
#endif /* ALGORITHM == 1 */
|
||||
|
||||
UNLOCK_RETURN(&q->m, NULL); /* end me */
|
||||
return returnList;
|
||||
|
||||
_end_request:
|
||||
UNLOCK_RETURN(&q->m, NULL);
|
||||
if(returnList)
|
||||
free(returnList);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* search for file and return result of search */
|
||||
int searchFile(queueT *q, char *filepath) {
|
||||
if(!q || !filepath) {
|
||||
@ -778,17 +974,43 @@ int searchFile(queueT *q, char *filepath) {
|
||||
goto _end_search_file_in_queue;
|
||||
|
||||
nodeT *tmp = q->head;
|
||||
/* if LRU keep track of previous */
|
||||
#if ALGORITHM == 1
|
||||
nodeT *prev = NULL;
|
||||
#endif
|
||||
|
||||
while (tmp) {
|
||||
if (strcmp(filepath, (tmp->data)->filepath) == 0) { /* found */
|
||||
break;
|
||||
}
|
||||
/* if LRU keep track of previous */
|
||||
#if ALGORITHM == 1
|
||||
prev = tmp;
|
||||
#endif
|
||||
tmp = tmp->next;
|
||||
}
|
||||
|
||||
if(!tmp)
|
||||
goto _end_search_file_in_queue;
|
||||
|
||||
/* if LRU put the file at the end */
|
||||
#if ALGORITHM == 1
|
||||
if(prev==NULL && tmp->next==NULL) {
|
||||
q->head = tmp;
|
||||
q->tail = tmp;
|
||||
} else if(prev==NULL) {
|
||||
q->head = tmp->next;
|
||||
q->tail->next = tmp;
|
||||
q->tail = tmp;
|
||||
tmp->next = NULL;
|
||||
} else if(tmp->next!=NULL) {
|
||||
prev->next = tmp->next;
|
||||
q->tail->next = tmp;
|
||||
q->tail = tmp;
|
||||
tmp->next = NULL;
|
||||
}
|
||||
#endif /* ALGORITHM == 1 */
|
||||
|
||||
UNLOCK_RETURN(&q->m, 0); /* end me */
|
||||
return 1;
|
||||
|
||||
@ -840,6 +1062,9 @@ void destroyQueue(queueT *q) {
|
||||
if (errno) {
|
||||
perror("destroyQueue: voidDequeue");
|
||||
}
|
||||
if(q->head == NULL) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_destroy(&q->m);
|
||||
|
||||
@ -7,6 +7,12 @@
|
||||
|
||||
#include "conn.h"
|
||||
|
||||
/* Algoritmo che viene usato per il rimpiazzo dei dati:
|
||||
se = 0 -> FIFO
|
||||
se = 1 -> LRU
|
||||
*/
|
||||
#define ALGORITHM 0
|
||||
|
||||
/* struttura dati per gestire i file in memoria principale */
|
||||
typedef struct {
|
||||
char *filepath; /* path assoluto del file */
|
||||
@ -67,8 +73,7 @@ void destroyFile(fileT *f);
|
||||
|
||||
|
||||
/**
|
||||
* Alloca ed inizializza una coda di fileT. Deve essere chiamata da un solo
|
||||
* thread.
|
||||
* Alloca ed inizializza una coda di fileT.
|
||||
* @param maxLen lunghezza massima della coda
|
||||
* @param maxSize dimensione massima della coda in bytes
|
||||
*
|
||||
@ -215,6 +220,15 @@ int removeFileFromQueue(queueT *q, char *filepath, int owner);
|
||||
*/
|
||||
fileT* find(queueT *q, char *filepath);
|
||||
|
||||
/**
|
||||
* Estrae n fileT dalla coda senza eliminarli
|
||||
* @param q puntatore alla coda
|
||||
* @param n numero di file da estrarre
|
||||
*
|
||||
* @return puntatore i file estratti (NULL terminated), NULL se errore
|
||||
*/
|
||||
fileT ** request(queueT *q, int n);
|
||||
|
||||
/**
|
||||
* Cerca il fileT nella coda e ritorna il siultato dell'operazione
|
||||
* @param q puntatore alla coda sulla quale cercare il fileT
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
* @function void *threadpool_thread(void *threadpool)
|
||||
* @brief funzione eseguita dal thread worker che appartiene al pool
|
||||
*/
|
||||
static void *workerpool_thread(void *threadpool) {
|
||||
static void *workerpool_thread(void *threadpool) {
|
||||
threadpool_t *pool = (threadpool_t *)threadpool; // cast
|
||||
taskfun_t task; // generic task
|
||||
pthread_t self = pthread_self();
|
||||
@ -34,28 +34,28 @@ static void *workerpool_thread(void *threadpool) {
|
||||
|
||||
LOCK_RETURN(&(pool->lock), NULL);
|
||||
for (;;) {
|
||||
// in attesa di un messaggio, controllo spurious wakeups.
|
||||
while((pool->count == 0) && (!pool->exiting)) {
|
||||
pthread_cond_wait(&(pool->cond), &(pool->lock));
|
||||
// in attesa di un messaggio, controllo spurious wakeups.
|
||||
while((pool->count == 0) && (!pool->exiting)) {
|
||||
pthread_cond_wait(&(pool->cond), &(pool->lock));
|
||||
}
|
||||
|
||||
if (pool->exiting > 1) break; // exit forzato, esco immediatamente
|
||||
// devo uscire ma ci sono messaggi pendenti
|
||||
if (pool->exiting == 1 && !pool->count) break;
|
||||
if (pool->exiting > 1) break; // exit forzato, esco immediatamente
|
||||
// devo uscire ma ci sono messaggi pendenti
|
||||
if (pool->exiting == 1 && !pool->count) break;
|
||||
|
||||
// nuovo task
|
||||
task.fun = pool->pending_queue[pool->head].fun;
|
||||
task.arg = pool->pending_queue[pool->head].arg;
|
||||
task.fun = pool->pending_queue[pool->head].fun;
|
||||
task.arg = pool->pending_queue[pool->head].arg;
|
||||
|
||||
pool->head++; pool->count--;
|
||||
pool->head = (pool->head == abs(pool->queue_size)) ? 0 : pool->head;
|
||||
pool->head++; pool->count--;
|
||||
pool->head = (pool->head == abs(pool->queue_size)) ? 0 : pool->head;
|
||||
|
||||
pool->taskonthefly++;
|
||||
UNLOCK_RETURN(&(pool->lock), NULL);
|
||||
UNLOCK_RETURN(&(pool->lock), NULL);
|
||||
|
||||
// eseguo la funzione
|
||||
(*(task.fun))(task.arg);
|
||||
|
||||
// eseguo la funzione
|
||||
(*(task.fun))(task.arg);
|
||||
|
||||
LOCK_RETURN(&(pool->lock), NULL);
|
||||
pool->taskonthefly--;
|
||||
}
|
||||
@ -69,22 +69,22 @@ static void *workerpool_thread(void *threadpool) {
|
||||
|
||||
static int freePoolResources(threadpool_t *pool) {
|
||||
if(pool->threads) {
|
||||
free(pool->threads);
|
||||
free(pool->pending_queue);
|
||||
|
||||
pthread_mutex_destroy(&(pool->lock));
|
||||
pthread_cond_destroy(&(pool->cond));
|
||||
free(pool->threads);
|
||||
free(pool->pending_queue);
|
||||
|
||||
pthread_mutex_destroy(&(pool->lock));
|
||||
pthread_cond_destroy(&(pool->cond));
|
||||
}
|
||||
free(pool);
|
||||
free(pool);
|
||||
return 0;
|
||||
}
|
||||
|
||||
threadpool_t *createThreadPool(int numthreads, int pending_size) {
|
||||
if(numthreads <= 0 || pending_size < 0) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
threadpool_t *pool = (threadpool_t *)malloc(sizeof(threadpool_t));
|
||||
if (pool == NULL) return NULL;
|
||||
|
||||
@ -115,20 +115,20 @@ threadpool_t *createThreadPool(int numthreads, int pending_size) {
|
||||
return NULL;
|
||||
}
|
||||
for(int i = 0; i < numthreads; i++) {
|
||||
if(pthread_create(&(pool->threads[i]), NULL,
|
||||
workerpool_thread, (void*)pool) != 0) {
|
||||
if(pthread_create(&(pool->threads[i]), NULL,
|
||||
workerpool_thread, (void*)pool) != 0) {
|
||||
/* errore fatale, libero tutto forzando l'uscita dei threads */
|
||||
destroyThreadPool(pool, 1);
|
||||
destroyThreadPool(pool, 1);
|
||||
errno = EFAULT;
|
||||
return NULL;
|
||||
}
|
||||
pool->numthreads++;
|
||||
return NULL;
|
||||
}
|
||||
pool->numthreads++;
|
||||
}
|
||||
return pool;
|
||||
}
|
||||
|
||||
|
||||
int destroyThreadPool(threadpool_t *pool, int force) {
|
||||
int destroyThreadPool(threadpool_t *pool, int force) {
|
||||
if(pool == NULL || force < 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
@ -171,30 +171,30 @@ int addToThreadPool(threadpool_t *pool, void (*f)(void *), void *arg) {
|
||||
UNLOCK_RETURN(&(pool->lock),-1);
|
||||
return 1; // esco con valore "coda piena"
|
||||
}
|
||||
|
||||
|
||||
if (pool->taskonthefly >= pool->numthreads) {
|
||||
if (nopending) {
|
||||
if (nopending) {
|
||||
// tutti i thread sono occupati e non si gestiscono task pendenti
|
||||
assert(pool->count == 0);
|
||||
|
||||
|
||||
UNLOCK_RETURN(&(pool->lock),-1);
|
||||
return 1; // esco con valore "coda piena"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pool->pending_queue[pool->tail].fun = f;
|
||||
pool->pending_queue[pool->tail].arg = arg;
|
||||
pool->count++;
|
||||
pool->count++;
|
||||
pool->tail++;
|
||||
if (pool->tail >= queue_size) pool->tail = 0;
|
||||
|
||||
|
||||
int r;
|
||||
if((r=pthread_cond_signal(&(pool->cond))) != 0) {
|
||||
UNLOCK_RETURN(&(pool->lock),-1);
|
||||
errno = r;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
UNLOCK_RETURN(&(pool->lock),-1);
|
||||
return 0;
|
||||
}
|
||||
@ -204,11 +204,11 @@ int addToThreadPool(threadpool_t *pool, void (*f)(void *), void *arg) {
|
||||
* @function void *thread_proxy(void *argl)
|
||||
* @brief funzione eseguita dal thread worker che non appartiene al pool
|
||||
*/
|
||||
static void *proxy_thread(void *arg) {
|
||||
static void *proxy_thread(void *arg) {
|
||||
taskfun_t *task = (taskfun_t*)arg;
|
||||
// eseguo la funzione
|
||||
// eseguo la funzione
|
||||
(*(task->fun))(task->arg);
|
||||
|
||||
|
||||
free(task);
|
||||
return NULL;
|
||||
}
|
||||
@ -220,7 +220,7 @@ int spawnThread(void (*f)(void*), void* arg) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
taskfun_t *task = malloc(sizeof(taskfun_t)); // la memoria verra' liberata dal proxy
|
||||
taskfun_t *task = malloc(sizeof(taskfun_t)); // la memoria verra' liberata dal proxy
|
||||
if (!task) return -1;
|
||||
task->fun = f;
|
||||
task->arg = arg;
|
||||
|
||||
@ -415,7 +415,7 @@ int execute(cmd_t *l, int print) {
|
||||
interval.tv_sec = (num/1000);
|
||||
|
||||
if (print)
|
||||
fprintf(stdout, "t - Tempo fra due richieste: %ld ms\tEsito: ok\n", num);
|
||||
fprintf(stdout, "t - Tempo fra due richieste: %ld ms [Esito: ok]\n", num);
|
||||
|
||||
tmp = NULL;
|
||||
break;
|
||||
|
||||
@ -209,7 +209,7 @@ int main(int argc, char *argv[]) {
|
||||
if( taglia_log(taglia, buf) < 0)
|
||||
goto _cleanup;
|
||||
|
||||
printf("File Server ready.");
|
||||
printf("File Server ready.\n");
|
||||
fflush(stdout);
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user