Commits

Armin Rigo committed be6a98c

Work in progress on porting to Windows.

Comments (0)

Files changed (3)

 #include <Python.h>
 #include "structmember.h"
 
+#ifdef MS_WIN32
+#include <windows.h>
+#else
 #include <stddef.h>
 #include <stdint.h>
 #include <dlfcn.h>
 #include <errno.h>
-
 #include <ffi.h>
 #include <sys/mman.h>
+#endif
 
 #include "malloc_closure.h"
 
 
 /* whenever running Python code, the errno is saved in this thread-local
    variable */
-static __thread int saved_errno = 0;
+#if defined(__GNUC__) && !defined(MS_WIN32)
+#  define CFFI_USE_THREAD_LOCALS
+#endif
+
+#ifdef CFFI_USE_THREAD_LOCALS
+static __thread int cffi_saved_errno = 0;
+static void save_errno(void) { cffi_saved_errno = errno; }
+static void *restore_errno(void) { errno = cffi_saved_errno; return NULL; }
+static void init_errno(void) { }
+#else
+#include "non_gcc_errno.h"
+#endif
 
 /************************************************************/
 
 write_raw_integer_data(char *target, unsigned PY_LONG_LONG source, int size)
 {
     if (size == sizeof(unsigned char))
-        *((unsigned char*)target) = source;
+        *((unsigned char*)target) = (unsigned char)source;
     else if (size == sizeof(unsigned short))
-        *((unsigned short*)target) = source;
+        *((unsigned short*)target) = (unsigned short)source;
     else if (size == sizeof(unsigned int))
-        *((unsigned int*)target) = source;
+        *((unsigned int*)target) = (unsigned int)source;
     else if (size == sizeof(unsigned long))
-        *((unsigned long*)target) = source;
+        *((unsigned long*)target) = (unsigned long)source;
     else if (size == sizeof(unsigned PY_LONG_LONG))
         *((unsigned PY_LONG_LONG*)target) = source;
     else
 write_raw_float_data(char *target, double source, int size)
 {
     if (size == sizeof(float))
-        *((float*)target) = source;
+        *((float*)target) = (float)source;
     else if (size == sizeof(double))
         *((double*)target) = source;
     else
                              == (CT_PRIMITIVE_SIGNED|CT_PRIMITIVE_FITS_LONG)) {
         /* this case is to handle enums, but also serves as a slight
            performance improvement for some other primitive types */
-        long value = read_raw_signed_data(cd->c_data, cd->c_type->ct_size);
+        long value = (long)read_raw_signed_data(cd->c_data,
+                                                cd->c_type->ct_size);
         return PyInt_FromLong(value);
     }
     if (cd->c_type->ct_flags & (CT_PRIMITIVE_SIGNED|CT_PRIMITIVE_UNSIGNED)) {
 
     resultdata = buffer + cif_descr->exchange_offset_arg[0];
 
-    errno = saved_errno;
+    restore_errno();
     ffi_call(&cif_descr->cif, (void (*)(void))(cd->c_data),
              resultdata, buffer_array);
-    saved_errno = errno;
+    save_errno();
 
     if (fresult->ct_flags & CT_VOID) {
         res = Py_None;
 
 /************************************************************/
 
+#ifdef MS_WIN32
+typedef HMODULE dl_handle_t;
+#else
+typedef void *dl_handle_t;
+#endif
+
 typedef struct {
     PyObject_HEAD
-    void *dl_handle;
+    dl_handle_t dl_handle;
     char *dl_name;
 } DynLibObject;
 
 static void dl_dealloc(DynLibObject *dlobj)
 {
+#ifdef MS_WIN32
+    if (dlobj->dl_handle)
+        FreeLibrary(dlobj->dl_handle);
+#else
     if (dlobj->dl_handle != RTLD_DEFAULT)
         dlclose(dlobj->dl_handle);
+#endif
     free(dlobj->dl_name);
     PyObject_Del(dlobj);
 }
 static void invoke_callback(ffi_cif *cif, void *result, void **args,
                             void *userdata)
 {
-    saved_errno = errno;
+    save_errno();
 
     PyObject *cb_args = (PyObject *)userdata;
     CTypeDescrObject *ct = (CTypeDescrObject *)PyTuple_GET_ITEM(cb_args, 0);
     Py_XDECREF(py_args);
     Py_XDECREF(py_res);
     Py_DECREF(cb_args);
-    errno = saved_errno;
+    restore_errno();
     return;
 
  error:
 
 static PyObject *b_get_errno(PyObject *self, PyObject *noarg)
 {
-    return PyInt_FromLong(saved_errno);
+    int err;
+    restore_errno();
+    err = errno;
+    errno = 0;
+    return PyInt_FromLong(err);
 }
 
 static PyObject *b_set_errno(PyObject *self, PyObject *args)
     int i;
     if (!PyArg_ParseTuple(args, "i:set_errno", &i))
         return NULL;
-    saved_errno = i;
+    errno = i;
+    save_errno();
+    errno = 0;
     Py_INCREF(Py_None);
     return Py_None;
 }
     return result;
 }
 
-static void _cffi_restore_errno(void) {
-    errno = saved_errno;
-}
-
-static void _cffi_save_errno(void) {
-    saved_errno = errno;
-}
-
 static PyObject *_cffi_from_c_char(char x) {
     return PyString_FromStringAndSize(&x, 1);
 }
     _cffi_from_c_pointer,
     _cffi_to_c_pointer,
     _cffi_get_struct_layout,
-    _cffi_restore_errno,
-    _cffi_save_errno,
+    restore_errno,
+    save_errno,
     _cffi_from_c_char,
     convert_to_object,
 };
     v = PyCObject_FromVoidPtr((void *)cffi_exports, NULL);
     if (v == NULL || PyModule_AddObject(m, "_C_API", v) < 0)
         return;
+
+    init_errno();
 }

