Явный вызов деструктора в обработчике сигнала - PullRequest
3 голосов
/ 14 ноября 2009

У меня есть деструктор, который выполняет необходимую очистку (убивает процессы). Он должен работать, даже когда SIGINT отправляется в программу. Мой код в настоящее время выглядит так:

typedef boost::shared_ptr<PidManager> PidManagerPtr
void PidManager::handler(int sig)
{
  std::cout << "Caught SIGINT\n";
  instance_.~PidManagerPtr();  //PidManager is a singleton
  exit(1);
}
//handler registered in the PidManager constructor

Это работает, но, кажется, есть многочисленные предупреждения против явного вызова деструктора. Правильно ли это делать в этой ситуации или есть «более правильный» способ сделать это?

Ответы [ 6 ]

5 голосов
/ 14 ноября 2009

Если этот объект является одиночным, вам не нужно использовать разделяемый указатель. (Там только один!)

Если вы переключите его на auto_ptr, вы можете позвонить на release(). Или, возможно, scoped_ptr, звоня reset().

Все это говорит, я на 99% уверен, что exit() разрушит статически построенные объекты. (Какие синглтоны обычно бывают.) Я знаю, что exit() вызывает зарегистрированные функции atexit().

Если ваш синглтон не уничтожается автоматически при выходе, то в вашем случае правильно сделать крюк atexit:

void release_singleton(void)
{
    //instance_.release();
    instance_.reset();
}

// in main, probably
atexit(release_singleton);
2 голосов
/ 16 ноября 2009

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

Что происходило

Для shared_ptr в обработчике использовалось значение use_count, равное двум. Одна ссылка была в самом PidManager, другая была в клиенте PidManager. Вызов деструктора shared_ptr (~ PidManager ()) уменьшил значение use_count на единицу. Затем, как намекнул GMan, при вызове метода exit () вызывался деструктор для статически инициализированного экземпляра PidManagerPtr, уменьшая значение use_count до 0 и вызывая деструктор PidManager. Очевидно, что если бы PidManager имел более одного клиента, use_count не упал бы до 0, и это не сработало бы вообще.

Это также дает некоторые подсказки относительно того, почему вызов instance_.reset () не работал. Вызов действительно уменьшает количество ссылок на 1. Но оставшаяся ссылка - это shared_ptr в клиенте PidManager. Этот shared_ptr является автоматической переменной, поэтому его деструктор не вызывается при выходе (). Деструктор instance_ вызывается, но, поскольку он был сброшен (), он больше не указывает на экземпляр PidManager.

Решение

Я полностью отказался от использования shared_ptrs и решил вместо этого пойти с Майерсом Синглтоном. Теперь мой код выглядит так:

void handler(int sig)
{
     exit(1);
}

typedef PidManager * PidManagerPtr
PidManagerPtr PidManager::instance()
{
    static PidManager instance_;
    static bool handler_registered = false;
    if(!handler_registered)
    {
        signal(SIGINT,handler);
        handler_registered = true;
    }
    return &instance_;
 }

Явный вызов exit позволяет запускать деструктор статически инициализированного экземпляра PidManager_, поэтому никакой другой код очистки не требуется помещать в обработчик. Это позволяет аккуратно избежать проблем с вызываемым обработчиком, когда PidManager находится в несовместимом состоянии.

2 голосов
/ 14 ноября 2009

Никогда явно не вызывайте деструктор, если объект не был создан с размещением new. Переместите код очистки в отдельную функцию и вызовите его. Эта же функция должна вызываться из деструктора.

1 голос
/ 14 ноября 2009

Вы действительно не хотите ничего делать в обработчике сигналов. Самое безопасное, что нужно сделать, - это просто установить флаг (например, глобальный volatile bool), а затем регулярно проверять цикл обработки событий вашей программы, чтобы помечать этот флаг очень часто, и, если он стал истинным, вызывать оттуда процедуру очистки / выключения.

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

Однако, если вам не нравится идея постоянно опрашивать логическое значение, еще одна вещь, которую вы можете сделать из обработчика сигнала (по крайней мере, в большинстве ОС), это отправить байт в сокет. Таким образом, вы могли бы заранее настроить socketpair () и иметь свой обычный цикл обработки событий select () (или любой другой) на другом конце пары сокетов; когда он получает байт в этом сокете, он знает, что ваш обработчик сигнала должен был отправить этот байт, и поэтому пришло время его очистить.

0 голосов
/ 14 ноября 2009

Просто вызовите reset () для shared_ptr, и он удалит ваш экземпляр для вас.

0 голосов
/ 14 ноября 2009

Другим способом может быть динамическое выделение синглтона (при первом использовании или в основном) и delete для очистки.

Э. Я предполагаю, что ваш PidManagerPtr фактически указывает на динамически размещенный объект ... Но разве boost :: shared_ptr не очищает при перераспределении? Так что должно быть достаточно:

instance_ = 0;

...