Nella precedente lezione abbiamo esaurientemente spiegato le funzioni e le procedure necessarie a gestire le porte messaggi. In questa lezione prenderemo in esame, come promesso quelle riguardanti i semafori. Vediamole:
1) ULONG Procure( struct SignalSemaphore *sigSem, struct SemaphoreMessage *bidMsg );
2) void Vacate( struct SignalSemaphore *sigSem, struct SemaphoreMessage *bidMsg );
3) void InitSemaphore( struct SignalSemaphore *sigSem );
4) void ObtainSemaphore( struct SignalSemaphore *sigSem );
5) void ReleaseSemaphore( struct SignalSemaphore *sigSem );
6) ULONG AttemptSemaphore( struct SignalSemaphore *sigSem );
7) void ObtainSemaphoreList( struct List *sigSem );
8) void ReleaseSemaphoreList( struct List *sigSem );
9) struct SignalSemaphore *FindSemaphore( UBYTE *sigSem );
10) void AddSemaphore( struct SignalSemaphore *sigSem );
11) void RemSemaphore( struct SignalSemaphore *sigSem );
12) void ObtainSemaphoreShared( struct SignalSemaphore *sigSem );
13) ULONG AttemptSemaphoreShared( struct SignalSemaphore *sigSem );
In pratica per creare un semaforo basta allocare memoria per una struttura SignalSemaphore, chiamare la InitSemaphore per inizializzarla, e inserirlo nella lista di sistema con la AddSemaphore.
A questo punto per utilizzarlo, serve ottenerlo e poi poterlo rilasciare. Bene, l'AmigaOS ci mette a disposizione molteplici funzioni per ogni possibilità. Vediamo quelle per ottenere (che devono ovviamente essere usate con le loro corrispettive).
La funzione Procure e la sua relativa Vacate, fino alla versione V39 del sistema operativo erano molto bacate e sconsigliate. Al loro posto veniva caldeggiato l'uso della 7 e della 8 che in pratica svolgono le stesse funzioni. Ma mentre la ObtainSemaphore blocca il task se il semaforo non è libero, la Procure, invia una richiesta al semaforo e ritorna. Quando il semaforo diventa disponibile, il precedente messaggio di richista viene replicato ed il task ottiene il semaforo. Questo sistema permette di aspettare su diversi semafori e di continuare a lavorare in attesa che si liberino. Ovviamente, un utilizzo del genere obbliga una maggior complessità del task che non potra` assumere che il ritorno della Procure coincida con l'effettivo possesso del semaforo richiesto come avviene invece per la ObtainSemaphore. Esaminate quindi la Procure/Vacate e la ObtainSemaphore/ReleaseSemaphore, passiamo a vedere le versioni evolute.
La ObtainSemaphoreList, differisce dalla ObtainSemaphore per il fatto di richedere una lista di semafori anziche` uno solo. Il sistema operativo garantisce l'assenza di deadlock (vi ricordate la prima puntata?) nel caso in cui uno o più dei semafori richiesti siano già occupati a patto che non venga usata la ObtainSemaphore su uno o piu` dei semafori presenti nella lista. Infatti si consiglia l'uso di un semaforo per controllare l'accesso alla lista...
Inoltre, un altro accorgimento che deve essere preso, è che una lista di semafori non può essere aggiunta alla lista dei semafori pubblici per via di alcune incompatibilita` di interpretazione di alcuni campi della struttura SignalSemaphore tra le funzioni Obtain/ReleaseSemaphoreList e le altre. Ovviamente la ReleaseSemaphoreList serve per rilasciare la lista di semafori precedentemente ottenuti. La AttemptSemaphore è simile alla Procure, perche` ritorna TRUE se e` riuscita a bloccare il semaforo e FALSE altrimenti.
Valgono ovviamente gli stessi discorsi per la Procure in merito alla complessità della logica del programma. Abbiamo infine le varianti Shared della Obtain/Attempt/ReleaseSemaphore. Queste tre funzioni, servono per effettuare un lock non esclusivo, ma appunto condiviso, su di un semaforo. Può essere utile quando una determinata struttura, per esempio una lista, viene letta molte volte e solo raramente scritta. In questo modo è possibile lasciare accedere i task che vogliono leggere quando nessun task che scrive ha il semaforo ed essere sospesi se un task scrivente sta accedendo alla struttura.
Queste ultime funzioni, essendo molto giovani, funzionano correttamente solo a partire dalla versione V39 del sistema operativo, a cui il corso fa e farà riferimento. Sugli amigaguide di exec sono presenti alcuni accorgimenti per ottenere lo stesso risultato su versioni precedenti che pero` noi non prenderemo in esame.
Anche se la struttura SignalSemaphore, non necessita in pratica di essere toccata dall'utente (fatta eccezione per impostare il nome e la priorita` del semaforo), vediamola ugualmente per dovere di cronaca:
struct SignalSemaphore {
struct Node ss_Link;
WORD ss_NestCount;
struct MinList ss_WaitQueue;
struct SemaphoreRequest ss_MultipleLink;
struct Task *ss_Owner;
WORD ss_QueueCount;
};
E` con rammarico, che mi accorgo solo adesso di non aver ancora introdotto le liste di sistema e le relative strutture.
Colgo quindi l'occasione per allungare la lezione ed introdurre quindi adesso un nuovo argomento fresco fresco. Torneremo alla struttura SignalSemaphore alla fine della lezione. La exec.library, tra le sue 137 funzioni, ne mette a disposizione 8, vediamole:
void Insert( struct List *list, struct Node *node, struct Node *pred );
void AddHead( struct List *list, struct Node *node );
void AddTail( struct List *list, struct Node *node );
void Remove( struct Node *node );
struct Node *RemHead( struct List *list );
struct Node *RemTail( struct List *list );
void Enqueue( struct List *list, struct Node *node );
struct Node *FindName( struct List *list, UBYTE *name );
struct Node {
struct Node *ln_Succ; /* Pointer to next (successor) */
struct Node *ln_Pred; /* Pointer to previous (predecessor) */
UBYTE ln_Type;
BYTE ln_Pri; /* Priority, for sorting */
char *ln_Name; /* ID string, null terminated */
}; /* Note: word aligned */
/* minimal node -- no type checking possible */
struct MinNode {
struct MinNode *mln_Succ;
struct MinNode *mln_Pred;
};
struct List {
struct Node *lh_Head;
struct Node *lh_Tail;
struct Node *lh_TailPred;
UBYTE lh_Type;
UBYTE l_pad;
}; /* word aligned */
/*
* Minimal List Header - no type checking
*/
struct MinList {
struct MinNode *mlh_Head;
struct MinNode *mlh_Tail;
struct MinNode *mlh_TailPred;
}; /* longword aligned */
#define LIST(list) ((struct List*)(list))
#define NODE(node) ((struct Node*)(node))
/* Inizializzazione di una lista */
#define NEW_LIST(l) LIST(l)->lh_Head = NODE(&LIST(l)->lh_Tail), \
LIST(l)->lh_TailPred = NODE(&LIST(l)->lh_Head), \
LIST(l)->lh_Tail = NULL
/* Inserimento del nodo <n> in testa nella lista <l> */
#define ADD_HEAD(l,n) AddHead(LIST(l),NODE(n))
/* Inserimento del nodo <n> in coda nella lista <l> */
#define ADD_TAIL(l,n) AddTail(LIST(l),NODE(n))
/* Inserimento del nodo <n> nella lista <l> dopo il nodo <p> */
#define INSERT(l,n,p) Insert(LIST(l),NODE(n),NODE(p))
/*
** Rimozione del nodo di testa, di tipo <t>, dalla lista <l>
** Viene restituito il puntatore al nodo rimosso
*/
#define REM_HEAD(l,t) (struct t *)RemHead(LIST(l))
/*
** Rimozione del nodo di coda, di tipo <t>, dalla lista <l>
** Viene restituito il puntatore al nodo rimosso
*/
#define REM_TAIL(l,t) (struct t *)RemTail(LIST(l))
/*
** Rimozione del nodo <n>. Il nodo viene rimosso dalla lista in cui si
** trova, qualunque essa sia. ATTENZIONE: il nodo DEVE essere inserito
** in una lista
*/
#define REMOVE(n) Remove(NODE(n))
/* Verifica se la lista <l> non contiene elementi */
#define ISEMPTY(l) (LIST(l)->lh_Head->ln_Succ == NULL)
/* Verifica se il nodo <n> e' il nodo fittizio di testa */
#define ISHEAD(n) (NODE(n)->ln_Pred == NULL)
/* Verifica se il nodo <n> e' il primo della lista */
#define ISFIRST(n) (ISHEAD(NODE(n)->ln_Pred))
/* Verifica se il nodo <n> e' il nodo fittizio di coda */
#define ISTAIL(n) (NODE(n)->ln_Succ == NULL)
/* Verifica se il nodo <n> e' l'ultimo della lista */
#define ISLAST(n) (ISTAIL(NODE(n)->ln_Succ))
/* Valuta al primo elemento della lista <l>, forzandone il tipo <t> */
#define HEAD(l,t) ((struct t *)LIST(l)->lh_Head)
/* Valuta all'ultimo elemento della lista <l>, forzandone il tipo <t> */
#define TAIL(l,t) ((struct t *)LIST(l)->lh_TailPred)
/* Valuta all' elemento successore del nodo <n>, forzandone il tipo <t> */
#define SUCC(n,t) ((struct t *)NODE(n)->ln_Succ)
/* Valuta all' elemento predecessore del nodo <n, forzandone il tipo <t> */
#define PRED(n,t) ((struct t *)NODE(n)->ln_Pred)
Bene, per finire questa lunghissima puntata, concludo raccontandovi la struttura SignalSemaphore:
ss_Link = per memorizzare nome e priorità del semaforo, oltre per linkarlo nella lista.
ss_NestCount = numero di lock effettuati dal task proprietario, il semaforo viene rilasciato solo quando il numero di unlock e` pari al numero di lock effettuati.
ss_WaitQueue = lista dei task che aspettano impazienti
ss_MultipleLink = struttura interna usata dalla ObtainSemaphore e soci
ss_Owner = l'attuale vincitore nella corsa al semaforo
ss_QueueCount = numero dei poverelli ancora in attesa.
Ovvio che modificare a mano questi campi non è consigliato!
Homepage: http://www.aspide.it/freeweb/hurricane/