c/non_gcc_errno.h

+
+#ifndef MS_WIN32
+#  error "only GCC or Win32 are supported so far"
+#endif
+
+struct cffi_errno_s {
+    int saved_errno;
+    int saved_lasterror;
+};
+
+static DWORD cffi_tls_index;
+
+static void init_errno(void)
+{
+    cffi_tls_index = TlsAlloc();
+    if (cffi_tls_index == TLS_OUT_OF_INDEXES)
+        PyErr_SetString(PyExc_WindowsError, "TlsAlloc() failed");
+}
+
+static struct cffi_errno_s *_geterrno_object(void)
+{
+    LPVOID p = TlsGetValue(cffi_tls_index);
+
+    if (p == NULL) {
+        p = PyMem_Malloc(sizeof(struct cffi_errno_s));
+        if (p == NULL)
+            return NULL;
+        memset(p, 0, sizeof(struct cffi_errno_s));
+        TlsSetValue(cffi_tls_index, p);
+    }
+    return (struct cffi_errno_s *)p;
+}
+
+static void save_errno(void)
+{
+    int current_err = errno;
+    int current_lasterr = GetLastError();
+    struct cffi_errno_s *p;
+
+    p = _geterrno_object();
+    if (p != NULL) {
+        p->saved_errno = current_err;
+        p->saved_lasterror = current_lasterr;
+    }
+    /* else: cannot report the error */
+}
+
+static void restore_errno(void)
+{
+    struct cffi_errno_s *p;
+
+    p = _geterrno_object();
+    if (p != NULL) {
+        SetLastError(p->saved_lasterror);
+        errno = p->saved_errno;
+    }
+    /* else: cannot report the error */
+}
-import sys
+import sys, os
 from setuptools import setup, Feature, Extension
 
 
+sources = ['c/_ffi_backend.c']
+libraries = ['ffi']
+include_dirs = []
+
+
+if sys.platform == 'win32':
+    COMPILE_LIBFFI = 'libffi_msvc'    # from the CPython distribution
+else:
+    COMPILE_LIBFFI = None
+
+if COMPILE_LIBFFI:
+    include_dirs.append(COMPILE_LIBFFI)
+    libraries.remove('ffi')
+    sources.extend(os.path.join(COMPILE_LIBFFI, filename)
+                   for filename in os.listdir(COMPILE_LIBFFI)
+                   if filename.lower().endswith('.c'))
+
+
 setup(
     name='ffi',
     descripton='experimental ffi after the example of lua ffi',
             standard='__pypy__' not in sys.modules,
             ext_modules=[
                 Extension(name='_ffi_backend',
-                          sources=['c/_ffi_backend.c'],
-                          libraries=['ffi']),
+                          include_dirs=include_dirs,
+                          sources=sources,
+                          libraries=libraries),
             ],
         ),
     },