Ronald Oussoren avatar Ronald Oussoren committed 4c09431

1) Allocate all closures using PyObjC_malloc_closure.

We now no longer get crashes during the unittests

2) Disable tests for poseAs: on 64-bit systems, as
posing is not supported at all there.

3) Make PyObjC_malloc_closure less wastefull with
memory (that is store more than one closure per
VM page)

There are still some test failures on 64-bit builds,
but we're almost there. The resulting test failures
are related and probably all just one real issue.

Comments (0)

Files changed (7)

pyobjc-core/Lib/objc/test/test_posing.py

 import objc.test
 import objc
+import sys
 
 # Most useful systems will at least have 'NSObject'.
 #NSObject = objc.lookUpClass('NSObject')
 BaseName = 'NSPortCoder'
 BaseClass = objc.lookUpClass(BaseName)
 
-class TestPosing(objc.test.TestCase):
-    def testPosing(self):
-        class PoseClass(BaseClass):
-            __slots__ = ()  # Don't add instance variables, not even __dict__
-            def testPosingMethod(self):
-                return u"<PoseClass instance>"
+if sys.maxint >= 2 ** 32:
+    # -poseAsClass: is not supported in 64-bit mode (the functionality is 
+    # not present in the 64-bit runtime and will never be because it 
+    # conflicts with new functionality such as non-fragile class layouts)
+    pass
 
+else:
+    class TestPosing(objc.test.TestCase):
+        def testPosing(self):
 
-        PoseClass.poseAsClass_(BaseClass)
+            class PoseClass(BaseClass):
+                __slots__ = ()  # Don't add instance variables, not even __dict__
+                def testPosingMethod(self):
+                    return u"<PoseClass instance>"
 
-        # BaseClass still refers to the old class, if we look it up again
-        # we get to see the new value. There's not much we can do about that.
-        obj = objc.lookUpClass(BaseName).new()
-        self.assertEquals(obj.testPosingMethod(), u"<PoseClass instance>")
 
-        # XXX: next assertion fails because the runtime seems to copy the
-        # original class.
-        #self.assert_(isinstance(obj, PoseClass))
-        self.assertNotEquals(BaseClass.__name__, BaseName)
-        self.assertEquals(PoseClass.__name__, BaseName)
-        del obj
+            PoseClass.poseAsClass_(BaseClass)
+
+            # BaseClass still refers to the old class, if we look it up again
+            # we get to see the new value. There's not much we can do about that.
+            obj = objc.lookUpClass(BaseName).new()
+            self.assertEquals(obj.testPosingMethod(), u"<PoseClass instance>")
+
+            # XXX: next assertion fails because the runtime seems to copy the
+            # original class.
+            #self.assert_(isinstance(obj, PoseClass))
+            self.assertNotEquals(BaseClass.__name__, BaseName)
+            self.assertEquals(PoseClass.__name__, BaseName)
+            del obj
 
 
 

pyobjc-core/Modules/objc/closure_pool.m

 /*
- * First a trivial implementation, if this works out I'll write a more memory-efficient one
+ * A simple allocator for closure. This assumes that most closures are kept
+ * alive forever and we therefore don't have to return storage to the OS.
  */
 #include "pyobjc.h"
 
 #include <sys/mman.h>
 
