summaryrefslogtreecommitdiffstats
path: root/Modules
diff options
context:
space:
mode:
authorThomas Heller <theller@ctypes.org>2008-06-04 18:59:03 (GMT)
committerThomas Heller <theller@ctypes.org>2008-06-04 18:59:03 (GMT)
commite70c3378c039cae30cd9ae559c4bdeb923254c43 (patch)
tree5977a09a2cd5bbe4aa3ff6bb52b4b554c179097a /Modules
parenta2b34b87a94871999c78f343773ccf0f8c545932 (diff)
downloadcpython-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.c15
-rw-r--r--Modules/_ctypes/callbacks.c21
-rw-r--r--Modules/_ctypes/callproc.c137
-rw-r--r--Modules/_ctypes/ctypes.h13
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
/*