【问题标题】:SystemError: Objects/cellobject.c:24: bad argument to internal functionSystemError: Objects/cellobject.c:24: bad argument to internal function
【发布时间】:2014-06-17 17:29:32
【问题描述】:

我正在使用 ctypes 来处理一个用 C 编写的库。这个 C 库允许我注册一个回调函数,这是我在 Python 中实现的。

这里是回调函数类型,根据 ctypes API:

_command_callback = CFUNCTYPE(
    UNCHECKED(c_int),
    POINTER(vedis_context),
    c_int,
    POINTER(POINTER(vedis_value)))

这是我为将函数标记为回调而编写的装饰器:

def wrap_callback(fn):
    return _command_callback(fn)

要使用它,我可以简单地写:

@wrap_callback
def my_callback(*args):
    print args
    return 1  # Needed by C library to indicate OK response.

c_library_func.register_callback(my_callback)

我现在可以从 C 调用我的回调 (my_callback),并且效果很好

我遇到的问题是我希望在这些回调中执行一些样板行为(例如返回成功标志等)。为了尽量减少样板,我尝试编写一个装饰器:

def wrap_callback(fn):
    def inner(*args, **kwargs):
        return fn(*args, **kwargs)
    return _command_callback(inner)

请注意,这在功能上等同于前面的示例。

@wrap_callback
def my_callback(*args):
    print args
    return 1

但是,当我尝试使用此方法调用回调时,我收到以下异常,源自 _ctypes/callbacks.c

Traceback (most recent call last):
  File "_ctypes/callbacks.c", line 314, in 'calling callback function'
  File "/home/charles/tmp/scrap/z1/src/vedis/vedis/core.py", line 28, in inner
    return fn(*args, **kwargs)
SystemError: Objects/cellobject.c:24: bad argument to internal function

我不确定这里发生了什么会导致第一个示例工作但第二个示例失败。任何人都可以对此有所了解吗?如果你能帮助我找到一种方法来装饰这些回调,那么我可以减少样板代码!

【问题讨论】:

  • inner.__closure__ 是一个带有 cell 的元组,其中包含对 fn 的引用。此单元格被加载到评估框架中。要获得cell 中的引用,操作码LOAD_DEREF 调用PyCell_Get。你得到的错误意味着PyCell_Get 是用一个不指向单元格的垃圾指针调用的。这意味着 __closure__ 元组已损坏。
  • 在将my_callback 传递给您的库后,您是否保留了对它的引用?否则,回调和 thunk 将被释放,同时引用 innerfn。内存仍然可以部分完好无损,导致 lib 最终调用回调时出现奇怪的错误。
  • 我会看看我是否可以做一些事情来确保有对 my_callback 的引用,但我认为你完全在正确的轨道上。
  • 如果我在模块范围内创建一个列表,然后将inner 附加到列表中,它会修复错误。谢谢!我现在将努力找出一种更清洁的方法来解决这个问题。

标签: python c ctypes cython cpython


【解决方案1】:

感谢 eryksyn,我得以解决此问题。修复看起来像:

def wrap_callback(fn):
    def inner(*args, **kwargs):
        return fn(*args, **kwargs)
    return _command_callback(inner), inner

def my_callback(*args):
    print args
    return 1

ctypes_cb, my_callback = wrap_callback(my_callback)

【讨论】:

  • 必须保留对ctypes_cb 的引用,但您不需要另外引用innerctypes_cb._objects 中的 CThunkObject 已经引用了 inner