我正在调用由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正在堆栈中分配所有内存,我已经溢出它,但我不知道如何验证这一点。
答案 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'