传递大型数组时,OSX 10.10上的ctypes分段故障11

时间:2016-01-11 15:32:25

标签: python ctypes

我正在调用由ctypes加载的dylib,它对输入值执行转换。据我所知,dylib正常工作(我写了它,并且可以提供源代码),并且我释放它“泄漏”回Python的内存。使用短于65018项的列表调用它可以按预期工作,但任何大于该项的列表都会生成Segmentation fault: 11

from ctypes import cdll, c_float, Structure, POINTER, c_uint32, c_size_t, c_void_p, cast
from sys import platform
import numpy as np

if platform == "darwin":
    ext = "dylib"
else:
    ext = "so"

lib = cdll.LoadLibrary('target/release/liblonlat_bng.' + ext)

class BNG_FFITuple(Structure):
    _fields_ = [("a", c_uint32),
                ("b", c_uint32)]

class BNG_FFIArray(Structure):
    _fields_ = [("data", c_void_p),
                ("len", c_size_t)]

    # Allow implicit conversions from a sequence of 32-bit unsigned
    # integers.
    @classmethod
    def from_param(cls, seq):
        return seq if isinstance(seq, cls) else cls(seq)

    # Wrap sequence of values. You can specify another type besides a
    # 32-bit unsigned integer.
    def __init__(self, seq, data_type = c_float):
        array_type = data_type * len(seq)
        raw_seq = array_type(*seq)
        self.data = cast(raw_seq, c_void_p)
        self.len = len(seq)

# A conversion function that cleans up the result value to make it
# nicer to consume.
def bng_void_array_to_tuple_list(array, _func, _args):
    res = cast(array.data, POINTER(BNG_FFITuple * array.len))[0]
    drop_bng_array(array)
    return [(i.a, i.b) for i in iter(res)]

convert_bng = lib.convert_to_bng
convert_bng.argtypes = (BNG_FFIArray, BNG_FFIArray)
convert_bng.restype = BNG_FFIArray
convert_bng.errcheck = bng_void_array_to_tuple_list
# cleanup
drop_bng_array = lib.drop_int_array
drop_bng_array.argtypes = (BNG_FFIArray,)
drop_bng_array.restype = None

def convertbng_threaded(lons, lats):
    """ Multi-threaded lon lat to BNG wrapper """
    return convert_bng(lons, lats)

N = 55.811741
E = 1.768960
S = 49.871159
W = -6.379880

num_coords = 65017
lon_ls = list(np.random.uniform(W, E, [num_coords]))
lat_ls = list(np.random.uniform(S, N, [num_coords]))


# segfault with > 65017
convertbng_threaded(lon_ls, lat_ls)

我的系统:MacBook Air带有4GB内存,OSX 10.11.2,Python 2.7.10

我唯一能想到的是因为我的dylib正在堆栈中分配所有内存,我已经溢出它,但我不知道如何验证这一点。

1 个答案:

答案 0 :(得分:3)

res中创建的bng_void_array_to_tuple_list数组是数据视图 - 而不是副本。这意味着您需要在调用drop_bng_array(array)之前评估列表推导以创建列表结果。

以下演示了新数组是原始数据的视图:

>>> from ctypes import *
>>> arr = (c_char * 3)(*'abc')
>>> ptr = POINTER(c_char)(arr)
>>> new_arr = cast(ptr, POINTER(c_char * 3))[0]
>>> new_arr[:]
'abc'

在视图中可以看到对原始数组所做的更改:

>>> arr[0] = 'z'
>>> new_arr[:]
'zbc'