Содержит ли этот код скрытую ошибку? - PullRequest
6 голосов
/ 29 октября 2011

следующий код:

  • Работает нормально при компиляции с gcc версии 4.4.5 (Ubuntu / Linaro 4.4.4-14ubuntu5 / 32bits)
  • Работает нормально при компиляции с MSVC10 (Win7 / 32bit)
  • Сбои при работе с gcc версии 4.5.2 (MinGW на Win7 / 32bit)

main.cpp :

# include <iostream>
# include <csetjmp>
# include <stdexcept>

using namespace std ;

void do_work(jmp_buf context)
{
    try
    {
        throw runtime_error("Ouch !") ;
    }
    catch(exception & e)
    {
    }

    longjmp(context, -1) ;                        //BP1
}

int main(int, char *[])
{
    jmp_buf context ;

    try
    {
        if( setjmp(context) != 0 )
        {
            throw runtime_error("Oops !") ;       //BP2
        }

        do_work(context) ;
    }
    catch(exception & e)
    {
        cout << "Caught an exception saying : " << e.what() << endl ;
    }
}

Я попытался отладить его, но программа ведет себя странно. Иногда я мог преодолеть первую точку останова (BP1), затем потерпеть крах в BP2, а иногда управление никогда не достигнет BP1, как если бы программа застряла в бесконечном цикле. Я не могу сказать больше с моими навыками отладки.

Этот код - минимальный, который я мог получить, который демонстрирует странное поведение с MinGW 4.5. Я также заметил, что:

  • Если я заменю вызов функции do_work ее содержимым, программа будет работать нормально.
  • Если я удаляю блок try{ ... } catch(...){ } внутри do_work, программа работает нормально.
  • Флаги оптимизации не действуют (всегда сбой).

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

Мой вопрос:

  • Это неисправный / ошибочный / ошибочный код? Или MinGW 4.5 неправильно обрабатывает код? (Винить инструмент довольно грубо и самонадеянно, но я подозреваю, что в нем есть некоторые настройки).

Спасибо за любой совет.

Пожалуйста, при необходимости измените метку.

Ответы [ 3 ]

3 голосов
/ 29 октября 2011

Страница man longjmp (3) в Unix сообщает:

Процедуры longjmp () нельзя вызывать после того, как подпрограмма, которая вызвала процедуры setjmp (), возвращает

Я думаю, это объясняет вашу озабоченность тем, что « иногда контроль никогда не достигает BP1 ».Я не думаю, что " работает нормально " - это надежное суждение.Я предпочел бы ожидать, что он случайным образом работает нормально и, как правило, портит стек.

Есть несколько четких рекомендаций, которые следует учитывать при работе, смешивая longjmp / setjmp с исключениями C ++, чтобы избежать сбоев инеопределенное поведение:

  • Не используйте setjmp / longjmp в программах на C ++.
  • Если вы используете функции setjmp / longjmp в программе, где могут возникать исключения, вы в безопасности, пока они делаютне взаимодействует.
  • Никогда не добавляйте и не пытайтесь войти или выйти из предложения try и предложения catch.
  • Никогда не создавайте longjmp через точку инициализации автоматических объектов.
  • Никогда не продолжайте указывать точку уничтожения, автоматическуюобъекты, особенно если деструктор нетривиален.
  • Никогда не бросать из обработчика сигнала.
  • Никогда не вызывать longjmp из вложенного обработчика сигнала.
  • Поведение longjmp из местоположения X в местоположениеY остается предсказуемым и действительным до тех пор, пока исключение, генерируемое в X и захваченное в X, будет иметь тот же эффект.
  • Если вы смешиваете setjmp / longjmp с исключениями, не ожидайте переносимости.
  • Вместо этого обратитесь к соответствующим деталям в используемом вами компиляторе документации.Например, если вы используете Visual C ++, прочитайте Используйте setjmp / longjmp

В этом вопросе упоминается работа с устаревшим кодом C в программах, написанных на C ++.Во время обзора одной из библиотек Boost было интересно обсудить проблемы sjlj в библиотеке jpeg.Обсуждение было долгим, но вот сущность с рекомендуемыми вариантами .

1 голос
/ 29 октября 2011

C ++ накладывает только одно дополнительное ограничение на использование longjmp():

Если какие-либо автоматические объекты будут уничтожены сгенерированным исключением, передающим управление другой (целевой) точке в программе, тогдавызов longjmp (jbuf, val) в точке выброса, который передает управление в ту же точку (пункт назначения), имеет неопределенное поведение. (18.7)

Вы, кажется, знаете об этом, и ваша программане делает этогоЯ голосую за дефект компилятора.

0 голосов
/ 29 октября 2011

longjmp и setjmp - это функции c, вызов их с семантикой обработки исключений C ++ и семантикой уничтожения объектов является неопределенным поведением, что означает, что его реализация зависит от того, работает он или нет (тестирование показывает, стек SEH разматывать семантику ломать вещи, в зависимости от используемого типа, если ваши рабочие используют dwarf2)

...