3

PyAudio에 대한 비동기 오디오 재생을 구현하고 있습니다. 백엔드 Portaudio는 자체 스레드를 생성하고 새로운 오디오 데이터가 필요할 때마다 C- 콜백 함수를 호출하여 비동기 재생을 구현합니다. 해당 C- 콜백 함수가 호출 될 때마다 사용자가 오디오 데이터를 제공해야하는 이전에 등록 된 Python 함수를 호출합니다.왜이 경우 PyGILState_Release (...)가 segfault입니까?

이 파이썬 호출이 파이썬이 아닌 스레드에서 발생하기 때문에 the documentation은 파이썬 호출 전에 PyGILState_Ensure()을 호출해야하고 이후에는 PyGILState_Release()을 호출해야한다고 말합니다. 그것은 대략 다음과 같습니다 PyGILState_Release(gstate)에서 세그먼테이션 폴트 (segfault)

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; 
} 

합니다. 이 콜백 함수는 매우으로 자주 호출됩니다. 몇 초에서 몇 백 번처럼 gstate은 32 비트 변수이며 때로는 1, 때로는 으로 설정됩니다. 1으로 설정하면 충돌이 발생합니다. 일반적으로 하나의 1과 2 ~ 4 개의 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

당신이 링크 한 이슈는 PyEval_ReleaseLock과 관련이 없습니다 (사용되지 않기 때문에). 귀하의 문제는 아닙니다. 디버거에서 PyGILState_Release에서 segfault가 어디에서 발생하는지 정확히 알 수 있습니까?gstate 값 1은 파이썬이 아닌 스레드에서 호출 한 것을 나타내므로 임시 스레드 상태가 실제로 파괴되어 꽤 진행될 수 있습니다. – ncoghlan

+1

또한 어떤 버전을 연결하고 있습니까? (GIL 코드가 3.2에서 상당히 바뀌었기 때문에 내가 보았던 버전이 달라졌습니다) – ncoghlan

+1

파이썬 2.7.1 OSX로 작업하고 있습니다. – bastibe

답변

4

나는 똑같은 문제가있었습니다. 모든 콜백이 발생하기 전에 메인 스레드에서 PyEval_InitThreads()으로 호출하여 수정되었습니다.

나는 그 이유가 다음과 같다고 생각합니다. 파이썬 인터프리터가 처음 시작될 때 대부분의 파이썬 프로그램이 단일 스레드이고 GIL의 존재로 인해 약간의 성능 저하가 발생하기 때문에 GIL 초기화를 피할 수 있습니다. 따라서 GIL을 초기화하지 않고 PyGILState_Ensure()PyGILState_Release()은 초기화되지 않은 데이터에서 작동하므로 모든 곳에서 이상한 충돌이 발생합니다.

PyEval_InitThreads()을 호출하면 GIL이 초기화되고 PyGILState_Ensure()PyGILState_Release()이 올바르게 작동합니다. GIL이 이미 초기화 된 경우 PyEval_InitThreads()은 아무 작업도 수행하지 않으므로 계속해서 반복해서 호출 할 수 있습니다.

3

여러 개의 스레드가 PyGILState_Ensure()을 동시에 실행하려고 시도했을 때 세그멘테이션 오류가 발생한 유일한 시간이라는 점에 유의할 지 모르지만 어제와 매우 유사한 점을 발견했습니다.

제 경우에는 인터프리터 (메인 스레드에서)를 초기화 할 때 PyEval_InitThreads()을 호출하지 않아서 발생한 것입니다. 초기화 후 PyEval_ReleaseLock()을 실행해야합니다. 그렇지 않으면 이 암시 적으로 GIL을 가져 오기 때문에 다음에 PyGILState_Ensure()을 호출하면 교착 상태가됩니다.

희망이 도움이됩니다.

관련 문제