В Fil-C появилась memory-safe поддержка setjmp/longjmp и ucontext (семейство getcontext, setcontext, makecontext, swapcontext). ucontext доступен с версии 0.680 — чтобы им пользоваться, нужно собирать Fil-C из исходников.
Проблема этих API в том, что их легко использовать неправильно. Например, сделать setjmp внутри функции, потом вернуться из неё, а затем вызвать longjmp — получится восстановление контекста на уже несуществующий стек. Или создать контекст через makecontext, освободить переданный ему стек, а потом переключиться на него — программа упадёт или даст атакующему контроль. В обычном C (Yolo-C) такие ошибки приводят к непредсказуемым крашам.
Fil-C решает это иначе. Для setjmp/longjmp используется непрозрачный объект zjmp_buf — Fil-C-код не может залезть в его внутренности. Компилятор требует, чтобы setjmp вызывался напрямую, а не через указатель, иначе программа не скомпилируется. Это гарантирует, что компилятор знает о returns_twice и не переиспользует spill-слоты неправильно. longjmp завершается паникой, если вызывается не из стека-предка того кадра, где был setjmp. Кроме того, zjmp_buf сохраняет GC-корни кадра, чтобы сборщик мусора не потерял объекты.
С ucontext ещё сложнее. Fil-C полностью игнорирует поле ss_sp — стек для волокна выделяется внутри zfiber_context. Состояния контекста строго контролируются: например, после getcontext можно только вызвать makecontext или передать контекст как from в swapcontext. Нельзя переключиться на контекст, который не был сделан runnable. Волокно привязано к потоку — переключить его на другой поток нельзя.
Большая проблема — интеграция со сборкой мусора. Если во время фазы маркировки GC переключиться с одного волокна на другое, то исходное волокно станет runnable, но GC уже посчитал его чёрным и больше не будет сканировать. Fil-C решает это через список grey_fibers: при переключении контекста во время маркировки волокно добавляется в серый список, и поток пересканирует его при следующей подсканировке стека.
Поддержка ucontext есть только в glibc-сборке Fil-C — в /opt/fil и Pizlix, но не в pizfix. Реализация использует собственные trampoline и проверки. В целом, Fil-C доказывает, что даже такие «грязные» возможности C можно сделать memory-safe.