Author: Armin Rigo <[email protected]>
Branch:
Changeset: r382:49d86c30469e
Date: 2012-06-16 08:10 +0200
http://bitbucket.org/cffi/cffi/changeset/49d86c30469e/
Log: Increase portability among UNIX systems with a bit of code from
ctypes.
diff --git a/c/_ffi_backend.c b/c/_ffi_backend.c
--- a/c/_ffi_backend.c
+++ b/c/_ffi_backend.c
@@ -10,6 +10,8 @@
#include <ffi.h>
#include <sys/mman.h>
+#include "malloc_closure.h"
+
/************************************************************/
/* base type flag: exactly one of the following: */
@@ -924,8 +926,10 @@
{
if (cd->c_type->ct_flags & CT_FUNCTIONPTR) {
/* a callback */
- PyObject *args = (PyObject *)((ffi_closure *)cd->c_data)->user_data;
+ ffi_closure *closure = (ffi_closure *)cd->c_data;
+ PyObject *args = (PyObject *)(closure->user_data);
Py_XDECREF(args);
+ ffi_closure_free(closure);
}
cdata_dealloc(cd);
}
@@ -2889,12 +2893,10 @@
static PyObject *b_callback(PyObject *self, PyObject *args)
{
CTypeDescrObject *ct;
- CDataObject_with_alignment *cda;
+ CDataObject *cd;
PyObject *ob;
cif_description_t *cif_descr;
- int dataoffset, datasize, pagesize;
ffi_closure *closure;
- char *rawaddr;
if (!PyArg_ParseTuple(args, "O!O:callback", &CTypeDescr_Type, &ct, &ob))
return NULL;
@@ -2911,16 +2913,14 @@
return NULL;
}
- dataoffset = offsetof(CDataObject_with_alignment, alignment);
- datasize = sizeof(ffi_closure);
- cda = (CDataObject_with_alignment *)PyObject_Malloc(dataoffset + datasize);
- if (PyObject_Init((PyObject *)cda, &CDataOwning_Type) == NULL)
- return NULL;
- closure = (ffi_closure *)&cda->alignment;
-
+ closure = ffi_closure_alloc();
+
+ cd = PyObject_New(CDataObject, &CDataOwning_Type);
+ if (cd == NULL)
+ goto error;
Py_INCREF(ct);
- cda->head.c_type = ct;
- cda->head.c_data = (char *)closure;
+ cd->c_type = ct;
+ cd->c_data = (char *)closure;
cif_descr = (cif_description_t *)ct->ct_extra;
if (cif_descr == NULL) {
@@ -2934,25 +2934,16 @@
"libffi failed to build this callback");
goto error;
}
- /* make the memory containing 'closure' executable */
- pagesize = sysconf(_SC_PAGE_SIZE);
- if (pagesize == -1)
- pagesize = 4096;
- rawaddr = (char *)closure;
- rawaddr -= ((Py_intptr_t)rawaddr) & (pagesize-1);
- if (mprotect(rawaddr, (((char*)(closure + 1))) - rawaddr,
- PROT_READ | PROT_WRITE | PROT_EXEC) < 0) {
- PyErr_SetFromErrno(PyExc_OSError);
- goto error;
- }
-
assert(closure->user_data == args);
Py_INCREF(args); /* capture the tuple (CTypeDescr, Python callable) */
- return (PyObject *)cda;
+ return (PyObject *)cd;
error:
closure->user_data = NULL;
- Py_DECREF(cda);
+ if (cd == NULL)
+ ffi_closure_free(closure);
+ else
+ Py_DECREF(cd);
return NULL;
}
diff --git a/c/malloc_closure.h b/c/malloc_closure.h
new file mode 100644
--- /dev/null
+++ b/c/malloc_closure.h
@@ -0,0 +1,123 @@
+/*
+ * This file is from CPython's Modules/_ctypes/malloc_closure.c
+ * and has received some edits.
+ */
+
+#include <ffi.h>
+#ifdef MS_WIN32
+#include <windows.h>
+#else
+#include <sys/mman.h>
+#include <unistd.h>
+# if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
+# define MAP_ANONYMOUS MAP_ANON
+# endif
+#endif
+
+/* 'allocate_num_pages' is dynamically adjusted starting from one
+ page. It grows by a factor of PAGE_ALLOCATION_GROWTH_RATE. This is
+ meant to handle both the common case of not needing a lot of pages,
+ and the rare case of needing many of them. Systems in general have a
+ limit of how many mmap'd blocks can be open.
+*/
+
+#define PAGE_ALLOCATION_GROWTH_RATE 1.3
+
+static Py_ssize_t allocate_num_pages = 0;
+
+/* #define MALLOC_CLOSURE_DEBUG */ /* enable for some debugging output */
+
+/******************************************************************/
+
+union mmaped_block {
+ ffi_closure closure;
+ union mmaped_block *next;
+};
+
+static union mmaped_block *free_list = 0;
+static Py_ssize_t _pagesize = 0;
+
+static void more_core(void)
+{
+ union mmaped_block *item;
+ Py_ssize_t count, i;
+
+/* determine the pagesize */
+#ifdef MS_WIN32
+ if (!_pagesize) {
+ SYSTEM_INFO systeminfo;
+ GetSystemInfo(&systeminfo);
+ _pagesize = systeminfo.dwPageSize;
+ }
+#else
+ if (!_pagesize) {
+#ifdef _SC_PAGESIZE
+ _pagesize = sysconf(_SC_PAGESIZE);
+#else
+ _pagesize = getpagesize();
+#endif
+ }
+#endif
+ if (_pagesize <= 0)
+ _pagesize = 4096;
+
+ /* bump 'allocate_num_pages' */
+ allocate_num_pages = 1 + (
+ (Py_ssize_t)(allocate_num_pages * PAGE_ALLOCATION_GROWTH_RATE));
+
+ /* calculate the number of mmaped_blocks to allocate */
+ count = (allocate_num_pages * _pagesize) / sizeof(union mmaped_block);
+
+ /* allocate a memory block */
+#ifdef MS_WIN32
+ item = (union mmaped_block *)VirtualAlloc(NULL,
+ count * sizeof(union mmaped_block),
+ MEM_COMMIT,
+ PAGE_EXECUTE_READWRITE);
+ if (item == NULL)
+ return;
+#else
+ item = (union mmaped_block *)mmap(NULL,
+ allocate_num_pages * _pagesize,
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ -1,
+ 0);
+ if (item == (void *)MAP_FAILED)
+ return;
+#endif
+
+#ifdef MALLOC_CLOSURE_DEBUG
+ printf("block at %p allocated (%ld bytes), %ld mmaped_blocks\n",
+ item, (long)(allocate_num_pages * _pagesize), (long)count);
+#endif
+ /* put them into the free list */
+ for (i = 0; i < count; ++i) {
+ item->next = free_list;
+ free_list = item;
+ ++item;
+ }
+}
+
+/******************************************************************/
+
+/* put the item back into the free list */
+static void ffi_closure_free(ffi_closure *p)
+{
+ union mmaped_block *item = (union mmaped_block *)p;
+ item->next = free_list;
+ free_list = item;
+}
+
+/* return one item from the free list, allocating more if needed */
+static ffi_closure *ffi_closure_alloc(void)
+{
+ union mmaped_block *item;
+ if (!free_list)
+ more_core();
+ if (!free_list)
+ return NULL;
+ item = free_list;
+ free_list = item->next;
+ return &item->closure;
+}
diff --git a/c/test_c.py b/c/test_c.py
--- a/c/test_c.py
+++ b/c/test_c.py
@@ -647,6 +647,18 @@
f = make_callback()
assert f(-142) == -141
+def test_a_lot_of_callbacks():
+ BInt = new_primitive_type("int")
+ def make_callback(m):
+ def cb(n):
+ return n + m
+ BFunc = new_function_type((BInt,), BInt, False)
+ return callback(BFunc, cb) # 'cb' and 'BFunc' go out of scope
+ #
+ flist = [make_callback(i) for i in range(10000)]
+ for i, f in enumerate(flist):
+ assert f(-142) == -142 + i
+
def test_enum_type():
BEnum = new_enum_type("foo", (), ())
assert repr(BEnum) == "<ctype 'enum foo'>"
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit