Каковы хорошие способы использования longjmp/setjmp для обработки ошибок C?

Мне нужно использовать C для одного проекта, и я думаю об использовании longjmp/setjmp для обработки ошибок, так как я думаю, что будет намного проще обрабатывать ошибки в одном месте, чем коды возврата. Я был бы признателен, если бы есть некоторые руководства о том, как это сделать.

Меня особенно беспокоит правильность очистки ресурсов в случае возникновения такой ошибки.

Кроме того, как мне обрабатывать ошибки, которые приводят к их использованию многопоточными программами?

Еще лучше, есть ли какая-то библиотека C, которая уже существует для обработки ошибок/исключений?


person dubnde    schedule 04.05.2009    source источник


Ответы (6)


Взгляните на этот пример/руководство:
http://www.di.unipi.it/~nids/docs/longjump_try_trow_catch.html

person eaanon01    schedule 04.05.2009
comment
с этой реализацией, как можно обрабатывать THROW из вызовов функций? ex_buf__ является локальным в функции, которая использует блок TRY-CATCH, никакая другая функция не видит эту переменную. Я также хочу избегать глобальных переменных из-за безопасности потоков. - person torbatamas; 11.01.2021
comment
Переменные @torbatamas thread_local могут вам помочь. - person Giovanni Cerretani; 22.07.2021

Если вы беспокоитесь об очистке ресурсов, вы должны серьезно задуматься о том, являются ли longjmp() и setjmp() хорошей идеей.

Если вы проектируете свою систему распределения ресурсов таким образом, чтобы на самом деле ее можно было аккуратно очистить, тогда все в порядке, но такой дизайн имеет тенденцию быть сложным и, как правило, неполным, если на самом деле стандартные библиотеки, которые использует ваш код, сами выделяют ресурсы, которые должны быть выпущеным. Он требует чрезвычайной осторожности, и, поскольку он не совсем надежен, он не подходит для долго работающих систем, которым, возможно, придется пережить многократное использование вызовов setjmp()/longjmp() (они будут просачиваться, расширяться и, в конечном итоге, вызывать проблемы).

person Jonathan Leffler    schedule 04.05.2009

Symbian реализовал свой механизм Leave с точки зрения longjmp(), и это служит хорошим обзором всего, что вам нужно сделать.

Symbian имеет глобальный «стек очистки», который вы нажимаете и извлекаете из того, что хотите очистить, если произошел скачок. Это ручная альтернатива автоматической раскрутке стека, которую делает компилятор C++ при возникновении исключения C++.

У Symbian были «ловушки-ловушки», в которые она могла прыгнуть; они могут быть вложенными.

(Недавно Symbian переосуществила его с точки зрения исключений C++, но интерфейс остался без изменений).

В целом, я думаю, что правильные исключения C++ менее подвержены ошибкам кодирования и намного быстрее, чем ваш собственный эквивалент C.

(Современные компиляторы C++ очень хороши в исключениях с «нулевыми издержками», когда они, например, не выбрасываются; longjmp() должен хранить состояние всех регистров и т. д., даже если прыжок не выполняется позже, поэтому принципиально никогда не может быть таким быстрым как исключения.)

Использование C++ в качестве лучшего C, где вы принимаете только исключения и RAII, было бы хорошим путем, если бы использование longjmp() для эмуляции исключений вызывало у вас искушение.

person Will    schedule 04.05.2009
comment
Пожалуйста, используйте правильное имя: longjmp / setjmp, а не longjump. - person Adriano Varoli Piazza; 04.05.2009
comment
Спасибо за информацию. Однако вопрос специфичен для использования C, а не C++ - person dubnde; 04.05.2009
comment
@MeThinks, все в порядке, мой совет применим к C; Я описал симуляцию раскручивания стека (например, стека очистки Symbian) как то, как вы выполняете очистку ресурсов, которую вы указали в своем вопросе. - person Will; 04.05.2009

Я нашел только одно применение для setjmp()/longjmp(), и это не было связано с обработкой ошибок.

На самом деле нет необходимости использовать его для этого, поскольку его всегда можно реорганизовать во что-то более простое для понимания. Использование setjmp()/longjmp() очень похоже на goto в том смысле, что им легко злоупотреблять. Все, что делает ваш код менее читаемым, в целом является плохой идеей. Обратите внимание: я не говорю, что они плохи по своей сути, просто они могут привести к плохому коду легче, чем альтернативы.

FWIW, единственное место, где они были бесценны, - это проект, который я делал на заре индустрии (временные рамки MS-DOS 6). Мне удалось собрать совместную многопоточную библиотеку с использованием Turbo C, которая использовала эти функции в функции yield() для переключения задач.

Я почти уверен, что не прикасался к ним (или не имел необходимости) с тех пор.

person paxdiablo    schedule 04.05.2009
comment
+1 Я тоже использовал их только один раз: в защите от копирования для DOS-игры. Я использовал longjmp вместо обычного ветвления, чтобы запутать взломщиков. Это отчасти сработало, потому что сгенерированный код выглядит совсем не как ветвь. :-) - person Nils Pipenbrinck; 04.05.2009
comment
Забавное совпадение: я построил кооперативный нитевдеватель ок. 1985, используя setjmp()/longjmp() для работы на рабочей станции SGI под их Unix-вариантом дня. Работал как шарм. Поскольку он использовался для анимации, он включал рандеву, который синхронизировал все потоки с вертикальным прерыванием видеосистемы. Однако с тех пор я не использовал их напрямую. - person RBerteig; 05.05.2009

Исключения — гораздо лучший общий механизм, но в темные дни прошлого C я написал эмулятор процессора, который включал командную оболочку. Оболочка, используемая для установки jmp/longjmp для обработки прерывания (т. е. процессор работает, и пользователь нажимает break/ctrl-c, код перехватывает SIGINT и longjmp обратно в оболочку).

person plinth    schedule 04.05.2009

Я использовал setjmp/longjmp достаточно аккуратно, чтобы выйти из обратного вызова, не прокладывая путь вверх по другим уровням библиотеки.

Этот случай (если я правильно помню) был, когда код внутри синтаксического анализатора, сгенерированного yacc, мог обнаружить (несинтаксическую) проблему и хотел отказаться от синтаксического анализа, но предоставить достаточно полезный отчет об ошибке обратно вызывающей стороне на другой стороне весь сгенерированный yacc код. Другой пример был внутри обратного вызова, вызванного парсером Expat. В каждом случае были и другие способы сделать это, но они казались более громоздкими и неясными, чем просто спасение таким образом.

Однако, как указывалось в других ответах, необходимо быть осторожным с очисткой и очень внимательно следить за тем, чтобы код longjmp вызывался только в пределах области, динамически защищенной setjmp.

Делаете это в контексте многопоточного программирования? Я уверен, что это не невозможно, но О-о-о, доставайте свою семейную пачку аспирина сейчас же. Вероятно, разумно держать пары setjmp/longjmp как можно ближе друг к другу. Пока совпадающая пара setjmp/longjmp находится в одном потоке, я ожидаю, что все будет в порядке, но... будьте осторожны.

person Norman Gray    schedule 12.08.2014