boost::python 和回调驱动的执行

Posted

技术标签:

【中文标题】boost::python 和回调驱动的执行【英文标题】:boost::python and callback driven execution 【发布时间】:2011-12-17 15:44:51 【问题描述】:

我在 a project 上遇到问题,涉及 。

我的项目正在使用回调机制从 C++ 运行一些 python 代码。

只要导致我的回调执行的初始函数调用来自 python 解释器,一切都很好。例如:

h = CallbackHandler()

def mycallback():
    print "yeah"

h.setCallback(mycallback)

h.runCallback()

# will print yeah

唉,事情没那么简单。我的项目使用RtAudio 与音频环境进行通信。 RtAudio 的执行是回调驱动的:我给 RtAudio 一个回调函数,当我启动 RtAudio 时,每次需要计算声音时都会调用回调。

在使用 RtAudio 的回调驱动执行时,只要我的代码尝试从 C++ 运行 python 回调,就会出现段错误。

要启动回调驱动的执行,我必须调用函数 start(),它是非阻塞的。这意味着回调驱动的执行发生在另一个线程中。

然后,当从 python 调用 start() 时,我正在创建另一个线程,分别访问 python 的执行环境。从我对python的GIL的一点了解来看,这样不好。

那么,我怎样才能让这个回调驱动的线程在不破坏一切的情况下运行 python 回调呢?

抱歉,我找不到将我的代码简化为一个简短、功能齐全的问题示例的方法...the problem is located there。

编辑

在查看python documentation 之后,我添加了几行代码,这些代码应该在非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)

如果我做对了,我的回调函数正试图访问一个 NULL 指针,对吗?

编辑 2

好的,我正在发现这一切。从pthread's documentation 看来,有一个对sem_post(sem) 的调用,其中sem 指向一个信号量。但它指向 NULL。

现在,我怎样才能更准确地查明错误?

【问题讨论】:

尝试在 valgrind 下运行程序以更好地诊断问题的根源。 @John : 添加 valgrind 结果 我猜你的信号量问题是你正在使用的队列中的一个实现错误。 【参考方案1】:

我认为您违反了 GIL。我的猜测是回调代码在深入 Python 解释器之前没有获取 GIL。

也可能是所有要使用 GIL 的线程都必须先注册自己才能这样做。并且执行你的回调的线程还没有注册自己。

我会设置它,这样 C++ 回调就不会执行 Python 回调。相反,它将相关数据写入 Python 程序正在读取的套接字中。然后你的 Python 程序就可以在从套接字获取相关数据时执行回调了。

【讨论】:

以上是关于boost::python 和回调驱动的执行的主要内容,如果未能解决你的问题,请参考以下文章

通过 Boost Python 将 Python 函数转换为 C++,用作回调

深入理解Node.js基于事件驱动的回调

从 Python 将不透明数据传递给 C++ 回调

javascript异步回调

并行编程(Future)

回调函数和表驱动法编程