+typedef struct freelist {
+	struct freelist* next;
+} freelist;
+
+static freelist* closure_freelist = NULL;
+
+
+static freelist* allocate_block(void)
+{
+
+	/* Allocate ffi_closure in groups of 10 VM pages */
+#define BLOCKSIZE ((PAGE_SIZE*10)/sizeof(ffi_closure*))
+
+	freelist* newblock = mmap(NULL, BLOCKSIZE * sizeof(ffi_closure),
+		PROT_READ|PROT_WRITE|PROT_EXEC,
+		MAP_PRIVATE|MAP_ANON, -1, 0);
+	size_t i;
+
+	if (newblock == (void*)-1) {
+		PyErr_NoMemory();
+		return NULL;
+	}
+	for (i = 0; i < BLOCKSIZE-1; i++) {
+		((freelist*)(((ffi_closure*)newblock)+i))->next = 
+			(freelist*)(((ffi_closure*)newblock)+(i+1));
+	}
+
+	((freelist*)(((ffi_closure*)newblock)+(BLOCKSIZE-1)))->next = NULL;
+	return newblock;
+}
+
+
+
 ffi_closure* 
 PyObjC_malloc_closure(void)
 {
-	ffi_closure* page = mmap(NULL, sizeof(ffi_closure*),
-		PROT_READ|PROT_WRITE|PROT_EXEC,
-		MAP_PRIVATE|MAP_ANON, -1, 0);
-	if (page == (void*)-1) {
-		PyErr_NoMemory();
-		return NULL;
+	if (closure_freelist == NULL) {
+		closure_freelist = allocate_block();
+		if (closure_freelist == NULL) {
+			return NULL;
+		}
 	}
-	return page;
+	ffi_closure* result = (ffi_closure*)closure_freelist;
+	closure_freelist = closure_freelist->next;
+	return result;
 }
 
 int
 PyObjC_free_closure(ffi_closure* cl)
 {
-	int rv = munmap(cl, sizeof(ffi_closure));
-	if (rv == -1) {
-		PyErr_NoMemory();
-		return -1;
-	}
+	((freelist*)cl)->next = closure_freelist;
+	closure_freelist = (freelist*)cl;
 }

pyobjc-core/Modules/objc/objc-runtime-compat.m

 
 -doesNotRecognizeSelector:(SEL)sel
 {
-	printf("--> %s\n", sel);
+	printf("--> %s\n", sel_getName(sel));
 	abort();
 }
 @end

pyobjc-core/Modules/objc/opaque-pointer.m

 		}
 	}
 
-	cl = PyMem_Malloc(sizeof(*cl));
+	cl = PyObjC_malloc_closure();
 	if (cl == NULL) {
-		PyErr_NoMemory();
 		goto error_cleanup;
 	}
 
 	to_c = (PyObjCPointerWrapper_FromPythonFunc)cl;
 	cl = NULL;
 
-	cl = PyMem_Malloc(sizeof(*cl));
+	cl = PyObjC_malloc_closure();
 	if (cl == NULL) {
-		PyErr_NoMemory();
 		goto error_cleanup;
 	}
 
 		PyMem_Free(newType);
 	}
 	if (cl) {
-		PyMem_Free(cl);
+		PyObjC_free_closure(cl);
 	}
 	if (to_c) {
-		PyMem_Free(to_c);
+		PyObjC_free_closure(to_c);
 	}
 	if (from_c) {
-		PyMem_Free(from_c);
+		PyObjC_free_closure(from_c);
 	}
 	Py_XDECREF(v);
 	Py_XDECREF(w);

pyobjc-core/Modules/objc/struct-wrapper.m

 		}
 	}
 
-	cl = malloc(sizeof(*cl));
+	cl = PyObjC_malloc_closure();
 	if (cl == NULL) {
-		PyErr_NoMemory();
 		return NULL;
 	}
 
 	rv = ffi_prep_closure(cl, init_cif, struct_init, (char*)typestr);
 	if (rv != FFI_OK) {
-		free(cl);
+		PyObjC_free_closure(cl);
 		PyErr_Format(PyExc_RuntimeError,
 			"Cannot create FFI closure: %d", rv);
 		return NULL;

pyobjc-core/NEWS.txt

 
 - PyObjC can now run in 64-bit mode.
 
-  (at least somewhat, there are still hard crashes but most of the 
-  unittests now pass).
+  (At least mostly: most unittests pass, but there are still some issues. I haven't tested
+  64-bit support beyond that, which means that there might be other issues)
+
+  This requires Leopard (OSX 10.5), earlier version of the OS don't have a 64-bit
+  Objective-C runtime at all.  This currently also requires a copy of python that was
+  build with ``MACOSX_DEPLOYMENT_TARGET=10.5``.
+
+  Note that class posing (the ``poseAsClass_`` class method) is not supported in 64-bit 
+  mode. It is also not possible to create new protocols in 64-bit code. Neither are 
+  supported by the 64-bit runtime APIs (that is, it is a restriction in Apple's 
+  Objective-C 2.0 runtime).
 
 - BUGFIX: It is now possible to override ``respondsToSelector:`` in Python.
 

pyobjc-core/setup.py

     ## on i386 systems when a method returns a struct that isn't returned
     ## in registers. 
     #"-O0",
-    "-O1",
+    "-O0",
     #"-O2",
     #"-O3",
 
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.