diff options
author | Thomas Heller <theller@ctypes.org> | 2008-06-04 18:59:03 (GMT) |
---|---|---|
committer | Thomas Heller <theller@ctypes.org> | 2008-06-04 18:59:03 (GMT) |
commit | e70c3378c039cae30cd9ae559c4bdeb923254c43 (patch) | |
tree | 5977a09a2cd5bbe4aa3ff6bb52b4b554c179097a /Modules | |
parent | a2b34b87a94871999c78f343773ccf0f8c545932 (diff) | |
download | cpython-e70c3378c039cae30cd9ae559c4bdeb923254c43.zip cpython-e70c3378c039cae30cd9ae559c4bdeb923254c43.tar.gz cpython-e70c3378c039cae30cd9ae559c4bdeb923254c43.tar.bz2 |
Issue #1798: Add ctypes calling convention that allows safe access to
errno (and LastError, on Windows).
ctypes maintains a module-global, but thread-local, variable that
contains an error number; called 'ctypes_errno' for this discussion.
This variable is a private copy of the systems 'errno' value; the copy
is swapped with the 'errno' variable on several occasions.
Foreign functions created with CDLL(..., use_errno=True), when called,
swap the values just before the actual function call, and swapped
again immediately afterwards. The 'use_errno' parameter defaults to
False, in this case 'ctypes_errno' is not touched.
The values are also swapped immeditately before and after ctypes
callback functions are called, if the callbacks are constructed using
the new optional use_errno parameter set to True: CFUNCTYPE(..., use_errno=TRUE)
or WINFUNCTYPE(..., use_errno=True).
Two new ctypes functions are provided to access the 'ctypes_errno'
value from Python:
- ctypes.set_errno(value) sets ctypes_errno to 'value', the previous
ctypes_errno value is returned.
- ctypes.get_errno() returns the current ctypes_errno value.
---
On Windows, the same scheme is implemented for the error value which
is managed by the GetLastError() and SetLastError() windows api calls.
The ctypes functions are 'ctypes.set_last_error(value)' and
'ctypes.get_last_error()', the CDLL and WinDLL optional parameter is
named 'use_last_error', defaults to False.
---
On Windows, TlsSetValue and TlsGetValue calls are used to provide
thread local storage for the variables; ctypes compiled with __GNUC__
uses __thread variables.
Diffstat (limited to 'Modules')
-rw-r--r-- | Modules/_ctypes/_ctypes.c | 15 | ||||
-rw-r--r-- | Modules/_ctypes/callbacks.c | 21 | ||||
-rw-r--r-- | Modules/_ctypes/callproc.c | 137 | ||||
-rw-r--r-- | Modules/_ctypes/ctypes.h | 13 |
4 files changed, 182 insertions, 4 deletions
diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 740b7f6..7deb3f3 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -3271,7 +3271,7 @@ CFuncPtr_new(PyTypeObject *type, PyObject *args, PyObject *kwds) thunk = AllocFunctionCallback(callable, dict->argtypes, dict->restype, - dict->flags & FUNCFLAG_CDECL); + dict->flags); if (!thunk) return NULL; @@ -5273,6 +5273,17 @@ init_ctypes(void) if (!m) return; +#ifdef MS_WIN32 + dwTlsIndex_LastError = TlsAlloc(); + dwTlsIndex_errno = TlsAlloc(); + if (dwTlsIndex_LastError == TLS_OUT_OF_INDEXES + || dwTlsIndex_errno == TLS_OUT_OF_INDEXES) { + PyErr_SetString(PyExc_MemoryError, + "Could not allocate TLSIndex for LastError value"); + return; + } +#endif + _pointer_type_cache = PyDict_New(); if (_pointer_type_cache == NULL) return; @@ -5394,6 +5405,8 @@ init_ctypes(void) PyModule_AddObject(m, "FUNCFLAG_STDCALL", PyInt_FromLong(FUNCFLAG_STDCALL)); #endif PyModule_AddObject(m, "FUNCFLAG_CDECL", PyInt_FromLong(FUNCFLAG_CDECL)); + PyModule_AddObject(m, "FUNCFLAG_USE_ERRNO", PyInt_FromLong(FUNCFLAG_USE_ERRNO)); + PyModule_AddObject(m, "FUNCFLAG_USE_LASTERROR", PyInt_FromLong(FUNCFLAG_USE_LASTERROR)); PyModule_AddObject(m, "FUNCFLAG_PYTHONAPI", PyInt_FromLong(FUNCFLAG_PYTHONAPI)); PyModule_AddStringConstant(m, "__version__", "1.1.0"); diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c index b78c528..78c7419 100644 --- a/Modules/_ctypes/callbacks.c +++ b/Modules/_ctypes/callbacks.c @@ -189,6 +189,7 @@ static void _CallPythonObject(void *mem, SETFUNC setfunc, PyObject *callable, PyObject *converters, + int flags, void **pArgs) { Py_ssize_t i; @@ -271,8 +272,22 @@ static void _CallPythonObject(void *mem, #define CHECK(what, x) \ if (x == NULL) _AddTraceback(what, "_ctypes/callbacks.c", __LINE__ - 1), PyErr_Print() + if (flags & FUNCFLAG_USE_ERRNO) + _swap_errno(); +#ifdef MS_WIN32 + if (flags & FUNCFLAG_USE_LASTERROR) + _swap_last_error(); +#endif + result = PyObject_CallObject(callable, arglist); CHECK("'calling callback function'", result); + +#ifdef MS_WIN32 + if (flags & FUNCFLAG_USE_LASTERROR) + _swap_last_error(); +#endif + if (flags & FUNCFLAG_USE_ERRNO) + _swap_errno(); if ((restype != &ffi_type_void) && result) { PyObject *keep; assert(setfunc); @@ -322,6 +337,7 @@ static void closure_fcn(ffi_cif *cif, p->setfunc, p->callable, p->converters, + p->flags, args); } @@ -351,7 +367,7 @@ static CThunkObject* CThunkObject_new(Py_ssize_t nArgs) CThunkObject *AllocFunctionCallback(PyObject *callable, PyObject *converters, PyObject *restype, - int is_cdecl) + int flags) { int result; CThunkObject *p; @@ -371,6 +387,7 @@ CThunkObject *AllocFunctionCallback(PyObject *callable, goto error; } + p->flags = flags; for (i = 0; i < nArgs; ++i) { PyObject *cnv = PySequence_GetItem(converters, i); if (cnv == NULL) @@ -398,7 +415,7 @@ CThunkObject *AllocFunctionCallback(PyObject *callable, cc = FFI_DEFAULT_ABI; #if defined(MS_WIN32) && !defined(_WIN32_WCE) && !defined(MS_WIN64) - if (is_cdecl == 0) + if ((flags & FUNCFLAG_CDECL) == 0) cc = FFI_STDCALL; #endif result = ffi_prep_cif(&p->cif, cc, diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index 95b2709..70ca73f 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -83,6 +83,131 @@ #define DONT_USE_SEH #endif +/* + ctypes maintains a module-global, but thread-local, variable that contains + an error number; called 'ctypes_errno' for this discussion. This variable + is a private copy of the systems 'errno' value; the copy is swapped with the + 'errno' variable on several occasions. + + Foreign functions created with CDLL(..., use_errno=True), when called, swap + the values just before the actual function call, and swapped again + immediately afterwards. The 'use_errno' parameter defaults to False, in + this case 'ctypes_errno' is not touched. + + The values are also swapped immeditately before and after ctypes callback + functions are called, if the callbacks are constructed using the new + optional use_errno parameter set to True: CFUNCTYPE(..., use_errno=TRUE) or + WINFUNCTYPE(..., use_errno=True). + + Two new ctypes functions are provided to access the 'ctypes_errno' value + from Python: + + - ctypes.set_errno(value) sets ctypes_errno to 'value', the previous + ctypes_errno value is returned. + + - ctypes.get_errno() returns the current ctypes_errno value. + + --- + + On Windows, the same scheme is implemented for the error value which is + managed by the GetLastError() and SetLastError() windows api calls. + + The ctypes functions are 'ctypes.set_last_error(value)' and + 'ctypes.get_last_error()', the CDLL and WinDLL optional parameter is named + 'use_last_error', defaults to False. + + --- + + On Windows, TlsSetValue and TlsGetValue calls are used to provide thread + local storage for the variables; ctypes compiled with __GNUC__ uses __thread + variables. +*/ + +#if defined(MS_WIN32) +DWORD dwTlsIndex_LastError; +DWORD dwTlsIndex_errno; + +void +_swap_last_error(void) +{ + DWORD temp = GetLastError(); + SetLastError((DWORD)TlsGetValue(dwTlsIndex_LastError)); + TlsSetValue(dwTlsIndex_LastError, (void *)temp); +} + +static PyObject * +get_last_error(PyObject *self, PyObject *args) +{ + return PyInt_FromLong((DWORD)TlsGetValue(dwTlsIndex_LastError)); +} + +static PyObject * +set_last_error(PyObject *self, PyObject *args) +{ + DWORD new_value, prev_value; + if (!PyArg_ParseTuple(args, "i", &new_value)) + return NULL; + prev_value = (DWORD)TlsGetValue(dwTlsIndex_LastError); + TlsSetValue(dwTlsIndex_LastError, (void *)new_value); + return PyInt_FromLong(prev_value); +} + +void +_swap_errno(void) +{ + int temp = errno; + errno = (int)TlsGetValue(dwTlsIndex_errno); + TlsSetValue(dwTlsIndex_errno, (void *)temp); +} + +static PyObject * +get_errno(PyObject *self, PyObject *args) +{ + return PyInt_FromLong((int)TlsGetValue(dwTlsIndex_errno)); +} + +static PyObject * +set_errno(PyObject *self, PyObject *args) +{ + int new_value, prev_value; + if (!PyArg_ParseTuple(args, "i", &new_value)) + return NULL; + prev_value = (int)TlsGetValue(dwTlsIndex_errno); + TlsSetValue(dwTlsIndex_errno, (void *)new_value); + return PyInt_FromLong(prev_value); +} + +#elif defined(__GNUC__) +static __thread int ctypes_errno; + +void +_swap_errno(void) +{ + int temp = errno; + errno = ctypes_errno; + ctypes_errno = temp; +} + +static PyObject * +get_errno(PyObject *self, PyObject *args) +{ + return PyInt_FromLong(ctypes_errno); +} + +static PyObject * +set_errno(PyObject *self, PyObject *args) +{ + int new_errno; + if (!PyArg_ParseTuple(args, "i", &new_errno)) + return NULL; + return PyInt_FromLong(_save_errno(new_errno)); +} +#else + +#error "TLS not implemented in this configuration" + +#endif + #ifdef MS_WIN32 PyObject *ComError; @@ -660,7 +785,11 @@ static int _call_function_pointer(int flags, if ((flags & FUNCFLAG_PYTHONAPI) == 0) Py_UNBLOCK_THREADS #endif + if (flags & FUNCFLAG_USE_ERRNO) + _swap_errno(); #ifdef MS_WIN32 + if (flags & FUNCFLAG_USE_LASTERROR) + _swap_last_error(); #ifndef DONT_USE_SEH __try { #endif @@ -675,7 +804,11 @@ static int _call_function_pointer(int flags, ; } #endif + if (flags & FUNCFLAG_USE_LASTERROR) + _swap_last_error(); #endif + if (flags & FUNCFLAG_USE_ERRNO) + _swap_errno(); #ifdef WITH_THREAD if ((flags & FUNCFLAG_PYTHONAPI) == 0) Py_BLOCK_THREADS @@ -1667,6 +1800,8 @@ pointer(PyObject *self, PyObject *arg) } PyMethodDef module_methods[] = { + {"get_errno", get_errno, METH_NOARGS}, + {"set_errno", set_errno, METH_VARARGS}, {"POINTER", POINTER, METH_O }, {"pointer", pointer, METH_O }, {"_unpickle", unpickle, METH_VARARGS }, @@ -1675,6 +1810,8 @@ PyMethodDef module_methods[] = { {"set_conversion_mode", set_conversion_mode, METH_VARARGS, set_conversion_mode_doc}, #endif #ifdef MS_WIN32 + {"get_last_error", get_last_error, METH_NOARGS}, + {"set_last_error", set_last_error, METH_VARARGS}, {"CopyComPointer", copy_com_pointer, METH_VARARGS, copy_com_pointer_doc}, {"FormatError", format_error, METH_VARARGS, format_error_doc}, {"LoadLibrary", load_library, METH_VARARGS, load_library_doc}, diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index 1a104cf..a7c3562 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -87,6 +87,7 @@ typedef struct { PyObject_VAR_HEAD ffi_closure *pcl; /* the C callable */ ffi_cif cif; + int flags; PyObject *converters; PyObject *callable; PyObject *restype; @@ -185,7 +186,7 @@ extern PyMethodDef module_methods[]; extern CThunkObject *AllocFunctionCallback(PyObject *callable, PyObject *converters, PyObject *restype, - int stdcall); + int flags); /* a table entry describing a predefined ctypes type */ struct fielddesc { char code; @@ -303,6 +304,8 @@ PyObject *_CallProc(PPROC pProc, #define FUNCFLAG_CDECL 0x1 #define FUNCFLAG_HRESULT 0x2 #define FUNCFLAG_PYTHONAPI 0x4 +#define FUNCFLAG_USE_ERRNO 0x8 +#define FUNCFLAG_USE_LASTERROR 0x10 #define TYPEFLAG_ISPOINTER 0x100 #define TYPEFLAG_HASPOINTER 0x200 @@ -421,8 +424,16 @@ extern int IsSimpleSubType(PyObject *obj); extern PyObject *_pointer_type_cache; +extern void _swap_errno(void); + #ifdef MS_WIN32 + +extern void _swap_last_error(void); + extern PyObject *ComError; + +extern DWORD dwTlsIndex_LastError; +extern DWORD dwTlsIndex_errno; #endif /* |