AtomCaml: First-Class Atomicity via Rollback
Transkrypt
AtomCaml: First-Class Atomicity via Rollback
AtomCaml: First-Class Atomicity via Rollback ● ● Autorzy: Michael F. Ringenburg i Dan Grossman Opracował Maciej Ciurlik na potrzeby przedmiotu Metody Bezpiecznego Programowania 1 Plan wykładu ● Dlaczego stosować atomowość ● Cechy AtomCaml ● atomic i yield_r ● Wywoływanie funkcji języka C ● Decyzje projektowe ● Implementacja AtomCaml ● Wydajność 2 Dlaczego stosować atomowość? „Writing code in which threads communicate via mutable shared memory will never be easy, but there is an increasing belief that locks and condition variables (the most common concurrency primitives in today’s high-level languages) make matters worse.” 3 Dlaczego stosować atomowość? ● ● ● Warunki wyścigu często występują w nowoczesnym oprogramowaniu W rozbudowanym oprogramowaniu poprawne stosowanie zamków jest trudne Odnalezienie fragmentów kodu powodujących błędy synchronizacji nie jest proste 4 Dlaczego stosować atomowość? ● ● ● Atomowe bloki kodu wykonają się bez przeplatania dostępu do pamięci (no interleaving), nawet jeśli kod jest kiepsko napisany. Fair scheduling pilnuje aby wątki nie zostały zagłodzone Programiści często uzyskują atomowość poprawnie stosując istniejące mechanizmy synchronizujące (Cormac Flanagan and Shaz Qadeer. Types for atomicity.) 5 Zastosowanie atomowości ● Systemy operacyjne ● Relacyjne bazy danych ● Systemy rozproszone ● Rosnące zainteresowanie wśród twórców języków programowania 6 Cechy AtomicCaml ● ● ● ● Mocniejsze gwarancje niż w przypadku implementacji dla Javy (Harris) Unikanie wyrafinowanych struktur danych oraz protokołów zatwierdzenia STM na rzecz prostego mechanizmu „loggingand-rollback” API pozwalające wywołać natywny kod z bloku atomowego Wsteczna kompatybilność z Ocaml 7 Wyrażenie atomic ● ● ● Funkcja typu (unit>’a)>’a Pobiera blok kodu jako argument i wykonuje go atomowo Implementacja zapewnia brak przeplatania w dostępie do pamięci 8 Wyrażenie atomic let totalWidgets = atomic (fun () > !blackWidgets let totalWidgets + !blueWidgets = !blackWidgets in + !blueWidgets in if totalWidgets >0 if totalWidgets > 0 then print_string (pickWidget () ^ " available") then print_string (pickWidget () ^ " available") else raise NoWidgets else raise NoWidgets) 9 Wyrażenie atomic ● ● ● Gdy wątek natrafi na kod w bloku atomowym zaczyna go wykonywać Jeśli wątek nie został wywłaszczony oznacza to, że kod wykonał się atomowo (kod nie jest wykonywany równolegle) Jeśli wątek zostanie wywłaszczony podczas wykonywania bloku atomowego zmiany dokonane przez ten kod są wycofywane, kod wykonywany jest ponownie przy kolejnym wywołaniu wątka 10 Wyrażenie atomic ● ● ● Blok atomic może zawierać: – Dowolny kod języka Ocaml włączając wywołania funkcji – Wywołania zewnętrznych funkcji napisanych wC Kod atomowy morze rzucać wyjątki, które mogą zostać obsłużone wewnątrz lub na zewnątrz bloku atomic Zagnieżdżanie wyrażenie atomic jest nadmiarowe. 11 Wyrażenie yield_r ● ● ● Zapis do bufora: – Sprawdź czy bufor jest pełny, jeśli nie to zapisz – Jeśli tak to czekaj Co jeśli bufor zostanie wypełniony po sprawdzeniu miejsca a przed naszym zapisem? Identyczny problem istnieje kiedy programujemy używając atomic 12 Wyrażenie yield_r ● ● ● Należy sprawdzić dostępność bufora wewnątrz tego samego bloku Jeśli bufor jest pełny kończymy wykonywanie bloku i ponawiamy zapis gdy pojawi się miejsce w buforze Czy wystarczy użyć yield ? 13 Wyrażenie yield_r yield Re-schedule the calling thread without suspending it. This function can be used to give scheduling hints, telling the scheduler that now is a good time to switch to other threads. 14 Wyrażenie yield_r „We noticed that the yielding code is often waiting for a mutable reference to have its contents changed and it is useless to rerun the thread until such change occur.” 15 Wyrażenie yield_r ● ● „We noticed that the yielding code is often waiting for a mutable reference to have its contents changed and it is useless to rerun the thread until such change occur.” yield_r x suspends the thread and allows (but does not require) the scheduler to skip the suspended thread whenever the contents of the reference bound to x are the same as when 16 yield_r was called Wyrażenie yield_r let add_to_bbuf bbuf item = Thread.atomic (fun () → if (is_full_bbuf bbuf) then Thread.yield_r bbuf.out_ptr else (); bbuf.buffer.(!(bbuf.in_ptr))<item; advance bbuf bbuf.in_ptr) let remove_from_bbuf bbuf = Thread.atomic (fun () → if (is_empty_bbuf bbuf) then Thread.yield_r bbuf.in_ptr else (); let ans = bbuf.buffer.(!(bbuf.out_ptr)) in advance bbuf bbuf.out_ptr; 17 ans) Wywoływanie funkcji języka C ● ● Jeśli kod może być uruchomiony atomowo bez żadnych modyfikacji: external foo : type_of_foo = "c_foo" Jeśli napisano osobną wersje funkcji bezpieczną do ładowania z sekcji atomowej: external foo : type_of_foo = "c_foo1 c_foo2" Wywołanie foo w bloku nie atomowym uruchomi c_foo1. Wywołanie foo w bloku atomowym uruchomi c_foo2 18 Wywoływanie funkcji języka C ● Jeśli funkcja nigdy nie ma być wywoływana atomowo: external foo : type = "c_foo1 raise_on_atomic" jeśli foo zostanie wywołane wewnątrz atomowego bloku to zostanie rzucony wyjątek wskazujący nazwę źle wywołanej funkcji 19 Przygotowanie atomowych wersji funkcji języka C ● ● caml_register_rollback_action(void( *reg_func)(void*), void* reg_env) Funkcja powoduje wywołanie reg_func z argumentem reg_env gdy wykonywany blok atomowy zostanie wycofany caml_register_commit_action(void (*reg_func)(void*), void* reg_env Funkcja powoduje wywołanie reg_func z argumentem reg_env gdy wykonywany blok atomowy zostanie zakończony 20 Decyzje projektowe ● Czy zezwolić na operacje wejścia w blokach atomowych? 21 Decyzje projektowe ● Czy zezwolić na operacje wejścia w blokach atomowych? – Każda funkcja wejścia wywoływana w bloku atomowym czyta kolejne wartości w buforze 22 Decyzje projektowe ● Czy zezwolić na operacje wejścia w blokach atomowych? – Każda funkcja wejścia wywoływana w bloku atomowym czyta kolejne wartości w buforze – W przypadku wycofania przeczytane wartości zostają z powrotem wpisane do bufora 23 Decyzje projektowe ● Czy zezwolić na operacje wejścia w blokach atomowych? – Każda funkcja wejścia wywoływana w bloku atomowym czyta kolejne wartości w buforze – W przypadku wycofania przeczytane wartości zostają z powrotem wpisane do bufora Konieczność sprawdzenia przez wszystkie funkcje wejścia (nawet nie atomowe) czy bufor nie jest zajęty przez inne wycofywane wątki. 24 Decyzje projektowe ● Czy zezwolić na operacje wejścia w blokach atomowych? – Każda funkcja wejścia wywoływana w bloku atomowym czyta kolejne wartości w buforze – W przypadku wycofania przeczytane wartości zostają z powrotem wpisane do bufora Brak możliwości ograniczenia wielkości bufora ze względu na niemożność przewidzenia ilości danych do wycofania 25 Decyzje projektowe ● ● Bufor operacji wyjścia dla funkcji atomowych nie może zostać ograniczony bo nie wolno go opróżnić do momentu zakończenia wykonywania bloku atomowego Bufor operacji wyjścia dla funkcji nie atomowych może zostać opróżniony w dowolnym momencie 26 Decyzje projektowe ● Co zrobić gdy wyjątek rzucony w bloku atomowym nie zostanie obsłużony wewnątrz tego bloku? 27 Decyzje projektowe ● Co zrobić gdy wyjątek rzucony w bloku atomowym nie zostanie obsłużony wewnątrz tego bloku? – Zakończyć wykonywanie bloku? – Wycofać dokonane zmiany? 28 Decyzje projektowe Co zrobić gdy wyjątek rzucony w bloku atomowym nie zostanie obsłużony wewnątrz tego bloku? let x = ref 0 atomic (fun () → x := 1; f() (* f may raise an exception *)) if !x = 0 then failwith "huh" else ... 29 Implementacja AtomCaml ● W przypadku wycofania bloku żadne dowody częściowego wykonania kodu nie mogą być widoczne 30 Implementacja AtomCaml ● W przypadku wycofania bloku żadne dowody częściowego wykonania kodu nie mogą być widoczne – Odczyty nie są logowane – System loguje wszystkie wykonane zapisy oraz poprzednie wartości – Zmiany lokalne wewnątrz bloku atomowego nie są logowane 31 Implementacja AtomCaml 32 Implementacja AtomCaml ● Jak upewnić się, że atomowy blok zostanie w końcu wykonany i sprawiedliwie zarządzać zasobami 33 Implementacja AtomCaml ● Jak upewnić się, że atomowy blok zostanie w końcu wykonany i sprawiedliwie zarządzać zasobami – Scheduler przyznaje więcej czasu po 2x nieudanych próbach wykonania kodu – Scheduler pomija wątki przez kilka rund proporcjonalnie do ilości dodatkowego czasu – Scheduler może nadać wyższy priorytet innym wątkom. 34 Implementacja AtomCaml ● Jak logować zmiany dokonane przez zewnętrzne funkcje wywołane z bloku atomowego nie spowalniając tych samych funkcji wywołanych nie atomowo? 35 Implementacja AtomCaml ● Jak logować zmiany dokonane przez zewnętrzne funkcje wywołane z bloku atomowego nie spowalniając tych samych funkcji wywołanych nie atomowo? – Kompilator generuje dwie wersje funkcji „logującą” dla kodu atomowego oraz „zwykłą” dla pozostałego kodu 36 Wydajność ● Wpływ na wydajność? 37 Wydajność 38 Koniec ● Dziękuje za uwagę ● Pytania? 39