Python PyGILState_ {Ensure / Release} вызывает segfault при возврате в C ++ из кода Python - PullRequest
11 голосов
/ 01 февраля 2011

ОБНОВЛЕНИЕ Что ж, похоже, что добавление PyEval_InitThreads () до того, как вызов PyGILState_Ensure () сделает свое дело. В спешке, чтобы разобраться, я неправильно приписал свое «зависание» PyEval_InitThreads ().

Однако, после прочтения некоторой документации по Python, я задаюсь вопросом, является ли это правильным решением.

Не безопасно вызывать эту функцию, когда неизвестно, какой поток (если есть) имеет глобальную блокировку интерпретатора.


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

http://gnuradio.org/redmine/issues/show/199

Теперь usrp_spectrum_sense.py, который упоминается в отчете об ошибке, вызывает gr_bin_statistics_f (C ++), который затем периодически перезванивает Python для перенастройки USRP (радио).

Вот что происходит, когда вызывается код Python:

PyGILState_STATE d_gstate;
d_gstate = PyGILState_Ensure();

// call python code

PyGILState_Release(d_gstate);

Итак, когда мы возвращаемся из кода Python, возникает ошибка сегментации при вызове PyGILState_Release (d_gstate). Хотя между моим кодом и исходным gr_bin_statistics_f есть различия, похоже, что с этим ничего не связано.

Я прочитал, что вызов PyEval_InitThreads () перед PyGILState_Ensure () решил проблему для некоторых людей, но это просто приводит к зависанию моей программы.

Может ли кто-нибудь пролить свет на это для меня? Или просто пришло время отправить сообщение в список рассылки GNU Radio?

Использование Python2.7 в Fedora 14 x86_64.

Вот обратная трассировка GDB:


(gdb) c
Continuing.
[New Thread 0x7fabd3a8d700 (LWP 23969)]
[New Thread 0x7fabd328c700 (LWP 23970)]
[New Thread 0x7fabd2a8b700 (LWP 23971)]
[New Thread 0x7fabd228a700 (LWP 23972)]
[New Thread 0x7fabd1a89700 (LWP 23973)]
[New Thread 0x7fabd1288700 (LWP 23974)]
[New Thread 0x7fabd0a87700 (LWP 23975)]
[New Thread 0x7fabbbfff700 (LWP 23976)]

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7fabbbfff700 (LWP 23976)]
0x00000036b3e0db00 in sem_post () from /lib64/libpthread.so.0
(gdb) bt
#0  0x00000036b3e0db00 in sem_post () from /lib64/libpthread.so.0
#1  0x00000036c1317679 in PyThread_release_lock () from /usr/lib64/libpython2.7.so.1.0
#2  0x00007fabd6159c1f in ~ensure_py_gil_state (this=0x2dc6fc0, x=887000000)
    at gnuradio_swig_py_general.cc:5593
#3  gr_py_feval_dd::calleval (this=0x2dc6fc0, x=887000000) at gnuradio_swig_py_general.cc:5605
#4  0x00007fabd77c4b6e in gr_noise_level_f::tune_window (this=0x2db3ca0, 
    target_freq=) at gr_noise_level_f.cc:97
#5  0x00007fabd77c554b in gr_noise_level_f::work (this=0x2db3ca0, noutput_items=7, 
    input_items=, output_items=)
    at gr_noise_level_f.cc:115
#6  0x00007fabd7860714 in gr_sync_block::general_work (this=0x2db3ca0, 
    noutput_items=, ninput_items=, 
    input_items=, output_items=) at gr_sync_block.cc:64
#7  0x00007fabd7846ce4 in gr_block_executor::run_one_iteration (this=0x7fabbbffed90)
    at gr_block_executor.cc:299
#8  0x00007fabd7864332 in gr_tpb_thread_body::gr_tpb_thread_body (this=0x7fabbbffed90, block=...)
    at gr_tpb_thread_body.cc:49
#9  0x00007fabd785cce7 in operator() (function_obj_ptr=...) at gr_scheduler_tpb.cc:42
#10 operator() (function_obj_ptr=...)
    at /home/tja/Research/energy/detector/gnuradio-3.3.0/gruel/src/include/gruel/thread_body_wrapper.h:49
#11 boost::detail::function::void_function_obj_invoker0, void>::invoke (function_obj_ptr=...) at /usr/include/boost/function/function_template.hpp:153
---Type  to continue, or q  to quit---
#12 0x00007fabd74914ef in operator() (this=)
    at /usr/include/boost/function/function_template.hpp:1013
#13 boost::detail::thread_data >::run (this=)
    at /usr/include/boost/thread/detail/thread.hpp:61
#14 0x00007fabd725ca55 in thread_proxy () from /usr/lib64/libboost_thread-mt.so.1.44.0
#15 0x00000036b3e06d5b in start_thread () from /lib64/libpthread.so.0
#16 0x00000036b3ae4a7d in clone () from /lib64/libc.so.6
(gdb) 

Спасибо за внимание!

Ответы [ 2 ]

13 голосов
/ 02 февраля 2011

Python ожидает, что основной поток выполнит определенную инициализацию до того, как что-либо попытается вызвать из подпотока.

Если основной поток является приложением, которое встраивает Python, он должен вызватьPyEval_InitThreads() сразу после вызова Py_Initialize().

Если основным потоком является сам интерпретатор Python (как здесь, кажется, имеет место), то модуль, использующий многопоточный модуль расширения, должен включать «импорт потоков»"рано, чтобы убедиться, что PyEval_InitThreads() вызывается правильно до появления каких-либо подпотоков.

8 голосов
/ 12 февраля 2011

Я тоже столкнулся с этой проблемой. Документация для всего, что связано с потоками в CPython, к сожалению, в лучшем случае неоднородна.

По сути, вам нужно сделать следующее:

В вашем основном потоке, ДО любые другие потоки создаются, вам нужно вызвать PyEval_InitThreads(). Хорошее место для этого - сразу после того, как вы позвоните PyInitialize().

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

Таким образом, в основном в вашей основной теме перед запуском любых других потоков вы хотите сказать:

PyInitialize();
PyEval_InitThreads();
PyEval_ReleaseLock();

Затем, в любом дополнительном потоке, каждый раз, когда вы используете API Python, вы должны сказать:

PyGILState_STATE gstate;
gstate = PyGILState_Ensure();

/* ... some code that does things with Python ... */

PyGILState_Release(gstate); 
...