Added file queue handling

This commit is contained in:
elvis
2022-03-15 00:30:04 +01:00
parent b4ca73b8d6
commit a72f0a0fad
7 changed files with 993 additions and 51 deletions

726
lib/threadpool/fileQueue.c Normal file
View File

@ -0,0 +1,726 @@
#include <fileQueue.h>
#include <util.h>
#define NAMELEN 256
// creazione di un fileT
fileT* createFileT(char *f, int O_LOCK, int client, int open){
if(!f){
errno = EINVAL;
return NULL;
}
fileT *file = malloc(sizeof(fileT));
if (f == NULL) {
perror("Malloc createFileT");
return NULL;
}
f->O_LOCK = (O_LOCK == 0)? 0 : 1;
f->owner = client;
f->open = (open == 0)? 0 : 1;
f->size = 0;
if ((f->filepath = malloc(sizeof(char)*NAMELEN)) == NULL) {
perror("Malloc filepath");
destroyFile(f);
return NULL;
}
strncpy(f->filepath, f, NAMELEN);
if ((f->data = malloc(1)) == NULL) { // così semplicemente facciamo realloc
perror("Malloc content");
return NULL;
}
return f;
}
// append su fileT
int writeFileT(fileT *f, void *data, size_t size) {
if(!f || !data || size < 0){
errno = EINVAL;
return -1;
}
if ((f->data = realloc(f->data, f->size + size)) == NULL) {
perror("Realloc content");
return -1;
}
memcpy((char*) f->data + f->size, data, size);
f->size = f->size + size;
}
// destroy fileT
void destroyFile(fileT *f) {
if(!f)
return;
if(f->filepath)
free(f->filepath);
if(f->data)
free(f->data);
free(f);
}
// --------------
// creazione queue
queueT* createQueue(size_t maxLen, size_t maxSize) {
if(maxLen<0 || maxSize<0){
errno = EINVAL;
return NULL;
}
queueT *q = malloc(sizeof(queueT));
if(q==NULL){
perror("Malloc createQueue");
return NULL;
}
// creazione lock
if (pthread_mutex_init(&q->m, NULL) != 0) {
perror("pthread_mutex_init createQueue");
destroyQueue(q);
return NULL;
}
q->head = NULL;
q->tail = NULL;
q->maxLen = maxLen;
q->len = 0;
q->maxSize = maxSize;
q->size = 0;
return q;
}
// insert
int enqueue(queueT *q, fileT* data) {
if(!q || !data){
errno = EINVAL;
return -1;
}
LOCK_RETURN(&q->m, -1); // begin me
if (q->len == q->maxLen) { // too many files
errno = ENFILE;
goto _end_enqueue;
}
if (q->size + data->size > q->maxSize) {
errno = EFBIG;
goto _end_enqueue;
}
// inserisco l'elemento
nodeT *newNode = malloc(sizeof(nodeT));
if (newNode == NULL) {
perror("malloc newNode");
goto _end_enqueue;
}
newNode->data = data;
newNode->next = NULL;
nodeT *tmp = queue->head;
if (q->head == NULL)
q->head = newNode;
else {
while (tmp->next)
tmp = temp->next;
temp->next = newNode;
}
q->tail = newNode;
++q->len;
q->size += data->size;
UNLOCK_RETURN(&q->m, -1); // end me
return 0;
_end_enqueue:
UNLOCK_RETURN(&q->m, -1);
return -1;
}
// remove
fileT* dequeue(queueT *q) {
if(!q) {
errno = EINVAL;
return NULL;
}
LOCK_RETURN(&q->m, NULL); // begin me
if (q->head == NULL || q->len == 0) { // coda vuota
errno = ENOENT;
goto _end_dequeue;
}
fileT *data = (q->head)->data;
nodeT *tmp = NULL;
tmp = q->head;
q->head = (q->head)->next;
if (q->head == NULL) { // coda diventa vuota
q->tail = NULL;
}
--q->len;
q->size -= data->size;
free(tmp); // free del nodo
UNLOCK_RETURN(&q->m, NULL); // end me
return data;
_end_dequeue:
UNLOCK_RETURN(&q->m, NULL);
return NULL;
}
void voidDequeue(queueT *q) {
if(!q) {
errno = EINVAL;
return;
}
LOCK(&q->m); // begin me
if (q->head == NULL || q->len == 0) { // coda vuota
errno = ENOENT;
goto _end_void_dequeue;
}
nodeT *tmp = NULL;
tmp = q->head;
q->head = (q->head)->next;
if (q->head == NULL) { // coda diventa vuota
q->tail = NULL;
}
--q->len;
q->size -= data->size;
destroyFile(tmp->data); // free fileT
free(tmp); // free nodo
UNLOCK(&q->m); // end me
return;
_end_void_dequeue:
UNLOCK_RETURN(&q->m, NULL);
return NULL;
}
// print queue
int printQueue(FILE *stream, queueT *q) {
if(!q || !stream) {
errno = EINVAL;
return -1;
}
LOCK_RETURN(&q->m, -1); // begin me
UNLOCK_RETURN(&q->m, -1);
return 0;
nodeT *tmp = q->head;
fprintf(stream, "Lista file:");
fprintf(stream, "[Nome File] -> Dimensione in MB");
while (temp!=NULL) {
float res = ((float)(tmp->data)->size)/1000000; // in MB
fprintf(stream, "[%s]\t-> %f MB\n", (tmp->data)->filepath, res);
temp = temp->next;
}
UNLOCK_RETURN(&q->m, -1); // end me
return 0;
_end_print_queue:
UNLOCK_RETURN(&q->m, -1);
return -1;
}
// acquisizione lock
int lockFileInQueue(queueT *q, char *filepath, int owner) {
if(!q || !filepath) {
errno = EINVAL;
return -1;
}
LOCK_RETURN(&q->m, -1); // begin me
if (q->len == 0) {
errno = ENOENT;
goto _end_lock_file_queue;
}
// scorro la queue per trovare l'elemento
nodeT *tmp = q->head;
while (tmp) {
if (strcmp(filepath, (tmp->data)->filepath) == 0) { // trovato
break;
}
tmp = tmp->next;
}
if(!tmp) { // non trovato
errno = ENOENT;
goto _end_lock_file_queue;
}
if ((tmp->data)->O_LOCK && (tmp->data)->owner != owner) { // lock non del owner
errno = EPERM;
goto _end_lock_file_queue;
}
// acquisisco lock sul file
(temp->data)->O_LOCK = 1;
(temp->data)->owner = owner;
UNLOCK_RETURN(&q->m, -1); // end me
return 0;
_end_lock_file_queue:
UNLOCK_RETURN(&q->m, -1);
return -1;
}
int unlockFileInQueue(queueT *q, char *filepath, int owner) {
if(!q || !filepath) {
errno = EINVAL;
return -1;
}
LOCK_RETURN(&q->m, -1); // begin me
if (q->len == 0) {
errno = ENOENT;
goto _end_unlock_file_queue;
}
// scorro la queue per trovare l'elemento
nodeT *tmp = q->head;
while (tmp) {
if (strcmp(filepath, (tmp->data)->filepath) == 0) { // trovato
break;
}
tmp = tmp->next;
}
if(!tmp) { // non trovato
errno = ENOENT;
goto _end_lock_file_queue;
}
if((tmp->data)->O_LOCK == 0) { // nessuno ha il lock
UNLOCK_RETURN(&q->m, -1);
return 0;
}
if((tmp->data)->O_LOCK == 1 && (tmp->data)->owner != owner) { // lock non del owner
errno = EPERM;
goto _end_unlock_file_queue;
}
(tmp->data)->O_LOCK = 0;
UNLOCK_RETURN(&q->m, -1); // end me
return 0;
_end_unlock_file_queue:
UNLOCK_RETURN(&q->m, -1);
return -1;
}
// open file
int openFileInQueue(queueT *q, char *filepath, int O_LOCK, int owner) {
if(!q || !filepath) {
errno = EINVAL;
return -1;
}
LOCK_RETURN(&q->m, -1); // begin me
if (q->len == 0) {
errno = ENOENT;
goto _end_open_file_queue;
}
// scorro la queue per trovare l'elemento
nodeT *tmp = q->head;
while (tmp) {
if (strcmp(filepath, (tmp->data)->filepath) == 0) { // trovato
break;
}
tmp = tmp->next;
}
if(!tmp) { // non trovato
errno = ENOENT;
goto _end_open_file_queue;
}
if((tmp->data)->O_LOCK == 1 && (tmp->data)->owner != owner) { // lock non del owner
errno = EPERM;
goto _end_open_file_queue;
}
(tmp->data)->open = 1;
(tmp->data)->O_LOCK = (O_LOCK==0)?0:1;
if(O_LOCK!=0) {
(tmp->data)->owner = client;
}
UNLOCK_RETURN(&q->m, -1); // end me
return 0;
_end_open_file_queue:
UNLOCK_RETURN(&q->m, -1);
return -1;
}
// close and relese lock
int closeFileInQueue(queueT *q, char *filepath, int owner) {
if(!q || !filepath) {
errno = EINVAL;
return -1;
}
LOCK_RETURN(&q->m, -1); // begin me
if (q->len == 0) {
errno = ENOENT;
goto _end_close_file_queue;
}
// scorro la queue per trovare l'elemento
nodeT *tmp = q->head;
while (tmp) {
if (strcmp(filepath, (tmp->data)->filepath) == 0) { // trovato
break;
}
tmp = tmp->next;
}
if(!tmp) { // non trovato
errno = ENOENT;
goto _end_close_file_queue;
}
if((tmp->data)->O_LOCK == 1 && (tmp->data)->owner != owner) { // lock non del owner
errno = EPERM;
goto _end_close_file_queue;
}
(tmp->data)->open = 0;
(tmp->data)->O_LOCK = 0;
(tmp->data)->owner = owner;
UNLOCK_RETURN(&q->m, -1); // end me
return 0;
_end_open_file_queue:
UNLOCK_RETURN(&q->m, -1);
return -1;
}
int writeFileInQueue(queueT *q, char *filepath, void *data, size_t size, int owner) {
if(!q || !filepath || !data) {
errno = EINVAL;
return -1;
}
if(size == 0)
return 0;
LOCK_RETURN(&q->m, -1); // begin me
if (q->len == 0) { // coda vuota
errno = ENOENT;
goto _end_write_file_queue;
}
if (q->size + size > q->maxSize) { // non c'e' abbastanza spazio
errno = EFBIG;
goto _end_write_file_queue;
}
// scorro la queue per trovare l'elemento
nodeT *tmp = q->head;
while (tmp) {
if (strcmp(filepath, (tmp->data)->filepath) == 0) { // trovato
break;
}
tmp = tmp->next;
}
if(!tmp) { // non trovato
errno = ENOENT;
goto _end_write_file_queue;
}
if ((tmp->data)->open == 0 || ((tmp->data)->O_LOCK && (tmp->data)->owner != owner)) {
// if file non è aperto o la lock non è del owner
errno = EPERM;
goto _end_write_file_queue;
}
// scrivo
if (((tmp->data)->data = realloc((tmp->data)->data, (tmp->data)->size + size)) == NULL) {
perror("Realloc content");
goto _end_write_file_queue;
}
memcpy((tmp->data)->data, data, size);
q->size = (q->size) - ((tmp->data)->size) + size;
(tmp->data)->size = size;
UNLOCK_RETURN(&q->m, -1); // end me
return 0;
_end_write_file_queue:
UNLOCK_RETURN(&q->m, -1);
return -1;
}
int appendFileInQueue(queueT *q, char *filepath, void *data, size_t size, int owner) {
if (!q || !filepath || !data) {
errno = EINVAL;
return -1;
}
if (size == 0)
return 0;
LOCK_RETURN(&q->m, -1); // begin me
if (q->len == 0) { // coda vuota
errno = ENOENT;
goto _end_append_file_queue;
}
if (q->size + size > q->maxSize) { // non c'e' abbastanza spazio
errno = EFBIG;
goto _end_append_file_queue;
}
// scorro la queue per trovare l'elemento
nodeT *tmp = q->head;
while (tmp) {
if (strcmp(filepath, (tmp->data)->filepath) == 0) { // trovato
break;
}
tmp = tmp->next;
}
if(!tmp) { // non trovato
errno = ENOENT;
goto _end_append_file_queue;
}
if ((tmp->data)->open == 0 || ((tmp->data)->O_LOCK && (tmp->data)->owner != owner)) {
// if file non è aperto o la lock non è del owner
errno = EPERM;
goto _end_append_file_queue;
}
// scrivo
if (((tmp->data)->data = realloc((tmp->data)->data, (tmp->data)->size + size)) == NULL) {
perror("Realloc content");
goto _end_append_file_queue;
}
memcpy(((tmp->data)->data) + (tmp->data)->size, data, size);
// memmove sarebbe un'alternativa
(tmp->data)->size += size;
q->size += size;
UNLOCK_RETURN(&q->m, -1); // end me
return 0;
_end_append_file_queue:
UNLOCK_RETURN(&q->m, -1);
return -1;
}
int removeFileFromQueue(queueT *q, char *filepath, int owner) {
if(!q || !filepath) {
errno = EINVAL;
return -1;
}
LOCK_RETURN(&q->m, -1); // begin me
if (q->len == 0) { // coda vuota
errno = ENOENT;
goto _end_remove_file_queue;
}
// scorro la queue per trovare l'elemento
nodeT *tmp = q->head;
nodeT *pre = q->head;
while (tmp) {
if (strcmp(filepath, (tmp->data)->filepath) == 0) { // trovato
break;
}
if(prc!=tmp)
prc = prc->next;
tmp = tmp->next;
}
if (!tmp) { // non trovato
errno = ENOENT;
goto _end_remove_file_queue;
}
if ((tmp->data)->open != 0 || ((tmp->data)->O_LOCK && (tmp->data)->owner != owner)) {
// if file è aperto o la lock non è del owner
errno = EPERM;
goto _end_append_file_queue;
}
if (tmp == prc) { // file è il primo
q->head = tmp->next;
if (tmp->next == NULL) {
q->tail = tmp;
}
} else { // file non è il primo
prc->next = tmp->next;
if (prc->next == NULL) {
q->tail = prc;
}
}
--q->len;
q->size -= (tmp->data)->size;
destroyFile(tmp->data); // free file
free(tmp); // free node
UNLOCK_RETURN(&q->m, -1); // end me
return 0;
_end_remove_file_queue:
UNLOCK_RETURN(&q->m, -1);
return -1;
}
// ----------------
// cerco e ritorno una copia
fileT* find(queueT *q, char *filepath) {
if(!q || !filepath) {
errno = EINVAL;
return NULL;
}
LOCK_RETURN(&q->m, NULL); // begin me
if (q->len == 0)
goto _end_find_in_queue;
fileT *res = NULL;
nodeT *tmp = q->head;
while (tmp) {
if (strcmp(filepath, (tmp->data)->filepath) == 0) { // trovato
break;
}
tmp = tmp->next;
}
if(!tmp)
goto _end_find_in_queue;
// creo una nuova istanza
res = createFileT((tmp->data)->filepath, (tmp->data)->O_LOCK, (tmp->data)->owner, (tmp->data)->open);
if (!res) {
perror("createFileT res");
goto _end_find_in_queue;
}
if (writeFileT(res, (tmp->data)->data, (tmp->data)->size) == -1) {
perror("writeFileT res");
goto _end_find_in_queue;
}
UNLOCK_RETURN(&q->m, NULL); // end me
return res;
_end_find_in_queue:
UNLOCK_RETURN(&q->m, NULL);
return NULL;
}
// numero di elementi della queue
size_t getLen(queueT *q) {
if(!q) {
errno = EINVAL;
return -1;
}
size_t len = -1;
LOCK_RETURN(&q->m, -1); // begin me
len = queue->len;
UNLOCK_RETURN(&q->m, -1); // end me
return len;
}
// dimensione in byte
size_t getSize(queueT *q) {
if(!q) {
errno = EINVAL;
return -1;
}
size_t size = -1;
LOCK_RETURN(&q->m, -1); // begin me
size = q->size;
UNLOCK_RETURN(&q->m, -1); // end me
return size;
}
void destroyQueue(queueT *q) {
if(!q)
return;
// no need for lock over queue
while (q->len > 0) {
errno = 0;
voiDequeue(q);
if (errno) {
perror("voiDequeue");
}
}
if (&queue->m) {
pthread_mutex_destroy(&q->m);
}
free(queue);
}

224
lib/threadpool/fileQueue.h Normal file
View File

@ -0,0 +1,224 @@
#pragma once
#ifndef _FILE_QUEUE
#define _FILE_QUEUE
// struttura dati per gestire i file in memoria principale
typedef struct {
char *filepath; // path assoluto del file
int O_LOCK; // 1 se il file e' in modalita' locked
int owner; // client che possiede la lock sul file
int open; // 1 se il file e' aperto
void *data; // contenuto del file
size_t size; // dimensione del file in bytes
} fileT;
// nodo di una linked list
typedef struct node {
fileT *data;
struct node *next;
} nodeT;
// coda FIFO
typedef struct {
nodeT *head; // puntatore al primo elemento
nodeT *tail; // puntatore all'ultimo elemento
size_t maxLen; // numero massimo di elementi
size_t len; // numero attuale di elementi
size_t maxSize; // dimensione massima
size_t size; // dimensione attuale
pthread_mutex_t m; // lock sulla coda
} queueT;
/**
* Creazione di un fileT.
* \param f: string che identifica il fileT tramite il suo path assoluto
* \param O_LOCK: 1 locked, 0 unlocked
* \param client: client associato
* \param open: apertura del file dopo la creazione
*
* \retval puntatore al fileT, NULL se errore (errno)
*/
fileT* createFileT(char *f, int O_LOCK, int client, int open);
/**
* Append su un fileT.
* \param f: fileT sul quale scrivere
* \param data: puntatore al buffer da scrivere
* \param size: dimensione in bytes del buffer
*
* \retval 0 se successo, -1 se errore (errno)
*/
int writeFileT(fileT *f, void *data, size_t size);
/**
* Rimozione di un fileT.
* \param f
*/
void destroyFile(fileT *f);
/**
* Alloca ed inizializza una coda di fileT. Deve essere chiamata da un solo
* thread.
* \param maxLen: lunghezza massima della coda
* \param maxSize: dimensione massima della coda in bytes
*
* \retval puntatore alla coda allocata, NULL se errore
*/
queueT* createQueue(size_t maxLen, size_t maxSize);
/**
* Inserisce un fileT nella coda.
* \param q: puntatore alla coda
* \param data: puntatore al fileT da inserire
*
* \retval 0 se successo, -1 se errore (errno)
*/
int enqueue(queueT *q, fileT* data);
/**
* Estrae un fileT dalla coda.
* \param q: puntatore alla coda
*
* \retval puntatore al file estratto, NULL se errore
*/
fileT* dequeue(queueT *q);
/**
* Estrae un fileT dalla coda come la dequeue, ma invece di restituire il
* file estratto lo distrugge immediatamente, liberandone la memoria.
*
* \param q: puntatore alla coda
*/
void voidDequeue(queueT *q);
/**
* Stampa l'intero contenuto della coda.
* \param stream: file su cui stampare
* \param q: puntatore alla coda da stampare
*
* \retval 0 se successo, -1 se errore (errno)
*/
int printQueue(FILE *stream, queueT *q);
/**
* Cerca di ottenere il lock su un fileT, fallisce se un altro client ha gia'
* richiesto la lock
* \param q: puntatore alla coda che contiene il fileT
* \param filepath: path assoluto (identificatore) del fileT
* \param owner: client che ha richiesto l'operazione
*
* \retval 0 se successo, -1 se errore (errno)
*/
int lockFileInQueue(queueT *q, char *filepath, int owner);
/**
* Rilascia il lock di un file, fallisce se non si e' il possessore del lock
* \param q: puntatore alla coda che contiene il fileT
* \param filepath -> path assoluto (identificatore) del fileT
* \param owner -> client che ha richiesto l'operazione
*
* \retval 0 se successo, -1 se errore (errno)
*/
int unlockFileInQueue(queueT *q, char *filepath, int owner);
/**
* Apre un fileT contenuto nella coda. Fallisce se il lock appartiene a un
* client diverso.
* \param q: puntatore alla coda che contiene il fileT da aprire
* \param filepath: path assoluto (identificatore) del fileT da aprire
* \param O_LOCK: 1 locked, 0 unlocked
* \param owner: client che ha richiesto l'apertura
*
* \retval 0 se successo, -1 se errore (errno)
*/
int openFileInQueue(queueT *q, char *filepath, int O_LOCK, int owner);
/**
* Chiude un file contenuto nella coda. Ha successo se fileT non e' stato aperto.
* Fallisce se il file e' stato messo in modalita' locked da un client diverso.
* \param q: puntatore alla coda che contiene il fileT da chiudere
* \param filepath: path assoluto (identificatore) del fileT da chiudere
* \param owner: client che ha richiesto la chiusura
*
* \retval 0 se successo, -1 se errore (errno)
*/
int closeFileInQueue(queueT *q, char *filepath, int owner);
/**
* Scrive del contenuto su un fileT all'interno della coda. Fallisce se il file
* non e' stato aperto o se il lock appartiene a un client diverso.
* \param q: puntatore alla coda che contiene il fileT su cui scrivere
* \param filepath: path assoluto (identificatore) del fileT
* \param data: buffer che contiene i dati
* \param size: dimensione in bytes del buffer
* \param owner: client che ha richiesto l'operazione di scrittura
*
* \retval 0 se successo, -1 se errore (errno)
*/
int writeFileInQueue(queueT *q, char *filepath, void *data, size_t size, int owner);
/**
* Scrive del contenuto in append su un fileT all'interno della coda. Fallisce
* se il file non e' stato aperto o se il lock appartiene a un client diverso.
* \param q: puntatore alla coda che contiene il fileT su cui effettuare l'append
* \param filepath: path assoluto (identificatore) del fileT
* \param data: buffer che contiene i dati
* \param size: dimensione in bytes del buffer
* \param owner: client che ha richiesto l'operazione di append
*
* \retval 0 se successo, -1 se errore (setta errno)
*/
int appendFileInQueue(queueT *q, char *filepath, void *data, size_t size, int owner);
/**
* Rimuove un fileT dalla coda. Fallisce se non si è in possesso della lock o se
* la lock e' posseduta da un client diverso.
* \param q: puntatore alla coda che contiene il fileT da rimuovere
* \param filepath: path assoluto (identificatore) del fileT da rimuovere
* \param owner: client che ha richiesto la rimozione
*
* \retval 0 se successo, -1 se errore (errno)
*/
int removeFileFromQueue(queueT *q, char *filepath, int owner);
/**
* Cerca un fileT nella coda.
* \param q: puntatore alla coda sulla quale cercare il fileT
* \param filepath: path assoluto del fileT da cercare
*
* \retval puntatore a una copia del fileT se trovato, NULL se non trovato o errore (errno)
*/
fileT* find(queueT *q, char *filepath);
/**
* Numero di elementi presenti nella coda.
* \param q: puntatore alla coda
*
* \retval numero di elementi presenti, -1 se errore (errno)
*/
size_t getLen(queueT *q);
/**
* Restituisce la dimensione attuale della coda in bytes.
* \param q: puntatore alla coda
*
* \retval dimensione della coda in bytes, -1 se errore (errno)
*/
size_t getSize(queueT *q);
/**
* Cancella una coda allocata con createQueue e ne libera la memoria.
* \param q: puntatore alla coda da distruggere
*/
void destroyQueue(queueT *q);
#endif // _FILE_QUEUE