Reputation: 1422
I wrote a Python wrapper for a C library using ctypes
. Everything worked fine, until I tried it with the threading
module.
Here is a small example to reproduce it:
#include <stdlib.h>
#include <assert.h>
char *foo_init() {
char *result = (char*)malloc(sizeof(char));
assert(result);
return result;
}
char foo_get(const char* p) {
return *p;
}
void foo_set(char *p, char value) {
*p = value;
}
void foo_del(char *p) {
free(p);
}
import threading
import ctypes
libfoo=ctypes.CDLL('foo.so')
libfoo.foo_init.restype = ctypes.c_void_p
libfoo.foo_get.restype = ctypes.c_char
class Foo:
def __init__(self):
self.__obj__ = libfoo.foo_init()
def get(self):
return libfoo.foo_get(self.__obj__)
def set(self, value):
libfoo.foo_set(self.__obj__, ctypes.c_char(value))
def __del__(self):
libfoo.foo_del(self.__obj__)
x = Foo()
x.set(b'x')
print(x.get())
def compute():
y = Foo()
y.set(b'y')
print(y.get())
t = threading.Thread(target=compute)
t.start()
t.join()
Compilation:
gcc -Wall -shared -o foo.so -fPIC foo.c
Execution:
export LD_LIBRARY_PATH=.
python3 foo.py
Result:
b'x'
[1] 8627 segmentation fault python3 foo.py
We see here that x.get()
is correctly printed whereas y.get()
does a segmentation fault. Thus, it seems that there is a problem linked to ctypes
and threading
.
Note that if I move the initialization of y
outside of the compute
function, then the program exits normally.
Moreover, each of the functions which manipulate the pointer produce a segmentation fault (e.g. if I just keep the __init__
and __del__
functions, there is still a crash).
Any idea on how to fix this?
Upvotes: 2
Views: 2360
Reputation: 1422
@user2357112 gave me the answer in comments: adding the (good) return type and the arguments types is important.
Replacing the beginning of foo.py
by the following fix the problem:
libfoo.foo_init.restype = ctypes.POINTER(ctypes.c_char)
libfoo.foo_init.argtypes = []
libfoo.foo_get.restype = ctypes.c_char
libfoo.foo_get.argtypes = [ctypes.POINTER(ctypes.c_char)]
libfoo.foo_set.argtypes = [ctypes.POINTER(ctypes.c_char), ctypes.c_char]
libfoo.foo_del.argtypes = [ctypes.POINTER(ctypes.c_char)]
Upvotes: 1