PyObject dealloc Python C API期间的SIGSEGV

2024-09-30 16:21:02 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在尝试使用pythoncapi实现一个简单的生成器。生成器功能正在工作,但是python解释器(或其他东西)在调用Py_TYPE(self)->tp_free((PyObject*)self)期间出错了。我已经读了又读了引用文档,并且认为引用计数正确,但是SIGSEGV仍然存在。我已经用调试符号构建了扩展,并用gdb(下面的输出)进行了调试,它似乎正在解释器中发生。我所知道的唯一一件我还没有尝试过的事情是从源代码构建带有调试符号的python,以允许进一步的自省。任何帮助或正确的文件点将不胜感激。我使用的是python3.6.4

#include "hexc.h"

// Define class boilerplate functions
static PyObject *hex_range_iter_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    hexrangeiter_t *self;

    self = (hexrangeiter_t *)(type->tp_alloc(type, 0));
    if (self != NULL) {
        self->pos = 0;
        self->dir = 0;
        self->distance = 1;
        self->radius = 0;
        self->hex = NULL;
        self->center = NULL;
    }

    return (PyObject *)self;
}

static void hex_range_iter_dealloc(hexrangeiter_t *self)
{
    Py_DECREF(self->hex);  // decref our stored hex
    Py_DECREF(self->center); // and our center point
    Py_TYPE(self)->tp_free((PyObject*)self); // SIGSEGV here

}

static int hex_range_iter_init(hexrangeiter_t *self, PyObject *args, PyObject *kwds)
{
    // Takes 2 args, the center of the ring as an AbstractHex, and the radius as an int
    static char *kwlist[] = {"center", "radius", NULL};
    if (! PyArg_ParseTupleAndKeywords(args, kwds, "Oi", kwlist, &self->center, &self->radius))
        return -1;

    Py_INCREF(self->center); //increc\f here because Py_Arg_Parse... returns borrowed references
    PyObject *radius = PyLong_FromLong(1L);
    PyObject *direction = PyObject_CallMethod(self->center, "direction", "i", 4);
    PyObject *scaled = PyNumber_Multiply(direction, radius);
    self->hex = PyNumber_Add(self->center, scaled); // No incref needed because Py_Number_Add returns a new reference
    Py_DECREF(radius); // decref all of our temp variables
    Py_DECREF(direction);
    Py_DECREF(scaled);
    return 0;
}

static PyObject *hex_range_iter_next(hexrangeiter_t *self)
{

    self->pos++; // pos is the index along a side of a hexagonal ring
    if (self->pos >= self->radius)  // its length is equal to the radius of the ring
    {                               // so check if we've run off the end and if so change direction
        self->pos = 0;
        self->dir++;
    }

    if (self->dir >= 6)             // If we've already done all 6 sides
    {
        self->dir = 0;              // reset our direction
        self->distance++;           // and increase our radius

        if (self->distance >= self->radius) // If we've reached our max radius
            return NULL;                    // return NULL to exit generator
                                            // Returning NULL in this case is enough. The next() builtin will raise the
                                            // StopIteration error for us.

        PyObject *radius = PyLong_FromLong(self->distance); // calculate our new starting position  v
        PyObject *direction = PyObject_CallMethod(self->center, "direction", "i", 4);
        PyObject *scaled = PyNumber_Multiply(direction, radius); 
        Py_DECREF(self->hex); // make sure to decref our unused hex from last call
        self->hex = PyNumber_Add(self->center, scaled);  // finish calc and save new hex            ^
        Py_DECREF(radius);  // decref all our temp variables
        Py_DECREF(direction);
        Py_DECREF(scaled);
    }

    PyObject *ret = self->hex; // grab a ref to our return value
    self->hex = PyObject_CallMethod(ret, "neighbor", "i", self->dir); // store the next hex in our sequence

    return (ret);
}

