Boost :: Python и выполнение с помощью обратного вызова - PullRequest
0 голосов
/ 17 декабря 2011

У меня проблемы с проектом , который включает в себя boost :: python и выполнение, управляемое обратным вызовом.

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

Пока исходный вызов функции, вызывающий выполнение моего обратного вызова, поступает из интерпретатора python, все в порядке. Например:

h = CallbackHandler()

def mycallback():
    print "yeah"

h.setCallback(mycallback)

h.runCallback()

# will print yeah

Увы, не все так просто. Мой проект использует RtAudio для связи со звуковым окружением. Выполнение RtAudio управляется обратным вызовом: я даю RtAudio функцию обратного вызова, и когда я запускаю RtAudio, обратный вызов вызывается каждый раз, когда необходимо вычислить звук.

При использовании выполнения RtAudio на основе обратного вызова, я получаю segfault, как только мой код пытается запустить обратный вызов Python из C ++.

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

Затем при вызове start () из python я создаю другой поток, отдельно обращающийся к среде исполнения python. Из моего небольшого понимания GIL Python, это не хорошо.

Итак, как я могу заставить этот поток, управляемый обратным вызовом, запускать обратные вызовы Python без разбивки всего?

Извините, я не смог найти способ упростить мой код до краткого, полнофункционального примера моей проблемы ... проблема находится там .

EDIT

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

gstate = PyGILState_Ensure();
while (!queue.empty() && queue.top()->next <= now ) {
    queue.top()->run();
    queue.pop();
}
PyGILState_Release(gstate);

Но я все еще получаю сегфо. Итак, я запустил его через valgrind, и вот что я получаю (за исключением странных вещей, которые valgrind всегда получает от интерпретатора python, что является «нормальным»):

==31836== Thread 2:
==31836== Invalid read of size 4
==31836==    at 0x41F0DD5: sem_post@@GLIBC_2.1 (in /lib/libpthread-2.14.1.so)
==31836==    by 0x5ED294C: callback(void*, void*, unsigned int, double, unsigned int, void*) (in /home/tom/Code/pyck/pyck/libcore.so)
==31836==    by 0x5F6B16B: RtApiAlsa::callbackEvent() (in /usr/lib/librtaudio.so)
==31836==    by 0x5F6BBCC: alsaCallbackHandler (in /usr/lib/librtaudio.so)
==31836==    by 0x42D286D: clone (in /lib/libc-2.14.1.so)
==31836==  Address 0x0 is not stack'd, malloc'd or (recently) free'd
==31836== 
==31836== 
==31836== Process terminating with default action of signal 11 (SIGSEGV)
==31836==  Access not within mapped region at address 0x0
==31836==    at 0x41F0DD5: sem_post@@GLIBC_2.1 (in /lib/libpthread-2.14.1.so)
==31836==    by 0x5ED294C: callback(void*, void*, unsigned int, double, unsigned int, void*) (in /home/tom/Code/pyck/pyck/libcore.so)
==31836==    by 0x5F6B16B: RtApiAlsa::callbackEvent() (in /usr/lib/librtaudio.so)
==31836==    by 0x5F6BBCC: alsaCallbackHandler (in /usr/lib/librtaudio.so)
==31836==    by 0x42D286D: clone (in /lib/libc-2.14.1.so)
==31836==  If you believe this happened as a result of a stack
==31836==  overflow in your program's main thread (unlikely but
==31836==  possible), you can try to increase the size of the
==31836==  main thread stack using the --main-stacksize= flag.
==31836==  The main thread stack size used in this run was 8388608.
==31836== 
==31836== HEAP SUMMARY:
==31836==     in use at exit: 2,313,541 bytes in 2,430 blocks
==31836==   total heap usage: 22,140 allocs, 19,710 frees, 22,007,627 bytes allocated
==31836== 
==31836== LEAK SUMMARY:
==31836==    definitely lost: 522 bytes in 5 blocks
==31836==    indirectly lost: 0 bytes in 0 blocks
==31836==      possibly lost: 66,669 bytes in 1,347 blocks
==31836==    still reachable: 2,246,350 bytes in 1,078 blocks
==31836==         suppressed: 0 bytes in 0 blocks
==31836== Rerun with --leak-check=full to see details of leaked memory
==31836== 
==31836== For counts of detected and suppressed errors, rerun with: -v
==31836== Use --track-origins=yes to see where uninitialised values come from
==31836== ERROR SUMMARY: 2703 errors from 196 contexts (suppressed: 83 from 13)

Если я правильно понял, моя функция обратного вызова пытается получить доступ к пустому указателю, верно?

РЕДАКТИРОВАТЬ 2

Хорошо, я открываю все это. Из документации pthread похоже, что есть вызов sem_post(sem), где sem указывает на семафор. Но там он указывает на NULL.

Теперь, как я могу более точно определить ошибку?

1 Ответ

0 голосов
/ 18 декабря 2011

Я полагаю, что вы столкнулись с GIL. Я предполагаю, что код обратного вызова не получает GIL перед погружением в интерпретатор Python.

Возможно также, что все потоки, которые собираются использовать GIL, должны зарегистрироваться, прежде чем они смогут это сделать. И поток, выполняющий ваш обратный вызов, не зарегистрировался.

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

...