Cython memoryview error - Fatal Python error: Acquisition count is

Cython memoryview error - Fatal Python error: Acquisition count is

我在使用 Cython 时遇到了一个我发现很难解决的错误。 我有一个结构 ret_val,它有一个名为 last_visited 的 long[:] 字段。 我正在尝试设置它但出现以下运行时错误:

Fatal Python error: Acquisition count is -1753032536 (line 5052)

以下是上面一行负责的 C 文件的摘录:

/* "cymain.pyx":196
*     last = np.array([1,1,1], dtype=np.int64)
*     ret_val.last_visited = last             # <<<<<<<<<<<<<<
*/

__pyx_t_9 = __Pyx_PyObject_to_MemoryviewSlice_ds_long(__pyx_v_last);

if (unlikely(!__pyx_t_9.memview)) {
    __pyx_filename = __pyx_f[0]; 
    __pyx_lineno = 196; 
    __pyx_clineno = __LINE__; 
    goto __pyx_L1_error;
}
__PYX_XDEC_MEMVIEW(&__pyx_v_ret_val->last_visited, 0);
__pyx_v_ret_val->last_visited = __pyx_t_9;
__pyx_t_9.memview = NULL;
__pyx_t_9.data = NULL;

我试着做了一个最小的例子来重现错误,但后来并没有发生。然后我用最小的例子重写了这个函数,又失败了。

这是一个不会产生错误的最小示例,但据我所知,它在功能上与导致错误的代码相同:

cdef struct baz:
    long[:] lv
    othermodule.something* cd

cdef baz* initialise_baz(dict req):
    cdef:
        baz* ret_val = <baz *> malloc(sizeof(baz))
        long nlevels = 3

    ret_val.cd = NULL

    lv = req["key"]
    lv = np.array(lv, dtype=np.int64)
    ret_val.lv = lv

    return ret_val

def test_memview_error(req):
    cdef baz* foo
    foo = initialise_baz(req)
    print "foo.lv[2]", foo.lv[2]

然后调用

import cymodule
cymodule.test_memview_error({"key":np.array([1,2,3])})

我认为问题与未初始化的内存有关(正如我在评论中所说)。从您的简单示例中查看生成的 C 代码:

/* "code.pyx":5
 * import numpy as np
 * 
 * cdef struct baz:             # <<<<<<<<<<<<<<
 *     long[:] lv
 *     #othermodule.something* cd
 */
struct __pyx_t_4code_baz {
  __Pyx_memviewslice lv;
};

(请注意,为简单起见,我已注释掉 othermodule.something)。 __Pyx_memviewslice 定义为

typedef struct {
  struct __pyx_memoryview_obj *memview;
  char *data;
  Py_ssize_t shape[8];
  Py_ssize_t strides[8];
  Py_ssize_t suboffsets[8];
} __Pyx_memviewslice;

来自 initialise_baz 的一些相关代码(我在这里跳过了一些小部分)

__pyx_v_ret_val = ((struct __pyx_t_4code_baz *)malloc((sizeof(struct __pyx_t_4code_baz))));

请注意 malloc 不会(必然)将内存归零。因此 lv 的内容(至关重要的是指向 memview 的指针)被设置为任意值(可能是之前内存中的内容 - 这显然取决于您之前的其他代码 运行 ) .如果您使用 calloc 而不是 malloc 它会将内存归零。 initialise_baz 继续:

  /* "code.pyx":18
 *     lv = req["key"]
 *     lv = np.array(lv, dtype=np.int64)
 *     ret_val.lv = lv             # <<<<<<<<<<<<<<
 * 
 *     return ret_val
 */
  __pyx_t_6 = __Pyx_PyObject_to_MemoryviewSlice_ds_long(__pyx_v_lv);
  if (unlikely(!__pyx_t_6.memview)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 18; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __PYX_XDEC_MEMVIEW(&__pyx_v_ret_val->lv, 0);
  __pyx_v_ret_val->lv = __pyx_t_6;
  __pyx_t_6.memview = NULL;
  __pyx_t_6.data = NULL;

关键行是 __PYX_XDEC_MEMVIEWlv 的先前(任意!)内容上调用。这就是问题所在。 lv.memview 指向任意位置,它从中读取它认为是 acquisition_count.

的内容

表面的修复是使用 calloc 而不是 malloc。但是,即使在那种情况下,当您释放 baz 时,lv 也永远不会被正确释放,这可能会导致内存泄漏。我 真的 认为将内存视图用作 C 结构的一部分没有意义。您能否将它们用作 cdef class 的一部分,这样一切都得到妥善处理?