Artykuł ten mówi:
„funkcja może być albo wklęsłego, bezpieczny wątku, zarówno, czy też nie.”
mówi również:
"funkcje dla wklęsłego są thread-niebezpieczne".
Widzę, jak to może powodować zamieszanie. Znaczy to, że standardowe funkcje udokumentowane jako niewymagane do ponownego wprowadzenia nie muszą być również bezpieczne dla wątków, co jest prawdą w bibliotekach POSIX iirc (i POSIX deklaruje, że jest to prawdziwe również w bibliotekach ANSI/ISO, mając ISO brak koncepcji nici, a więc brak koncepcji bezpieczeństwa nici). Innymi słowy, "jeśli jakaś funkcja mówi, że jest nie-reentrant, wtedy mówi, że jest również niebezpieczna dla wątków". To nie jest logiczna konieczność, to tylko konwencja.
Oto pseudo-kod, który jest bezpieczny dla wątków (cóż, istnieje wiele możliwości wywołań zwrotnych w celu utworzenia zakleszczenia z powodu inwersji blokowania, ale załóżmy, że dokumentacja zawiera wystarczającą ilość informacji, aby użytkownicy mogli tego uniknąć), ale nie powtórnie .Ma to zwiększyć globalny licznik oraz wykonywać oddzwanianie:
take_global_lock();
int i = get_global_counter();
do_callback(i);
set_global_counter(i+1);
release_global_lock();
Jeśli oddzwaniania tę procedurę jeszcze raz, co skutkuje innym zwrotnego, a następnie oba poziomy zwrotnego dostanie ten sam parametr (co może być OK, w zależności od API), ale licznik zostanie inkrementowany tylko jeden raz (co prawie na pewno nie jest interfejsem API, który chcesz, więc musiałby zostać zbanowany).
Zakładamy oczywiście, że zamek jest rekursywny. Jeśli blokada jest nierekurencyjna, to oczywiście kod jest nierezydentalny, ponieważ zablokowanie po raz drugi nie zadziała.
Oto niektóre pseudo-code, która jest „słabo ponownej instalacji” ale nie bezpieczny wątku:
int i = get_global_counter();
do_callback(i);
set_global_counter(get_global_counter()+1);
Teraz jest w porządku, aby wywołać funkcję z zwrotnego, ale nie jest to bezpieczne, aby wywołać funkcję jednoczesnego z różnych wątków. Nie jest również bezpiecznie wywoływać go z kontrolera sygnału, ponieważ ponowne wejście od obsługi sygnału może również przerwać liczenie, jeśli sygnał wystąpi we właściwym czasie. Tak więc kod jest niezarejestrowany według właściwej definicji.
Oto kod, który prawdopodobnie jest w pełni ponownie wprowadzony (chyba, że standard rozróżnia między reentrantem a "nieprzerywalnym sygnałami" i nie jestem pewien, gdzie to się dzieje), ale wciąż nie jest wątkiem bezpieczne:
int i = get_global_counter();
do_callback(i);
disable_signals(); // and any other kind of interrupts on your system
set_global_counter(get_global_counter()+1);
restore_signal_state();
W aplikacji jednowątkowej wszystko jest w porządku, zakładając, że system operacyjny obsługuje wyłączanie wszystkiego, co musi być wyłączone. Zapobiega ponownemu entrancy w punkcie krytycznym. W zależności od tego, jak wyłączone są sygnały, może być bezpiecznie wywołać z procedury obsługi sygnału, chociaż w tym konkretnym przypadku problem z przekazywaniem wywołania zwrotnego jest taki sam dla osobnych wywołań. Jednak nadal może pójść źle w wielowątkowych.
W praktyce, zabezpieczenie nie-wątkowe często implikuje brak ponownego wprowadzenia, ponieważ (nieformalnie) wszystko, co może pójść nie tak ze względu na przerwanie wątku przez program planujący, oraz funkcję wywoływaną ponownie z innego wątku, może również pójdzie źle, jeśli wątek zostanie przerwany przez sygnał, a funkcja zostanie ponownie wywołana z procedury obsługi sygnału. Ale wtedy "poprawka", aby zapobiec sygnałom (wyłączając je) różni się od "poprawki", aby zapobiec współbieżności (zwykle blokady). Jest to w najlepszym razie regułą.
Zauważyłem, że tutaj zostały podane globale, ale dokładnie te same uwagi miałyby zastosowanie, gdyby funkcja przyjęła jako parametr wskaźnik do licznika i blokady. Chodzi o to, że różne przypadki byłyby niebezpieczne dla wątków lub nie były ponownie wprowadzane po wywołaniu z tym samym parametrem, a nie po wywołaniu w ogóle.
Twój drugi przykład nie brzmi dla mnie ponownie. Jeśli zmiana może zostać przerwana, pozostawiając niespójny stan, a sygnał pojawia się w tym punkcie, a funkcja obsługi tego sygnału wywołuje tę funkcję, wówczas zwykle zaczyna bum. To kwestia ponownego wejścia, a nie kwestia bezpieczeństwa wątków. –
Masz rację - jak mówisz poniżej, musiałbyś również wyłączyć sygnały, aby ten ostatni był skutecznym powrotem. – ConcernedOfTunbridgeWells
@ConcernedOfTunbridgeWells, jeśli func używa stosu wewnątrz, istnieje duża szansa, że ten func nie zostanie ponownie wprowadzony. Czemu? – Alcott