Implementacja bariery w RMI i adzie
Transkrypt
Implementacja bariery w RMI i adzie
Implementacja mechanizmu bariery w środowisku RMI oraz w j˛ezyku Ada (z wykorzystaniem pakietu Glade) Jakub Gorgolewski, nr indeksu 55456 15 czerwca 2005 1 Wst˛ep Celem ćwiczenia było zaimplementowanie mechanizmu bariery. Przyj˛eto nast˛epujace ˛ założenia odnośnie jej funkcjonalności. Każdy dost˛ep do bariery realizowany jest z jednym parametrem o nazwie count. Proces pozostaje zablokowany na barierze dopóki parametr count jest mniejszy niż liczba procesów zablokowanych na barierze. Warunkiem wyjścia z blokady nazywany jest w tej pracy warunek: liczba procesów na barierze ≥ parametr count Przy realizacji barier przyj˛eto, że parametr count może przyjmować różne wartości dla różnych procesów, co może prowadzić do takich sytuacji, że proces wywołujacy ˛ barier˛e powoduje spełnienie warunku wyjścia dla innych procesów na niej zablokowanych nie majac ˛ samemu spełnionego tego warunku. 2 Implementacja w środowisku RMI Interfejs serwera bariery jest prosty. Składa si˛e z deklaracji jednej jedno argumentowej funkcji realizujacej ˛ główne zadanie bariery. Kod interfejsu przedstawiony jest na listingu 5. Kod klienta jest również prosty, gdyż sprowadza si˛e do połaczenia ˛ z serwerem RMI i wywołania metody barrier z argumentem pobranym z linii komend. Został on przedstawiony na listingu 6. Najważniejsze przetwarzanie realizowane jest po stronie serwera w metodzie barrier w linijkach 23–37. Wykorzystywane w nim sa˛ nast˛epujace ˛ prywatne pola typu int: • CurrentProcNum – przechowujace ˛ liczb˛e procesów aktualnie b˛edacych ˛ na blokadzie • NotifyProcNum – przechowujace ˛ liczb˛e procesów b˛edacych ˛ na blokadzie w momencie, gdy rozpocz˛eto testowanie warunku wyjścia • NotifyReceived – przechowujace ˛ liczb˛e procesów, które jeszcze nie sprawdziły warunku wyjścia w tym cyklu Należy zwrócić uwag˛e, że jest to metoda synchronizowana, co oznacza, że równocześnie do tej metody może si˛e odwoływać tylko jeden proces. Przetwarzanie realizowane w kodzie przedstawionym na listingu 1 można podzielić na kilka etapów: 1 23–25 kod ten ma zapobiegać sytuacji, gdy bariera wywoływana jest przez nowy proces zanim sprawdzone zostana˛ warunki wyjścia wszystkich procesów oczekujacych ˛ na barierze; 26–31 przygotowanie zmiennych i obudzenie procesów oczekujacych ˛ na barierze; 32–35 odnotowanie sprawdzenia warunku wyjścia i samo jego sprawdzenie – jeśli jest prawdziwy przerywana jest p˛etla while, w przeciwnym wypadku proces jest usypiany 36–37 odnotowanie opuszczenia bariery 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 Listing 1: Główny kod serwera bariery - Java while(NotifyReceived != 0) { wait(); } NotifyReceived = CurrentProcNum; CurrentProcNum++; NotifyProcNum = CurrentProcNum; notifyAll(); while(count != NotifyProcNum) { wait(); NotifyReceived--; } CurrentProcNum--; Pełen kod serwera znajduje si˛e w listingu 7 na końcu tego dokumentu. 3 Implementacja w j˛ezyku Ada Przy implementacji bariery w j˛ezyku Ada zastosowano nast˛epujac ˛ a˛ konfiguracj˛e (listing 12). Jedyny interfejs zdalnego wywołania (RCI– Remote Call Interface) wchodzi w skład partycji serwera i jest zadeklarowany w plikach barrierserver.ads i barrierserver.adb (listingi 10 i 11). Jako główna procedura systemu została zadeklarowana procedura BarrierManager, a jako główna procedura partycji klienckiej BarrierClient. Dodatkowo zadeklarowano, iż partycja kliencka może kończyć prace nie czekajac ˛ na zakończenie pracy partycji serwerowej. Implementacja procedury BarrierClient (listing 8) jest zbliżona do jej odpowiednika w Javie, a procedura BarrierManager (listing 9) sprowadza si˛e jedynie do oczekiwania na sygnał do zakończenia pracy serwera. Podobnie jak w przypadku implementacji w j˛ezyku Java, najważniejsze przetwarzanie realizowane jest po stronie serwera. W tym przypadku rol˛e metody synchronizowanej pełni obiekt chroniony, zdefiniowany w liniach 20–56. Wykorzystuje on cztery zmienne typu integer, gdzie trzy pełnia˛ analogiczne role co pola opisane przy implementacji bariery w środowisku RMI. Dodatkowa zmienna, ReNotRec, służy do zliczania ile procesów jeszcze nie zostało przekierowanych na prywatna metod˛e wejściowa˛ wait. Ponadto w obiekcie chronionym barrier zadeklarowano 3 procedury wejścia: jedna˛ publiczna˛ – barrier i dwie prywatne – wait i rewait. Ich kod został przedstawiony kolejno na listingach 2, 3 i 4. W procedurze block warunki wejścia sa˛ prawdziwe wtedy i tylko wtedy, gdy wszystkie procesy korzystajace ˛ w danej chwili z bariery opuszczaja˛ ja˛ lub zostały już przekierowane do procedury wait. Po wejściu do procedury ustawiane sa˛ zmienne powodujace ˛ „zablokowanie” wejścia do procedury block i „odblokowanie” wejścia do procedury wait. Dzi˛eki obecności mechanizmu warunków wejścia nie musimy wywoływać specjalnej procedury do budzenia procesów oczekujacych, ˛ jak to było w przypadku Javy. Dalej sprawdzane sa˛ warunki wyjścia i w przypadku ich niespełnienia sterowanie przekierowane jest do procedury rewait. 2 Listing 2: Procedura wejścia block 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 entry block(count: in Integer) when (NotRec = 0) and (ReNotRec = 0) is begin NotRec := CurProcNum; ReNotRec := CurProcNum; CurProcNum := CurProcNum + 1; NotProcNum := CurProcNum; if(count <= NotProcNum) then CurProcNum := CurProcNum - 1; else ReNotRec := ReNotRec + 1; requeue rewait; end if; end; Procedura wait jest odpowiednikiem funkcji wait z j˛ezyka Java. Procesy usypiane sa˛ po przez zablokowanie ich na warunkach wejściowych, a dokłaniej, gdy zmienna NotRec jest równa 0. Ponieważ każdy proces, który wejdzie do procedury wait dekrementuje ta˛ zmienna˛ to ustawienie jej na n powoduje wpuszczenie n procesów. Dalej, podobnie jak w przypadku procedury block sprawdzane sa˛ warunki wyjścia i w przypadku ich niespełnienia sterowanie przekierowywane jest do procedury rewait. Listing 3: Procedura wejścia wait 38 39 40 41 42 43 44 45 46 47 48 entry wait(count: in Integer) when NotRec > 0 is begin NotRec := NotRec - 1; if count <= NotProcNum then CurProcNum := CurProcNum - 1; ReNotRec := ReNotRec - 1; else requeue rewait; end if; end; Procedura rewait została dodana, ponieważ nie przyj˛eto żadnych założeń co do kolejności obsługi procesów oczekujacych ˛ na warunku wejściowym do procedury wejściowej. Procesy wywołujace ˛ rewait sa˛ blokowane na warunku wejściowym póki kolejka procesów oczekujacych ˛ na wejście do procedury wait nie bedzie pusta. Dzieki temu mamy gwarancj˛e, iż każdy proces oczekujacy ˛ wykona procedur˛e wait (a co za tym idzie sprawdzenia warunków wyjścia) dokładnie raz na jeden cykl „budzenia” zainicjowany przez nowy proces wywołujacy ˛ procedure block. Listing 4: Procedura wejścia rewait 50 51 52 53 54 55 4 entry rewait(count: in Integer) when (NotRec = 0) and (ReNotRec > 0) is begin ReNotRec := ReNotRec - 1; requeue wait; end; Podsumowanie Zademonstorowane powyżej przykłady implementacji mechanizmu bariery w dwóch wysoko poziomowych j˛ezykach programowania aż prosza˛ si˛e o porównanie. Jak widać Ada w wi˛ekszym stopniu pozwala 3 si˛e skupić na samej cz˛eści implementacyjnej izolujac ˛ konfiguracj˛e środowiska pracy. Z drugiej strony, Java dysponuje, nie tak wszechstronnymi, ale bardziej intuicyjnymi mechanizmami synchronizacji procesów. Spis pełnych listingów plików 5 6 7 8 9 10 11 12 Interfejs bariery - Java (BarrierInterface.java), . . . Kod klienta bariery - Java (BarrierClient.java) . . . Kod serwera bariery - Java (BarrierServer.java) . . Kod klienta bariery - Ada (barrierclient.adb) . . . . Kod “zarzadcy” ˛ bariery - Ada (barriermanager.adb) Nagłówek serwera bariery - Ada (barrierserver.ads) Kod serwera bariery - Ada (barrierserver.adb) . . . Konfiguracja partycji - Glade (barrierconf.cfg) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 4 4 5 6 6 6 7 Listing 5: Interfejs bariery - Java (BarrierInterface.java), . . . . . . . . 1 2 3 4 5 6 import java.rmi.Remote; import java.rmi.RemoteException; public interface BarrierInterface extends Remote{ public void barrier(int count) throws RemoteException; } Listing 6: Kod klienta bariery - Java (BarrierClient.java) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import import import import java.rmi.Naming; java.rmi.RMISecurityManager; java.rmi.RemoteException; java.rmi.server.UnicastRemoteObject; public class BarrierClient { public static void main(String[] args) { if(System.getSecurityManager() == null) { System.setSecurityManager(new RMISecurityManager()); } try { String name = "//localhost:1099/Barrier"; BarrierInterface barrier = (BarrierInterface) Naming.lookup( name); System.out.println(args[0]); barrier.barrier(Integer.parseInt(args[0])); } catch(Exception e) { System.err.println("Barrier critical: " + e.getMessage()); e.printStackTrace(); } } } Listing 7: Kod serwera bariery - Java (BarrierServer.java) 1 2 3 4 5 import import import import import java.rmi.Naming; java.rmi.RMISecurityManager; java.rmi.RemoteException; java.rmi.server.UnicastRemoteObject; java.lang.Thread; 4 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 public class BarrierServer extends UnicastRemoteObject implements BarrierInterface { private int CurrentProcNum; private int NotifyProcNum; private int NotifyReceived; public BarrierServer() throws RemoteException { super(); CurrentProcNum = 0; NotifyReceived = 0; } synchronized public void barrier(int count) { try { while(NotifyReceived != 0) { wait(); } NotifyReceived = CurrentProcNum; CurrentProcNum++; NotifyProcNum = CurrentProcNum; notifyAll(); while(count != NotifyProcNum) { wait(); NotifyReceived--; } CurrentProcNum--; } catch (Exception e) { System.err.println("BarrierServer critical while invocation: " + e.getMessage()); e.printStackTrace(); } } public static void main(String[] args) { if(System.getSecurityManager() == null) { System.setSecurityManager(new RMISecurityManager()); } String name = "//localhost:1099/Barrier"; try { BarrierServer engine = new BarrierServer(); Naming.bind(name, engine); System.out.println("BarrierServer online"); } catch(Exception e) { System.err.println("BarrierServer critical: " + e.getMessage() ); e.printStackTrace(); } } } Listing 8: Kod klienta bariery - Ada (barrierclient.adb) 5 1 2 3 4 5 6 7 8 9 10 11 12 13 procedure barrierClient is package IntIO is new ada.text_io.integer_io(integer); use IntIO; begin if argument_count = 0 then put("One argument required!"); else barrierServer.barrier(integer’value(argument(1))); end if; end; Listing 9: Kod “zarzadcy” ˛ bariery - Ada (barriermanager.adb) 1 2 3 4 5 6 7 8 9 10 with ada.text_io, barrierServer; use ada.text_io; procedure barrierManager is Request : String (1 .. 16); Length : Natural; begin put("Insert anything to quit."); get_line(Request, Length); end barrierManager; Listing 10: Nagłówek serwera bariery - Ada (barrierserver.ads) with ada.text_io, ada.command_line, barrierServer; use ada.text_io, ada.command_line; 1 2 3 4 5 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package barrierServer is pragma Remote_Call_Interface; procedure barrier(arg: in integer); end barrierServer; Listing 11: Kod serwera bariery - Ada (barrierserver.adb) with ada.text_io; use ada.text_io; package body barrierServer is protected type barrier_type is entry block(count: in Integer); private entry wait(count: in Integer); entry rewait(count: in Integer); CurProcNum: Integer NotProcNum: Integer NotRec: Integer ReNotRec: Integer end barrier_type; :=0; :=0; :=0; :=0; protected body barrier_type is 6 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 1 2 3 4 5 6 7 8 9 10 11 entry block(count: in Integer) when (NotRec = 0) and (ReNotRec = 0) is begin NotRec := CurProcNum; ReNotRec := CurProcNum; CurProcNum := CurProcNum + 1; NotProcNum := CurProcNum; if(count <= NotProcNum) then CurProcNum := CurProcNum - 1; else ReNotRec := ReNotRec + 1; requeue rewait; end if; end; entry wait(count: in Integer) when NotRec > 0 is begin NotRec := NotRec - 1; if count <= NotProcNum then CurProcNum := CurProcNum - 1; ReNotRec := ReNotRec - 1; else requeue rewait; end if; end; entry rewait(count: in Integer) when (NotRec = 0) and (ReNotRec > 0) is begin ReNotRec := ReNotRec - 1; requeue wait; end; end barrier_type; barrier_inst: barrier_type; procedure barrier(arg: in integer) is begin barrier_inst.block(arg); end barrier; end barrierServer; Listing 12: Konfiguracja partycji - Glade (barrierconf.cfg) configuration barrierConf is pragma Starter(none); pragma Boot_Location("tcp", "localhost:6000"); Server : Partition := (barrierServer); Client : Partition ; for Client’Termination use Local_Termination; procedure barrierManager is in Server; procedure barrierClient; 7 12 13 14 for Client’Main use barrierClient; end barrierConf; 8