Semafori in ADA

 

4 Implementazione

4 Uso

4 Download codice semafori ed esempi (file .zip)

4 Supporto Tecnico

 

 

 Implementazione

  

Il package usr_semaphore, realizzato da Saltarelli Ettore come progetto di Sistemi per l'elaborazione dell'informazione II nel corso dell'A.A: 1997/98, definisce i semafori a conteggio e i semafori privati. Un semaforo è un'istanza di un tipo task semaphore le cui entry sono:

  • init (d: in integer), che inizializza il semaforo ad un dato valore (1 per i semafori binari, un intero maggiore di uno per i semafori a conteggio;
  • P, che implementa l'accesso al semaforo;
  • V, che implementa il rilascio del semaforo;
  • Close, che serve per la terminazione dell'istanza del tipo task semaphore.

Task body semaphore is

disp : integer;

again : boolean := true;

begin

if n < 0 then

raise INVALID_SEMAPHORE_VALUE;

end if;

disp := n;

while again loop

select

when disp > 0 =>

accept P;

disp := disp - 1;

or

accept V;

disp := disp + 1;

or

accept Init (d : in integer) do

disp := d;

end;

or

accept Close;

again := false;

or

delay infinite;

again := false;

end select;

end loop;

end;

Il tipo semaphore è riservato e limitato, per cui il programmatore può richiamare le entry solo attraverso le apposite procedure definite nel package. Queste procedure sono di due classi: una implementa i semafori a conteggio e l'altra i semafori privati.

Per quanto riguarda i semafori a conteggio le procedure accettano come parametro l'istanza del task semaphore. Il codice relativo alle quattro procedure per operare con i semafori a conteggio è:

-- procedura P

procedure P (s : in out semaphore) is

begin

s.P;

end;

-- procedura V

procedure V (s : in out semaphore) is

begin

s.V;

end;

-- procedura Init:

--definisce la disponibilita' di un semaforo

procedure Init (s : in out semaphore; d : in integer) is

begin

if d < 0 then

raise INVALID_SEMAPHORE_VALUE;

end if;

s.Init (d);

end;

-- procedura Close:

--termina il task associato al semaforo

procedure Close (s : in out semaphore) is

begin

s.Close;

end;

Qualche parola in più meritano i semafori privati. A differenza della PrivV, che può essere eseguita da qualsiasi task, la PrivP può essere eseguita soltanto dal task proprietario del semaforo. Bisogna quindi associare ad ogni processo il proprio semaforo ed a tale scopo si definisce una tavola di semafori privati in cui ogni elemento è un record di due campi: un identificatore del task proprietario (tipo Task_ID definito nel package ADA.Task_Identification) ed un puntatore ad un semaforo.

type access_semaphore is access semaphore;

type t_sem_descriptor is

record

ID : Task_ID := Null_Task_ID;

sem : access_semaphore := null;

end record;

descriptor_table : array (1..max_priv_sem) of t_sem_descriptor;

ID_NOT_FOUND : constant := 0;

Ci sono poi due funzioni per la gestione della descriptor_table:

-- funzione find_ID_in_table:

--ritorna la posizione di id nella descriptor_table

--oppure ID_NOT_FOUND se non e' presente

function find_ID_in_table (id : in Task_ID) return integer is

found : boolean := false;

i : integer := 1;

begin

while (i <= max_priv_sem) and (not found) loop

if (descriptor_table(i).sem /= null) then

if (descriptor_table(i).sem'terminated) then

descriptor_table(i).ID := Null_Task_ID;

descriptor_table(i).sem := null;

end if;

end if;

if descriptor_table(i).ID = id then

found := true;

else

i := i + 1;

end if;

end loop;

if found then

return i;

else

return ID_NOT_FOUND;

end if;

end;

-- funzione find_ID:

--se id e' Null_Task_ID innalza INVALID_TASK_ID, altrimenti

--se e' presente, ritorna la posizione di id nella

--descriptor_table, altrimenti se c'e' un posto libero ne

--inizializza il contenuto e ne ritorna la posizione,

--altrimenti innalza l'eccezione FULL_PRIVATE_SEMAPHORE_TABLE

function find_ID (id : in Task_ID) return integer is

pos : integer;

begin

if id = Null_Task_ID then

raise INVALID_TASK_ID;

end if;

pos := find_ID_in_table (id);

if pos = ID_NOT_FOUND then

pos := find_ID_in_table (Null_Task_ID);

if pos = ID_NOT_FOUND then

raise FULL_PRIVATE_SEMAPHORE_TABLE;

end if;

descriptor_table(pos).ID := id;

descriptor_table(pos).sem := new semaphore;

end if;

return pos;

end;

 

Infine, le quattro procedure utilizzabili dall'utente sono:

-- procedura PrivP

procedure PrivP is

curr_ID : Task_ID;

pos : integer;

begin

curr_ID := Current_Task;-- ritorna il proprio indice

pos := find_ID (curr_ID);

P (descriptor_table(pos).sem.all);

end;

-- procedura PrivV

procedure PrivV (id : in Task_ID) is

pos : integer;

begin

pos := find_ID (id);

V (descriptor_table(pos).sem.all);

end;

-- procedura PrivInit:

--definisce la disponibilita' del semaforo privato

procedure PrivInit (id : in Task_ID; d : in integer) is

pos : integer;

begin

pos := find_ID (id);

Init (descriptor_table(pos).sem.all, d);

end;

-- procedura PrivClose:

--termina il task associato al semaforo privato e libera la

--posizione nella descriptor_table

procedure PrivClose is

curr_ID : Task_ID;

pos : integer;

begin

curr_ID := Current_Task;-- ritorna il proprio indice

pos := find_ID_in_table (curr_ID);

if pos /= ID_NOT_FOUND then

Close (descriptor_table(pos).sem.all);

descriptor_table(pos).ID := Null_Task_ID;

descriptor_table(pos).sem := null;

end if;

end;

 

 

 

 

Uso

  

 L'uso dei semafori in un programma ADA non è diverso da quello noto dalla teoria della programmazione concorrente. Naturalmente, il primo passo è quello di includere il package contenente i semafori attraverso le istruzioni:

with usr_semaphore;

use usr_semaphore;

Poi, per definire un semaforo, bisogna definire un'istanza del tipo semaphore con la dichiarazione:

<nome_semaforo> : semaphore(<num_intero>);

in cui <nome_semaforo> è il nome che si vuole dare all'istanza e <num_intero> è il numero che identifica il massimo numero di accessi contemporanei alla sezione critica (1 per semafori binari e valori maggiori di 1 per semafori a conteggio).

A questo punto è possibile utilizzare le P e le V per risolvere i problemi di programmazione concorrente.

Alcuni esempi sull'uso di questo package sono inclusi nel file .zip scaricabile dall'apposito link all'inizio della pagina; essi sono:

  • Problema dei filosofi a cena (Saltarelli);
  • Problema dei lettori-scrittori (Saltarelli);
  • Produttore-consumatore (riscrittura del codice del progetto di Claudio Rossi, Davide Benedetti e Nicola Colonna con i semafori di Saltarelli);
  • Esercizio d'esame: "Un canale navigabile è attraversato da una strada con due ponti girevoli A e B posti a distanza relativamente breve. Il canale è percorso da barche nella direzione dal ponte B al ponte A. La strada è a senso unico ed il senso di percorrenza è da A a B. Le imbarcazioni possono superare i ponti solo quando questi sono aperti, mentre le auto possono usare i ponti solo se questi sono chiusi. Un ponte può essere percorso al più da un'automobile. Utilizzando le primitive sui semafori, scrivere le procedure eseguite da auto e barche, garantendo lo svolgimento di un traffico ordinato (riscrittura del codice del progetto di Claudio Rossi, Davide Benedetti e Nicola Colonna con i semafori di Saltarelli).

 

 

 

 Supporto Tecnico

  

Per eventuali problemi, chiarimenti e/o suggerimenti scrivete a: clemente@csr.unibo.it