Hi Stefan,
sorry for the delay, I was in the US for parental leave. I had hoped to
find some time to work on the promised patch over there, but I fell short.
On 04/22/2013 02:28 PM, Stefan Behnel wrote:
> Please do. Just ask back on this list if there's anything that's not
> clear to you.
I attached my current (trivial) patch. Currently I only support a decorator
@cython.noclear
cdef class ...
to inhibit generation of tp_clear.
Before I continue with this approach I am wondering about the API. Is
noclear usable as a name? I think not because nobody will know what it
is talking about.
But I do not know how to press the information "do not generate the
tp_clear slot which will clear references to break reference cycles
during GC" into a short name.
Perhaps something along the lines of "@cython.gc_keep_references" or
"@cython.exempt_from_gc_cycle_breaker"!?
How should the decorator to completely disable GC for a class be called?
@cython.nogc? @cython.refcounted (because the class will only support
cleanup via reference counting)?
Any input appreciated.
Greetings, Torsten
--
DYNAmore Gesellschaft fuer Ingenieurdienstleistungen mbH
Torsten Landschoff
Office Dresden
Tel: +49-(0)351-4519587
Fax: +49-(0)351-4519561
mailto:[email protected]
http://www.dynamore.de
DYNAmore Gesellschaft für FEM Ingenieurdienstleistungen mbH
Registration court: Stuttgart, HRB 733694
Managing director: Prof. Dr. Karl Schweizerhof, Dipl.-Math. Ulrich Franz
>From 48cc65e658ac8f831661c5a880cf43154c919011 Mon Sep 17 00:00:00 2001
From: Torsten Landschoff <[email protected]>
Date: Wed, 10 Jul 2013 23:58:08 +0200
Subject: [PATCH] Inhibit tp_clear generation via class decorator.
---
Cython/Compiler/ModuleNode.py | 3 +-
Cython/Compiler/Options.py | 3 ++
Cython/Compiler/TypeSlots.py | 9 ++++-
tests/run/noclear.h | 64 ++++++++++++++++++++++++++++++++
tests/run/noclear.pyx | 82 +++++++++++++++++++++++++++++++++++++++++
5 files changed, 159 insertions(+), 2 deletions(-)
create mode 100644 tests/run/noclear.h
create mode 100644 tests/run/noclear.pyx
diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py
index ce6c304..791fc8e 100644
--- a/Cython/Compiler/ModuleNode.py
+++ b/Cython/Compiler/ModuleNode.py
@@ -1009,7 +1009,8 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
self.generate_dealloc_function(scope, code)
if scope.needs_gc():
self.generate_traverse_function(scope, code, entry)
- self.generate_clear_function(scope, code, entry)
+ if not scope.directives.get('noclear', False):
+ self.generate_clear_function(scope, code, entry)
if scope.defines_any(["__getitem__"]):
self.generate_getitem_int_function(scope, code)
if scope.defines_any(["__setitem__", "__delitem__"]):
diff --git a/Cython/Compiler/Options.py b/Cython/Compiler/Options.py
index 795ae86..f09e3e3 100644
--- a/Cython/Compiler/Options.py
+++ b/Cython/Compiler/Options.py
@@ -96,6 +96,7 @@ directive_defaults = {
'final' : False,
'internal' : False,
'profile': False,
+ 'noclear': False,
'linetrace': False,
'infer_types': None,
'infer_types.verbose': False,
@@ -201,6 +202,7 @@ directive_types = {
'ccall' : None,
'cclass' : None,
'returns' : type,
+ 'noclear': bool,
'set_initial_path': str,
'freelist': int,
'c_string_type': one_of('bytes', 'str', 'unicode'),
@@ -214,6 +216,7 @@ for key, val in directive_defaults.items():
directive_scopes = { # defaults to available everywhere
# 'module', 'function', 'class', 'with statement'
'final' : ('cclass', 'function'),
+ 'noclear' : ('cclass',),
'internal' : ('cclass',),
'autotestdict' : ('module',),
'autotestdict.all' : ('module',),
diff --git a/Cython/Compiler/TypeSlots.py b/Cython/Compiler/TypeSlots.py
index 0413e4a..7bc9fb8 100644
--- a/Cython/Compiler/TypeSlots.py
+++ b/Cython/Compiler/TypeSlots.py
@@ -331,6 +331,13 @@ class GCDependentSlot(InternalMethodSlot):
return InternalMethodSlot.slot_code(self, scope)
+class GCClearReferencesSlot(GCDependentSlot):
+ def slot_code(self, scope):
+ if scope.directives.get('noclear', False):
+ return "0"
+ return GCDependentSlot.slot_code(self, scope)
+
+
class ConstructorSlot(InternalMethodSlot):
# Descriptor for tp_new and tp_dealloc.
@@ -753,7 +760,7 @@ slot_table = (
DocStringSlot("tp_doc"),
GCDependentSlot("tp_traverse"),
- GCDependentSlot("tp_clear"),
+ GCClearReferencesSlot("tp_clear"),
# Later -- synthesize a method to split into separate ops?
MethodSlot(richcmpfunc, "tp_richcompare", "__richcmp__", inherited=False), # Py3 checks for __hash__
diff --git a/tests/run/noclear.h b/tests/run/noclear.h
new file mode 100644
index 0000000..144762c
--- /dev/null
+++ b/tests/run/noclear.h
@@ -0,0 +1,64 @@
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+typedef struct {
+ int magic;
+ int usage_counter;
+} fakep11_context;
+
+typedef struct {
+ char *name;
+} fakep11_slot;
+
+fakep11_context *fakep11_ctx_new(void);
+fakep11_slot *fakep11_enumerate(fakep11_context *ctx, unsigned int *nslots);
+void fakep11_release_all(fakep11_context *ctx, fakep11_slot *slots, unsigned int nslots);
+void fakep11_ctx_free(fakep11_context *ctx);
+
+fakep11_context *fakep11_ctx_new(void)
+{
+ fakep11_context *p = (fakep11_context *) calloc(1, sizeof(*p));
+ p->magic = 0x37e6712f;
+ return p;
+}
+
+fakep11_slot *fakep11_enumerate(fakep11_context *ctx, unsigned int *nslots)
+{
+ int NSLOTS = 13;
+ int i;
+ char buf[128];
+ fakep11_slot *slots = (fakep11_slot *) calloc(NSLOTS, sizeof(*slots));
+
+ for (i = 0; i < NSLOTS; i++) {
+ sprintf(buf, "Slot #%d", i);
+ slots[i].name = strdup(buf);
+ }
+ ++ctx->usage_counter;
+ *nslots = NSLOTS;
+ return slots;
+}
+
+void fakep11_release_all(fakep11_context *ctx, fakep11_slot *slots, unsigned int nslots)
+{
+ /* This simulates the crash that is happening in the real p11wrap. I expected that
+ this function will not even be called due to a null pointer dereference via the
+ cleared Slots.context attribute but in fact Slots.context becomes None in tp_clear
+ so that the imp pointer can still be read. */
+ if (ctx->magic != 0x37e6712f) {
+ fprintf(stderr, "Bad magic number %x, aborting.\n", ctx->magic);
+ exit(1);
+ }
+ free(slots);
+ --ctx->usage_counter;
+}
+
+void fakep11_ctx_free(fakep11_context *ctx)
+{
+ if (ctx->magic != 0x37e6712f) {
+ fprintf(stderr, "Bad magic number %x, aborting.\n", ctx->magic);
+ exit(1);
+ }
+ ctx->magic = 0xdeadbeaf;
+ free(ctx);
+}
diff --git a/tests/run/noclear.pyx b/tests/run/noclear.pyx
new file mode 100644
index 0000000..b353288
--- /dev/null
+++ b/tests/run/noclear.pyx
@@ -0,0 +1,82 @@
+cimport cython
+
+
+cdef extern from "noclear.h":
+ ctypedef struct fakep11_context:
+ int usage_counter
+
+ ctypedef struct fakep11_slot:
+ char *name
+
+ fakep11_context *fakep11_ctx_new()
+ fakep11_slot *fakep11_enumerate(fakep11_context *ctx, unsigned int *nslots)
+ void fakep11_release_all(fakep11_context *ctx, fakep11_slot *slots, unsigned int nslots)
+ void fakep11_ctx_free(fakep11_context *ctx)
+
+
+cdef class Context:
+ cdef fakep11_context *imp
+
+ def __cinit__(self):
+ self.imp = fakep11_ctx_new()
+
+ def __dealloc__(self):
+ fakep11_ctx_free(self.imp)
+ self.imp = NULL
+
+ def slots(self):
+ return Slots(self).as_list()
+
+ property usage_counter:
+ def __get__(self):
+ return self.imp.usage_counter
+
+
[email protected]
[email protected]
+cdef class Slots:
+
+ cdef Context context
+ cdef unsigned int nslots
+ cdef fakep11_slot *slots
+
+ def __cinit__(self, Context context):
+ self.context = context
+ self.slots = fakep11_enumerate(context.imp, &self.nslots)
+
+ def __dealloc__(self):
+ print "Slots dealloc"
+ fakep11_release_all(self.context.imp, self.slots, self.nslots)
+
+ def as_list(self):
+ l = [None] * self.nslots
+ for x in range(self.nslots):
+ l[x] = Slot().bind(self, &self.slots[x])
+ return l
+
+cdef class Slot:
+ cdef Slots slots
+ cdef fakep11_slot *imp
+
+ def __cinit__(self):
+ self.slots = None
+ self.imp = NULL
+
+ cdef Slot bind(self, Slots slots, fakep11_slot *imp):
+ self.slots = slots
+ self.imp = imp
+ return self
+
+ property name:
+ def __get__(self):
+ return self.imp.name
+
+
+def crashes():
+ """
+ >>> ctx = Context()
+ >>> slots = ctx.slots()
+ >>> a = {"slot": slots[0]}
+ >>> b = {"slot": slots[1], "cycle": a}
+ >>> a["cycle"] = b
+ """
--
1.7.10.362.g010b2
_______________________________________________
cython-devel mailing list
[email protected]
http://mail.python.org/mailman/listinfo/cython-devel