Как мне поймать сигнал прерывания в Python, когда внутри метода повышения блокирования c ++?

У меня есть набор инструментов, который был написан на C ++, и дал привязки Boost для Python.

Первоначально этот код был весь C ++, и я поймал CTRL+С прервать с:

signal( SIGINT, signalCallbackHandler );

а также

void signalCallbackHandler(int /*signum*/)
{
g_myObject->stop();
}

Это работало нормально.

Однако теперь я добавил привязки Python и использую Python для инициализации объектов.

Моей первоначальной мыслью было сделать это так:

import signal

def handle_interrupt( signum, frame ) :
g_myObject.stop()

signal.signal( signal.SIGINT, handle_interrupt )
g_myObject = MyObject()
g_myObject.start()

Однако этот обработчик сигнала никогда не вызывается.

Как я должен обрабатывать такое прерывание? Нужно ли делать это внутри C ++, а затем вызывать функцию Python оттуда?

2

Решение

Ваш обработчик сигналов python не вызывается, потому что python откладывает выполнение обработчиков сигналов до тех пор, пока не будет выполнена следующая инструкция байт-кода — см. документация библиотеки для сигнала, раздел 18.8.1.1:

Обработчик сигнала Python не выполняется внутри обработчика сигнала низкого уровня (C). Вместо этого низкоуровневый обработчик сигнала устанавливает флаг, который указывает виртуальной машине выполнить соответствующий обработчик сигнала Python на более позднем этапе (например, при следующей инструкции байт-кода). Это имеет последствия:

  • Нет смысла ловить синхронные ошибки, такие как SIGFPE или же SIGSEGV которые вызваны неправильной операцией в C-коде. Python вернется из обработчика сигнала к коду C, который, вероятно, снова вызовет тот же сигнал, что приведет к зависанию Python. Начиная с Python 3.3, вы можете использовать faulthandler модуль для отчета о синхронных ошибках.
  • Долгосрочные вычисления, реализованные исключительно на языке C (например, сопоставление регулярных выражений для большого объема текста), могут выполняться непрерывно в течение произвольного промежутка времени, независимо от принятых сигналов. Обработчики сигналов Python будут вызваны после завершения расчета.

Причина этого заключается в том, что сигнал может поступить в любое время, возможно, на полпути после выполнения инструкции Python. Для ВМ было бы небезопасно начинать выполнение обработчика сигнала, потому что ВМ находится в неизвестном состоянии. Следовательно, фактический обработчик сигналов, установленный python, просто устанавливает флаг, указывающий виртуальной машине вызывать обработчик сигналов после завершения текущей инструкции.

Если сигнал поступает во время выполнения вашей функции C ++, то обработчик сигнала устанавливает флаг и возвращается к вашей функции C ++.

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

Такой подход позволит вам использовать один и тот же код независимо от того, вызываете ли вы свой код из Python, C ++ или, возможно, из другой привязки.

1

Другие решения

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

Одна вещь, которую я не упомянул ранее, это то, что MyObject — это синглтон, поэтому я получаю его с MyObject.getObject()

В Python у меня есть:

def signalHandler( signum ) :
if signum == signal.SIGINT :
MyObject.getObject().stop()

def main() :
signal.signal( signal.SIGINT, handle_interrupt )

myObject = MyObject.getObject()
myObject.addSignalHandler( signal.SIGINT, signalHandler )
myObject.start()

В моем коде C ++, в области, которая ничего не должна знать о Python, я имею:

class MyObject
{
public :
void addSignalHandler( int signum, void (*handler)( int, void* ), void *data = nullptr );
void callSignalHandler( int signum );
private :
std::map<int, std::pair<void (*)( int, void* ), void*> > m_signalHandlers;
}

void signalCallbackHandler( int signum )
{
MyObject::getObject()->callSignalHandler( signum );
}

void MyObject::addSignalHandler( int signum, void (*handler)( int, void* ), void *data )
{
m_signalHandlers.insert( std::pair<int, std::pair<void (*)( int, void* ), void *> >( signum, std::make_pair( handler, data ) ) );
signal( signum, signalCallbackHandler );
}

void MyObject::callSignalHandler( int signum )
{
std::map<int, std::pair<void (*)( int, void* ), void*> >::iterator handler = m_signalHandlers.find( signum );
if( handler != m_signalHandlers.end() )
{
handler->second.first( signum, handler->second.second );
}
}

А потом в моих привязках Python:

void signalHandlerWrapper( int signum, void *data )
{
if( nullptr == data )
{
return;
}

PyObject *func = (PyObject*)( data );
if( PyFunction_Check( func ) )
{
PyObject_CallFunction( func, "i", signum );
}
}

void addSignalHandlerWrapper( MyObject *o, int signum, PyObject *func )
{
Py_INCREF( func );
if( PyFunction_Check( func ) )
{
o->addSignalHandler( signum, &signalHandlerWrapper, func );
}
}

У меня нет чего-то, что я должен добавить, что-то в addSignalHandlerWrapper (), которое проверит, было ли уже что-то для этого номера сигнала, и если да, то получит его и уменьшит ссылку перед добавлением нового. Я еще не сделал этого, так как эта функциональность используется только для завершения программы, но для полноты ее следует использовать.

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

0

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector