diff options
author | Armin Rigo <arigo@tunes.org> | 2018-02-16 09:27:27 +0100 |
---|---|---|
committer | Armin Rigo <arigo@tunes.org> | 2018-02-16 09:27:27 +0100 |
commit | 10b51aec2ac55e31a254cec0959bd2ca2659b61a (patch) | |
tree | 5f5820dfc9183d1e3f31912319432ae922e2f77e | |
parent | bb2645dbe2c021db1cbfb72fb32e79c0350aa42b (diff) | |
download | cffi-10b51aec2ac55e31a254cec0959bd2ca2659b61a.tar.gz |
Implement ffi.dlclose() for the in-line case
-rw-r--r-- | c/_cffi_backend.c | 34 | ||||
-rw-r--r-- | cffi/api.py | 10 | ||||
-rw-r--r-- | testing/cffi0/test_function.py | 20 |
3 files changed, 63 insertions, 1 deletions
diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c index 19070b1..b812600 100644 --- a/c/_cffi_backend.c +++ b/c/_cffi_backend.c @@ -3982,7 +3982,8 @@ typedef struct { static void dl_dealloc(DynLibObject *dlobj) { - dlclose(dlobj->dl_handle); + if (dlobj->dl_handle != NULL) + dlclose(dlobj->dl_handle); free(dlobj->dl_name); PyObject_Del(dlobj); } @@ -3992,6 +3993,17 @@ static PyObject *dl_repr(DynLibObject *dlobj) return PyText_FromFormat("<clibrary %s>", dlobj->dl_name); } +static int dl_check_closed(DynLibObject *dlobj) +{ + if (dlobj->dl_handle == NULL) + { + PyErr_Format(PyExc_ValueError, "library '%s' has already been closed", + dlobj->dl_name); + return -1; + } + return 0; +} + static PyObject *dl_load_function(DynLibObject *dlobj, PyObject *args) { CTypeDescrObject *ct; @@ -4002,6 +4014,9 @@ static PyObject *dl_load_function(DynLibObject *dlobj, PyObject *args) &CTypeDescr_Type, &ct, &funcname)) return NULL; + if (dl_check_closed(dlobj) < 0) + return NULL; + if (!(ct->ct_flags & (CT_FUNCTIONPTR | CT_POINTER | CT_ARRAY))) { PyErr_Format(PyExc_TypeError, "function or pointer or array cdata expected, got '%s'", @@ -4034,6 +4049,9 @@ static PyObject *dl_read_variable(DynLibObject *dlobj, PyObject *args) &CTypeDescr_Type, &ct, &varname)) return NULL; + if (dl_check_closed(dlobj) < 0) + return NULL; + dlerror(); /* clear error condition */ data = dlsym(dlobj->dl_handle, varname); if (data == NULL) { @@ -4059,6 +4077,9 @@ static PyObject *dl_write_variable(DynLibObject *dlobj, PyObject *args) &CTypeDescr_Type, &ct, &varname, &value)) return NULL; + if (dl_check_closed(dlobj) < 0) + return NULL; + dlerror(); /* clear error condition */ data = dlsym(dlobj->dl_handle, varname); if (data == NULL) { @@ -4074,10 +4095,21 @@ static PyObject *dl_write_variable(DynLibObject *dlobj, PyObject *args) return Py_None; } +static PyObject *dl_close_lib(DynLibObject *dlobj, PyObject *no_args) +{ + if (dl_check_closed(dlobj) < 0) + return NULL; + dlclose(dlobj->dl_handle); + dlobj->dl_handle = NULL; + Py_INCREF(Py_None); + return Py_None; +} + static PyMethodDef dl_methods[] = { {"load_function", (PyCFunction)dl_load_function, METH_VARARGS}, {"read_variable", (PyCFunction)dl_read_variable, METH_VARARGS}, {"write_variable", (PyCFunction)dl_write_variable, METH_VARARGS}, + {"close_lib", (PyCFunction)dl_close_lib, METH_NOARGS}, {NULL, NULL} /* sentinel */ }; diff --git a/cffi/api.py b/cffi/api.py index 446e554..7b63ca7 100644 --- a/cffi/api.py +++ b/cffi/api.py @@ -143,6 +143,13 @@ class FFI(object): self._libraries.append(lib) return lib + def dlclose(self, lib): + """Close a library obtained with ffi.dlopen(). After this call, + access to functions or variables from the library will fail + (possibly with a segmentation fault). + """ + type(lib).__cffi_close__(lib) + def _typeof_locked(self, cdecl): # call me with the lock! key = cdecl @@ -898,6 +905,9 @@ def _make_ffi_library(ffi, libname, flags): return addressof_var(name) raise AttributeError("cffi library has no function or " "global variable named '%s'" % (name,)) + def __cffi_close__(self): + backendlib.close_lib() + self.__dict__.clear() # if libname is not None: try: diff --git a/testing/cffi0/test_function.py b/testing/cffi0/test_function.py index 61be656..077f806 100644 --- a/testing/cffi0/test_function.py +++ b/testing/cffi0/test_function.py @@ -499,3 +499,23 @@ class TestFunction(object): """) m = ffi.dlopen(lib_m) assert dir(m) == ['MYE1', 'MYE2', 'MYFOO', 'myconst', 'myfunc', 'myvar'] + + def test_dlclose(self): + if self.Backend is CTypesBackend: + py.test.skip("not with the ctypes backend") + ffi = FFI(backend=self.Backend()) + ffi.cdef("int foobar(void); int foobaz;") + lib = ffi.dlopen(lib_m) + ffi.dlclose(lib) + e = py.test.raises(ValueError, ffi.dlclose, lib) + assert str(e.value).startswith("library '") + assert str(e.value).endswith("' has already been closed") + e = py.test.raises(ValueError, getattr, lib, 'foobar') + assert str(e.value).startswith("library '") + assert str(e.value).endswith("' has already been closed") + e = py.test.raises(ValueError, getattr, lib, 'foobaz') + assert str(e.value).startswith("library '") + assert str(e.value).endswith("' has already been closed") + e = py.test.raises(ValueError, setattr, lib, 'foobaz', 42) + assert str(e.value).startswith("library '") + assert str(e.value).endswith("' has already been closed") |