Cython memoryview错误 - 致命的Python错误:获取计数是

时间:2016-03-09 00:25:20

标签: python cython

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

class AsyncOperation extends Thread { public function __construct($threadId) { $this->threadId = $threadId; } public function run() { printf("T %s: Sleeping 3sec\n", $this->threadId); sleep(3); printf("T %s: Hello World\n", $this->threadId); } } $start = microtime(true); for ($i = 1; $i <= 5; $i++) { $t[$i] = new AsyncOperation($i); $t[$i]->start(); } echo microtime(true) - $start . "\n"; echo "end\n";

以下是上述行负责人C档案的摘录:

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

我尝试制作一个最小的例子来重现错误,但后来没有发生。然后我重写了使用最小例子绘制的函数,它再次失败。

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

/* "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]

1 个答案:

答案 0 :(得分:1)

我认为问题与未初始化的记忆有关(正如我在评论中所说)。查看简单示例中生成的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。但是,即使在这种情况下lv在释放baz时也永远不会被正确释放,这可能会导致内存泄漏。我确实认为将内存视图用作C结构的一部分并不合理。您是否可以将它们作为cdef class的一部分使用,其中一切都得到妥善处理?