PyTypeObject HexRangeGenType = {
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
    "HexRangeGenerator",             /* tp_name */
    sizeof(hexrangeiter_t),           /* tp_basicsize */
    0,                              /* tp_itemsize */
    (destructor)hex_range_iter_dealloc,  /* tp_dealloc */
    0,                              /* tp_print */
    0,                              /* tp_getattr */
    0,                              /* tp_setattr */
    0,                              /* tp_reserved */
    0,                 /* tp_repr */
    0,                              /* tp_as_number */
    0,                              /* tp_as_sequence */
    0,                              /* tp_as_mapping */
    0,                              /* tp_hash */
    0,                              /* tp_call */
    0,                              /* tp_str */
    0,                              /* tp_getattro */
    0,                              /* tp_setattro */
    0,                              /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT,             /* tp_flags */
    0,                              /* tp_doc */
    0,                              /* tp_traverse */
    0,                              /* tp_clear */
    0,                              /* tp_richcompare */
    0,                              /* tp_weaklistoffset */
    PyObject_SelfIter,              /* tp_iter */
    (iternextfunc)hex_range_iter_next,   /* tp_iternext */
    0,                              /* tp_methods */
    0,                              /* tp_members */
    0,                              /* tp_getset */
    0,                              /* tp_base */
    0,                              /* tp_dict */
    0,                              /* tp_descr_get */
    0,                              /* tp_descr_set */
    0,                              /* tp_dictoffset */
    (initproc)hex_range_iter_init,                 /* tp_init */
    0,            /* tp_alloc */
    hex_range_iter_new,                  /* tp_new */
};

十六进制c

#ifndef HEXC_H
#define HEXC_H

#include <Python.h>
#include <structmember.h>
#include <stdlib.h>

// #include "math_3d.h"

// declare Hex object structure
struct abstracthex_s;
struct hex_s;
struct slice_s;
struct stack_s;
struct grid_s;

struct hexringiter_s;
struct hexrangeiter_s;

typedef struct abstracthex_s    abstracthex_t;
typedef struct slice_s          slice_t;
typedef struct stack_s          stack_t;
typedef struct hex_s            hex_t;
typedef struct grid_s           grid_t;

typedef struct hexringiter_s    hexringiter_t;
typedef struct hexrangeiter_s   hexrangeiter_t;

struct abstracthex_s
{
    PyObject_HEAD
    int q;
    int r;
    int s;
};

struct hexringiter_s
{
    PyObject_HEAD
    int pos;
    int dir;
    int radius;
    PyObject *hex;
};

struct hexrangeiter_s
{
    PyObject_HEAD
    int dir;
    long pos;
    long distance;
    long radius;
    PyObject *hex;
    PyObject *center;
};

// declare Python type objects
extern PyTypeObject AbstractHexType;
extern PyTypeObject HexRingGenType;
extern PyTypeObject HexRangeGenType;
extern PyTypeObject HexType;
extern PyTypeObject SliceType;
extern PyTypeObject StackType;
extern PyTypeObject GridType;

// declare some handy constants
static int  DIRECTIONS[6][3] = {
    {1, 0, -1},
    {1, -1, 0},
    {0, -1, 1},
    {-1, 0, 1},
    {-1, 1, 0},
    {0, 1, -1}
};

#endif

你知道吗测试.py你知道吗

import hexc
import time

center = hexc.AbstractHex(0,0,0)

start = time.time()
z = 0
for hex in hexc.HexRangeGenerator(center, 5):
    z += 1
print(time.time() - start)

gdb堆栈跟踪

#0  0x0000000000000000 in ?? ()
#1  0x00007ffff67ce521 in hex_range_iter_dealloc (self=0x7ffff6a0f5f0) at hexc/hex_range_iter.c:28
#2  0x00007ffff73c2e05 in _PyEval_EvalFrameDefault () from /usr/lib/libpython3.6m.so.1.0
#3  0x00007ffff742b2c8 in PyEval_EvalCodeEx () from /usr/lib/libpython3.6m.so.1.0
#4  0x00007ffff73be6ec in PyEval_EvalCode () from /usr/lib/libpython3.6m.so.1.0
#5  0x00007ffff7490f84 in ?? () from /usr/lib/libpython3.6m.so.1.0
#6  0x00007ffff7493751 in PyRun_FileExFlags () from /usr/lib/libpython3.6m.so.1.0
#7  0x00007ffff7493954 in PyRun_SimpleFileExFlags () from /usr/lib/libpython3.6m.so.1.0
#8  0x00007ffff748970b in Py_Main () from /usr/lib/libpython3.6m.so.1.0
#9  0x0000555555554c39 in main ()

编辑

这个问题与参考文献计数无关(我想)

在将类型添加到模块时,我犯了一个错误。你知道吗

if (PyType_Ready(&HexRingGenType) < 0)
    return NULL;

应该是的

if (PyType_Ready(&HexRangeGenType) < 0)
    return NULL

修复此问题导致故障消失。你知道吗

正如暗影游侠在评论中所指出的,我在引用计数方面也犯了错误


Tags: inpyselfourrangestructintpyobject