Почему PyGILState_Release (…) в этом случае имеет ошибку? - PullRequest
4 голосов
/ 28 февраля 2011

Я работаю над реализацией асинхронного воспроизведения звука для PyAudio .Бэкэнд Portaudio реализует асинхронное воспроизведение, создавая собственный поток и вызывая функцию C-callback всякий раз, когда ему нужны / есть новые аудиоданные.Всякий раз, когда вызывается эта функция C-callback, я вызываю ранее зарегистрированную функцию Python, где пользователь должен предоставить аудиоданные.

Поскольку этот вызов Python происходит в потоке, не созданном Python, документация говорит, что я должен позвонить PyGILState_Ensure() до вызова Python и PyGILState_Release() после этого.Примерно это выглядит так:

int stream_callback(const void *in, void* out, unsigned long frameCount,
                    const PaStreamCallbackTimeInfo *timeInfo,
                    PaStreamCallbackFlags statusFlags, void *userData)
{
    PyGILState_STATE gstate = PyGILState_Ensure();

    /* create some python variables, as used below… */
    py_result = PyObject_CallFunctionObjArgs(py_callback,
                                             py_frameCount,
                                             py_inTime,
                                             py_curTime,
                                             py_outTime,
                                             py_inputData,
                                             NULL);
    /* evaluate py_result, do some audio stuff… */

    PyGILState_Release(gstate);
    return returnVal;
}

Который segfaults в PyGILState_Release(gstate).Эта функция обратного вызова вызывается очень часто.Мол, от нескольких сотен до нескольких тысяч раз в секунду.gstate является 32-битной переменной и иногда устанавливается на 1, иногда на 0 на PyGILState_Ensure().Вылетает, только если установлено значение 1.Обычно это один 1, за которым следуют два-четыре 0.

Такое ощущение, что PyGILState_Release(…) занимает немного больше времени, чем фактическое возвращение, и, таким образом, вызывается во время работы или что-то в этом роде.

При сбое трассировка стека выглядит следующим образомэто:

#0  0x00007fff88c287b7 in pthread_mutex_lock ()
#1  0x00000001001009a6 in PyThread_release_lock ()
#2  0x00000001002efc82 in stream_callback (in=0x1014a4670, out=0x1014a4670, frameCount=4316612208, timeInfo=0x1014a4850, statusFlags=4297757032, userData=0x38) at _portaudiomodule.c:1554
#3  0x00000001004e3710 in AdaptingOutputOnlyProcess ()
#4  0x00000001004e454b in PaUtil_EndBufferProcessing ()
#5  0x00000001004e9665 in AudioIOProc ()
#6  0x00000001013485d0 in dyld_stub_strlen ()
#7  0x0000000101348194 in dyld_stub_strlen ()
#8  0x0000000101346523 in dyld_stub_strlen ()
#9  0x0000000101345870 in dyld_stub_strlen ()
#10 0x000000010134aceb in AUGenericOutputEntry ()
#11 0x00007fff88aa132d in HP_IOProc::Call ()
#12 0x00007fff88aa10ff in IOA_Device::CallIOProcs ()
#13 0x00007fff88aa0f35 in HP_IOThread::PerformIO ()
#14 0x00007fff88a9ef44 in HP_IOThread::WorkLoop ()
#15 0x00007fff88a9e817 in HP_IOThread::ThreadEntry ()
#16 0x00007fff88a9e745 in CAPThread::Entry ()
#17 0x00007fff88c5c536 in _pthread_start ()
#18 0x00007fff88c5c3e9 in thread_start ()

Имеет ли это смысл для кого-либо?

Ответы [ 2 ]

6 голосов
/ 17 февраля 2013

У меня была точно такая же проблема. Исправление состояло в том, чтобы вызвать PyEval_InitThreads() в главном потоке, прежде чем произойдут какие-либо обратные вызовы.

Я считаю, что причина этого заключается в следующем. Когда интерпретатор Python запускается впервые, он избегает инициализации GIL, поскольку большинство программ на Python являются однопоточными, а наличие GIL влечет за собой небольшое снижение производительности. Таким образом, без инициализации GIL PyGILState_Ensure() и PyGILState_Release() работают с неинициализированными данными, вызывая странные сбои повсюду.

При вызове PyEval_InitThreads() GIL инициализируется и PyGILState_Ensure() и PyGILState_Release() работают правильно. Если GIL уже инициализирован, PyEval_InitThreads() ничего не делает, поэтому безопасно вызывать снова и снова.

4 голосов
/ 02 мая 2011

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

В моем случае это было вызвано тем, что я не вызывал PyEval_InitThreads(), когда я инициализировал интерпретатор (в основном потоке). Обратите внимание, что нужно выполнить PyEval_ReleaseLock() после инициализации, иначе вы будете в тупике при следующем вызове PyGILState_Ensure(), поскольку PyEval_InitThreads() неявно получает GIL.

Надеюсь, это поможет.

...