setjmp(3)

NAZWA
setjmp, sigsetjmp, longjmp, siglongjmp - przeprowadza nielokalne goto

BIBLIOTEKA
Standardowa biblioteka C (libc, -lc)

SKŁADNIA
#include <setjmp.h>

int setjmp(jmp_buf env);
int sigsetjmp(sigjmp_buf env, int savesigs);

[[noreturn]] void longjmp(jmp_buf env, int val);
[[noreturn]] void siglongjmp(sigjmp_buf env, int val);

    Wymagane ustawienia makr biblioteki glibc (patrz feature_test_macros(7)):

setjmp(): patrz HISTORIA.

sigsetjmp():
    _POSIX_C_SOURCE

OPIS
Funkcje opisane w niniejszym podręczniku służą do wykonywania „nielokalnych goto”: przeniesienia wykonania z jednej funkcji, do określonego wcześniej położenia w innej funkcji. Funkcja setjmp() dynamicznie określa cel, do którego zostanie następnie przeniesiona kontrola, a longjmp() dokonuje przeniesienia wykonania.

Funkcja setjmp() zapisuje różne informacje o środowisku wywołującego (zwykle wskaźnik stosu, wskaźnik instrukcji, ewentualnie wartości innych rejestrów oraz maskę sygnałów) w buforze env, do późniejszego użytku przez longjmp(). W tym przypadku, setjmp() zwraca 0.

Funkcja longjmp() używa informacji zapisanych w env do ponownego przeniesienia kontroli do miejsca, z którego wywołano setjmp() i przywrócenia („przewinięcia wstecz”) stosu, do jego stanu w momencie wywołania setjmp(). Dodatkowo, w zależności od implementacji (zob. UWAGI i HISTORIA), wartości niektórych innych rejestrów i maska sygnałów procesu mogą być przywrócone do ich stanu z chwili wywołania setjmp().

Po pomyślnym longjmp(), wykonanie kontynuuje, jak gdyby setjmp() powróciło drugi raz. Ten „udawany” powrót można rozróżnić od prawdziwego wywołania setjmp(), ponieważ „udawany” powrót zwraca wartość przekazaną w val. Jeśli programista omyłkowo poda w val wartość 0, to „udawany” powrót zwróci w zamian 1.

  sigsetjmp() i siglongjmp()
sigsetjmp() i siglongjmp() również wykonują nielokalne goto, lecz zapewniają przewidywalną obsługę maski sygnałów procesu.

Wtedy, i tylko wtedy, gdy argument savesigs przekazany do sigsetjmp jest niezerowy, bieżąca maska sygnałów procesu jest zachowywana w env i zostanie odtworzona przez późniejsze wykonanie siglongjmp() z tym samym env.

WARTOŚĆ ZWRACANA
Przy bezpośrednim wywołaniu, setjmp() i sigsetjmp() zwracają 0; przy „udawanym” powrocie, który następuje po longjmp() lub siglongjmp(), zwracana jest wartość niezerowa przekazana w val.

Funkcje longjmp() i siglongjmp() nie powracają.

ATRYBUTY
Informacje o pojęciach używanych w tym rozdziale można znaleźć w podręczniku attributes(7).
Interfejs | Atrybut | Wartość
setjmp(), sigsetjmp() | Bezpieczeństwo wątkowe | MT-bezpieczne
longjmp(), siglongjmp() | Bezpieczeństwo wątkowe | MT-bezpieczne

STANDARDY

setjmp()
longjmp()
    C11, POSIX.1-2008.

sigsetjmp()
siglongjmp()
    POSIX.1-2008.

HISTORIA

setjmp()
longjmp()
    POSIX.1-2001, C89.

sigsetjmp()
siglongjmp()
    POSIX.1-2001.

POSIX nie określa, czy setjmp() zapisze maskę sygnałów (do późniejszego jej przywrócenia w trakcie longjmp()). W Systemie V nie ma to miejsca. W 4.3BSD tak się dzieje, występuje również funkcja _setjmp(), która tego nie robi. Zachowanie w Linuksie zależy od wersji glibc i ustawienia makra sprawdzania cech. Przed glibc 2.19, setjmp() naśladuje domyślnie zachowanie Systemu V, a zachowanie BSD jest zapewniane, jeśli makro sprawdzania cech _BSD_SOURCE jest jawnie zdefiniowane, a żadne z: _POSIX_SOURCE, _POSIX_C_SOURCE, _XOPEN_SOURCE, _GNU_SOURCE, ani _SVID_SOURCE nie jest zdefiniowane. Od glibc 2.19, <setjmp.h> ujawnia jedynie wersję Systemu V setjmp(). Programy potrzebujące semantyki BSD powinny zastąpić wywołania do setjmp(), wywołaniami sigsetjmp() z niezerwoym argumentem savesigs.

UWAGI
setjmp() i longjmp() mogą być przydatne do radzenia sobie z błędami w głęboko zagnieżdżonych wywołaniach funkcji albo do umożliwienia procedurze obsługi sygnału do przekazania kontroli do określonego punktu programu, zamiast do powracania do punktu, gdzie procedura obsługi przerwała główny program. W tym drugim przypadku, jeśli chce się zachować i przywrócić maski sygnałów w sposób przenośny, należy korzystać z sigsetjmp() i siglongjmp(). Zob. też opis na temat czytelności programu poniżej.

ZASTRZEŻENIA
Kompilator może dokonać optymalizacji zmiennych do rejestrów, a longjmp() może przywrócić wartości innych rejestrów oprócz wskaźnika stosu i licznika programu. Co za tym idzie, wartości automatycznych zmiennych są nieokreślone po wywołaniu longjmp(), jeśli zostaną spełnione wszystkie poniższe kryteria:

•   są lokalne w stosunku do funkcji, która wykonała odpowiednie wywołanie setjmp();

•   ich wartości są zmienione pomiędzy wywołaniami do setjmp() i longjmp(); i

•   nie są zadeklarowane jako volatile (ulotne).

Te same uwagi stosują się do siglongjmp().

  Nielokalne goto i czytelność programu
Choć może być nadużywane, tradycyjne wyrażenie „goto” języka C korzysta przynajmniej ze wskazówek leksykalnych (wyrażenie goto i docelowa etykieta), co pozwala programiście łatwo wyczuć przepływ kontroli. Nielokalne goto nie zapewniają takich wskazówek, tej samej zmiennej jmp_buf może używać wiele wywołań setjmp(), tak więc zawartość zmiennej może się zmieniać w trakcie istnienia aplikacji. Co za tym idzie, programista może być zmuszony do szczegółowej analizy kodu, aby określić dynamiczny cel danego wywołania longjmp() (aby ułatwić życie programiście, każde wywołanie setjmp() powinno mieć unikatową zmienną jmp_buf).

Dodatkową trudność stanowi fakt, że wywołania setjmp() i longjmp() nie muszą być nawet w tym samym module kodu źródłowego.

Podsumowując, nielokalne goto powodują, że programy są trudne do zrozumienia i utrzymywania, zatem jeśli jest to możliwe, należy używać innych rozwiązań.

  Niezdefiniowane zachowanie
Jeśli funkcja, która wywołała setjmp() powróci przed wywołaniem longjmp(), zachodzi niezdefiniowane zachowanie. Na pewno wystąpi, mniejszy lub większy, chaos.

Jeśli w programie wielowątkowym, wywołanie longjmp() użyje bufora env, który był zainicjowany wywołaniem setjmp() w innym wątku, zachowanie jest niezdefiniowanie.

POSIX.1-2008 Technical Corrigendum 2 dodaje longjmp() i siglongjmp() do listy funkcji async-signal-safe. Jednak standard zaleca unikania korzystania z tych funkcji z procedur obsługi sygnału wskazując, że gdy funkcje te są wywoływane z procedury obsługi sygnału, który przerwał wywołanie do funkcji non-async-signal-safe (lub odpowiednika, np. ekwiwalentne kroki do exit(3), które zachodzą przy powrocie z pierwotnego wywołania do main()), zachowanie jest niezdefiniowane, gdy program wykona następnie wywołanie do funkcji non-async-signal-safe. Jedynym sposobem uniknięcia niezdefiniowanego zachowania jest zapewnienie jednego z poniższych:

•   Po długim skoku z pierwotnej procedury obsługi sygnału, program nie wywołuje żadnych funkcji non-async-signal-safe i nie powraca z pierwotnego wywołania do main().

•   Wszelkie sygnały, których procedury obsługi wykonują długi skok, muszą być blokowane w trakcie każdego wywołania do funkcji non-async-signal-safe i żadna funkcja non-async-signal-safe nie może być wywołana po powrocie z pierwotnego wywołania do main().

ZOBACZ TAKŻE
signal(7), signal-safety(7)

TŁUMACZENIE
Tłumaczenie niniejszej strony podręcznika: Przemek Borys <pborys@dione.ids.pl>, Andrzej Krzysztofowicz <ankry@green.mf.pg.gda.pl> i Michał Kułach <michal.kulach@gmail.com>

Niniejsze tłumaczenie jest wolną dokumentacją. Bliższe informacje o warunkach licencji można uzyskać zapoznając się z GNU General Public License w wersji 3 (https://www.gnu.org/licenses/gpl-3.0.html) lub nowszej. Nie przyjmuje się ŻADNEJ ODPOWIEDZIALNOŚCI.

Błędy w tłumaczeniu strony podręcznika prosimy zgłaszać na adres listy dyskusyjnej manpages-pl-list@lists.sourceforge.net.