Commit 83047799 authored by gsamain's avatar gsamain Committed by Xavier Thompson

Gwenael: Remove nogil extension and introduce refcounted cypclass

parent 6521ad76
......@@ -2039,7 +2039,7 @@ class CCodeWriter(object):
entry.cname, dll_linkage=dll_linkage))
if entry.init is not None:
self.put_safe(" = %s" % entry.type.literal_code(entry.init))
elif entry.type.is_pyobject:
elif entry.type.is_pyobject or entry.type.is_cyp_class:
self.put(" = NULL")
self.putln(";")
......@@ -2050,8 +2050,8 @@ class CCodeWriter(object):
self.putln("%s = NULL;" % decl)
elif type.is_memoryviewslice:
self.putln("%s = %s;" % (decl, type.literal_code(type.default_value)))
elif type.is_struct and type.is_extension_type and type.nogil:
self.putln("%s;" % decl)
elif type.is_cyp_class:
self.putln("%s = NULL;" % decl)
else:
self.putln("%s%s;" % (static and "static " or "", decl))
......@@ -2084,6 +2084,29 @@ class CCodeWriter(object):
return ''
return '%s ' % ' '.join([mapper(m,m) for m in modifiers])
# CyObjects reference counting
def put_cygotref(self, cname):
self.putln("Cy_GOTREF(%s);" % cname)
def put_cygiveref(self, cname):
self.putln("Cy_GIVEREF(%s);" % cname)
def put_cyxgiveref(self, cname):
self.putln("Cy_XGIVEREF(%s);" % cname)
def put_cyxgotref(self, cname):
self.putln("Cy_XGOTREF(%s);" % cname)
def put_cyincref(self, cname):
self.putln("Cy_INCREF(%s);" % cname)
def put_cydecref(self, cname):
self.putln("Cy_DECREF(%s);" % cname)
def put_cyxdecref(self, cname):
self.putln("Cy_XDECREF(%s);" % cname)
# Python objects and reference counting
def entry_as_pyobject(self, entry):
......
......@@ -782,7 +782,10 @@ class ExprNode(Node):
If the result is in a temp, it is already a new reference.
"""
if not self.result_in_temp():
code.put_incref(self.result(), self.ctype())
if self.type.is_cyp_class and "NULL" not in self.result():
code.put_cyincref(self.result())
else:
code.put_incref(self.result(), self.ctype())
def make_owned_memoryviewslice(self, code):
"""
......@@ -824,7 +827,10 @@ class ExprNode(Node):
self.generate_subexpr_disposal_code(code)
self.free_subexpr_temps(code)
if self.result():
code.put_decref_clear(self.result(), self.ctype(),
if self.type.is_cyp_class:
code.put_cyxdecref(self.result())
else:
code.put_decref_clear(self.result(), self.ctype(),
have_gil=not self.in_nogil_context)
if self.has_temp_moved:
code.globalstate.use_utility_code(
......@@ -847,6 +853,8 @@ class ExprNode(Node):
self.free_subexpr_temps(code)
elif self.type.is_pyobject:
code.putln("%s = 0;" % self.result())
elif self.type.is_cyp_class:
code.putln("%s = 0;" % self.result())
elif self.type.is_memoryviewslice:
code.putln("%s.memview = NULL;" % self.result())
code.putln("%s.data = NULL;" % self.result())
......@@ -1837,7 +1845,10 @@ class ImagNode(AtomicExprNode):
self.result(),
float(self.value),
code.error_goto_if_null(self.result(), self.pos)))
self.generate_gotref(code)
if self.type.is_cyp_class:
code.put_cygotref(self.result())
else:
self.generate_gotref(code)
class NewExprNode(AtomicExprNode):
......@@ -2319,8 +2330,9 @@ class NameNode(AtomicExprNode):
code.error_goto_if_null(self.result(), self.pos)))
self.generate_gotref(code)
elif entry.is_local and isinstance(entry.type, PyrexTypes.CythonExtensionType):
pass
elif entry.is_local and entry.type.is_cyp_class:
code.put_cygotref(self.result())
#pass
# code.putln(entry.cname)
elif entry.is_local or entry.in_closure or entry.from_closure or entry.type.is_memoryviewslice:
# Raise UnboundLocalError for objects and memoryviewslices
......@@ -2426,6 +2438,10 @@ class NameNode(AtomicExprNode):
assigned = False
if is_external_ref:
rhs.generate_giveref(code)
elif self.type.is_cyp_class:
code.put_cyxdecref(self.result())
if isinstance(rhs, NameNode):
rhs.make_owned_reference(code)
if not self.type.is_memoryviewslice:
if not assigned:
if overloaded_assignment:
......@@ -2537,6 +2553,22 @@ class NameNode(AtomicExprNode):
else:
code.put_decref_clear(self.result(), self.ctype(),
have_gil=not self.nogil)
elif self.entry.type.is_cyp_class:
if not self.cf_is_null:
if self.cf_maybe_null and not ignore_nonexisting:
code.put_error_if_unbound(self.pos, self.entry)
if self.entry.in_closure:
# generator
if ignore_nonexisting and self.cf_maybe_null:
code.put_cyxgotref(self.result())
else:
code.put_cygotref(self.result())
if ignore_nonexisting and self.cf_maybe_null:
code.put_cyxdecref(self.result())
else:
code.put_cydecref(self.result())
code.putln('%s = NULL;' % self.result())
else:
error(self.pos, "Deletion of C names not supported")
......@@ -4135,6 +4167,8 @@ class IndexNode(_IndexingBaseNode):
code.error_goto_if(error_check % self.result(), self.pos)))
if self.type.is_pyobject:
self.generate_gotref(code)
elif self.type.is_cyp_class:
code.put_cygotref(self.result())
def generate_setitem_code(self, value_code, code):
if self.index.type.is_int:
......@@ -5555,18 +5589,6 @@ class CallNode(ExprNode):
self.analyse_c_function_call(env)
self.type = type
return True
elif type and type.is_struct and type.nogil:
args, kwds = self.explicit_args_kwds()
items = []
for arg, member in zip(args, type.scope.var_entries):
items.append(DictItemNode(pos=arg.pos, key=StringNode(pos=arg.pos, value=member.name), value=arg))
if kwds:
items += kwds.key_value_pairs
self.key_value_pairs = items
self.__class__ = DictNode
self.analyse_types(env) # FIXME
self.coerce_to(type, env)
return True
def is_lvalue(self):
return self.type.is_reference
......@@ -6069,6 +6091,8 @@ class SimpleCallNode(CallNode):
code.putln("%s%s; %s" % (lhs, rhs, goto_error))
if self.type.is_pyobject and self.result():
self.generate_gotref(code)
elif self.type.is_cyp_class and self.result():
code.put_cygotref(self.result())
if self.has_optional_args:
code.funcstate.release_temp(self.opt_arg_struct)
......@@ -7073,6 +7097,8 @@ class AttributeNode(ExprNode):
self.op = "->"
elif obj_type.is_reference and obj_type.is_fake_reference:
self.op = "->"
elif obj_type.is_cyp_class:
self.op = "->"
else:
self.op = "."
if obj_type.has_attributes:
......@@ -7223,12 +7249,9 @@ class AttributeNode(ExprNode):
# (AnalyseExpressionsTransform)
self.member = self.entry.cname
if obj.type.nogil:
return "%s" % self.entry.func_cname
else:
return "((struct %s *)%s%s%s)->%s" % (
obj.type.vtabstruct_cname, obj_code, self.op,
obj.type.vtabslot_cname, self.member)
return "((struct %s *)%s%s%s)->%s" % (
obj.type.vtabstruct_cname, obj_code, self.op,
obj.type.vtabslot_cname, self.member)
elif self.result_is_used:
return self.member
# Generating no code at all for unused access to optimised builtin
......@@ -7329,6 +7352,11 @@ class AttributeNode(ExprNode):
from . import MemoryView
MemoryView.put_assign_to_memviewslice(
select_code, rhs, rhs.result(), self.type, code)
elif self.type.is_cyp_class:
rhs.make_owned_reference(code)
code.put_cygiveref(rhs.result())
code.put_cygotref(select_code)
code.put_cyxdecref(select_code)
if not self.type.is_memoryviewslice:
code.putln(
......@@ -8827,8 +8855,6 @@ class DictNode(ExprNode):
# pairs are evaluated and used one at a time.
code.mark_pos(self.pos)
self.allocate_temp_result(code)
if hasattr(self.type, 'nogil') and self.type.nogil:
code.putln("%s = (struct %s *)malloc(sizeof(struct %s));" % (self.result(), self.type.objstruct_cname, self.type.objstruct_cname))
is_dict = self.type.is_pyobject
if is_dict:
......@@ -8886,11 +8912,6 @@ class DictNode(ExprNode):
code.putln('}')
if self.exclude_null_values:
code.putln('}')
elif self.type.nogil:
code.putln("%s->%s = %s;" % (
self.result(),
item.key.value,
item.value.result()))
else:
code.putln("%s.%s = %s;" % (
self.result(),
......@@ -13519,7 +13540,10 @@ class CoerceToTempNode(CoercionNode):
self.result(), self.arg.result_as(self.ctype())))
if self.use_managed_ref:
if not self.type.is_memoryviewslice:
code.put_incref(self.result(), self.ctype())
if self.type.is_cyp_class:
code.put_cyincref(self.result())
else:
code.put_incref(self.result(), self.ctype())
else:
code.put_incref_memoryviewslice(self.result(), self.type,
have_gil=not self.in_nogil_context)
......
......@@ -6,7 +6,7 @@ from __future__ import absolute_import
import cython
cython.declare(Naming=object, Options=object, PyrexTypes=object, TypeSlots=object,
error=object, warning=object, py_object_type=object, UtilityCode=object,
error=object, warning=object, py_object_type=object, cy_object_type=object, UtilityCode=object,
EncodedString=object, re=object)
from collections import defaultdict
......@@ -28,7 +28,7 @@ from . import PyrexTypes
from . import Pythran
from .Errors import error, warning
from .PyrexTypes import py_object_type
from .PyrexTypes import py_object_type, cy_object_type
from ..Utils import open_new_file, replace_suffix, decode_filename, build_hex_version
from .Code import UtilityCode, IncludeCode
from .StringEncoding import EncodedString, encoded_string_or_bytes_literal
......@@ -980,17 +980,21 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
# Just let everything be public.
code.put("struct %s" % type.cname)
if type.base_classes:
base_class_decl = ", public ".join(
[base_class.empty_declaration_code() for base_class in type.base_classes])
base_class_list = [base_class.empty_declaration_code() for base_class in type.base_classes]
if type.is_cyp_class and type.base_classes[-1] is cy_object_type:
base_class_list[-1] = "virtual " + base_class_list[-1]
base_class_decl = ", public ".join(base_class_list)
code.put(" : public %s" % base_class_decl)
code.putln(" {")
self.generate_type_header_code(scope.type_entries, code)
self.generate_cyp_class_wrapper_definitions(scope.sue_entries, code)
py_attrs = [e for e in scope.entries.values()
if e.type.is_pyobject and not e.is_inherited]
has_virtual_methods = False
constructor = None
destructor = None
for attr in scope.var_entries:
cname = attr.cname
if attr.type.is_cfunction and attr.type.is_static_method:
code.put("static ")
elif attr.name == "<init>":
......@@ -1001,7 +1005,9 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
elif attr.type.is_cfunction:
code.put("virtual ")
has_virtual_methods = True
code.putln("%s;" % attr.type.declaration_code(attr.cname))
elif attr.type.is_cyp_class:
cname = "%s = NULL" % cname
code.putln("%s;" % attr.type.declaration_code(cname))
is_implementing = 'init_module' in code.globalstate.parts
def generate_cpp_constructor_code(arg_decls, arg_names, is_implementing, py_attrs, constructor):
......@@ -1084,6 +1090,10 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.putln("%s& operator=(const %s& __Pyx_other);" % (type.cname, type.cname))
code.putln("};")
if type.is_cyp_class:
code.globalstate.use_utility_code(
UtilityCode.load("CyObjects", "CyObjects.cpp", proto_block="utility_code_proto_before_types"))
def generate_enum_definition(self, entry, code):
code.mark_pos(entry.pos)
type = entry.type
......@@ -1197,7 +1207,6 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
self.sue_header_footer(type, "struct", type.objstruct_cname)
code.putln(header)
base_type = type.base_type
nogil = type.nogil
if base_type:
basestruct_cname = base_type.objstruct_cname
if basestruct_cname == "PyTypeObject":
......@@ -1208,16 +1217,6 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
("struct ", "")[base_type.typedef_flag],
basestruct_cname,
Naming.obj_base_cname))
elif nogil:
# Extension type with nogil keyword indicate it is a CPython-free struct
code.globalstate.use_utility_code(
UtilityCode.load_cached("CythonReferenceCounting", "ObjectHandling.c"))
code.putln(
"// nogil"
)
code.putln(
"int ob_refcnt;" # "CyObject_HEAD;" Sometimes the CythonReferenceCounting was put after the nogil extension declaration, WTF!!!
)
else:
code.putln(
"PyObject_HEAD")
......
......@@ -7,7 +7,7 @@ from __future__ import absolute_import
import cython
cython.declare(sys=object, os=object, copy=object,
Builtin=object, error=object, warning=object, Naming=object, PyrexTypes=object,
py_object_type=object, ModuleScope=object, LocalScope=object, ClosureScope=object,
py_object_type=object, cy_object_type=object, ModuleScope=object, LocalScope=object, ClosureScope=object,
StructOrUnionScope=object, PyClassScope=object,
CppClassScope=object, UtilityCode=object, EncodedString=object,
error_type=object, _py_int_types=object)
......@@ -20,7 +20,7 @@ from .Errors import error, warning, InternalError, CompileError
from . import Naming
from . import PyrexTypes
from . import TypeSlots
from .PyrexTypes import py_object_type, error_type
from .PyrexTypes import py_object_type, cy_object_type, error_type
from .Symtab import (ModuleScope, LocalScope, ClosureScope, PropertyScope,
StructOrUnionScope, PyClassScope, CppClassScope, TemplateScope,
punycodify_name)
......@@ -956,7 +956,10 @@ class CArgDeclNode(Node):
default.make_owned_reference(code)
result = default.result() if overloaded_assignment else default.result_as(self.type)
code.putln("%s = %s;" % (target, result))
code.put_giveref(default.result(), self.type)
if self.type.is_cyp_class:
code.put_cygiveref(default.result())
else:
code.put_giveref(default.result(), self.type)
default.generate_post_assignment_code(code)
default.free_temps(code)
......@@ -1008,6 +1011,9 @@ class CSimpleBaseTypeNode(CBaseTypeNode):
error(self.pos, "Unrecognised type modifier combination")
elif self.name == "object" and not self.module_path:
type = py_object_type
elif self.name == "cyobject":
type = cy_object_type
self.arg_name = EncodedString(self.name)
elif self.name is None:
if self.is_self_arg and env.is_c_class_scope:
#print "CSimpleBaseTypeNode.analyse: defaulting to parent type" ###
......@@ -1474,7 +1480,7 @@ class CppClassNode(CStructOrUnionDefNode, BlockNode):
error(self.pos, "Required template parameters must precede optional template parameters.")
self.entry = env.declare_cpp_class(
self.name, None, self.pos, self.cname,
base_classes=[], visibility=self.visibility, templates=template_types)
base_classes=[], visibility=self.visibility, templates=template_types, cypclass=self.cypclass)
def analyse_declarations(self, env):
if self.templates is None:
......@@ -1491,10 +1497,24 @@ class CppClassNode(CStructOrUnionDefNode, BlockNode):
return True
else:
error(self.pos, "Base class '%s' not a struct or class." % base_class)
base_class_types = filter(base_ok, [b.analyse(scope or env) for b in self.base_classes])
base_types_list = [b.analyse(scope or env) for b in self.base_classes]
if self.cypclass:
cyobject_base = False
for base_type in base_types_list:
cyobject_base = cyobject_base or base_type is cy_object_type or base_type.is_cyp_class
if not cyobject_base:
cyobject_class = CSimpleBaseTypeNode(self.pos,
name = "cyobject", module_path = [],
is_basic_c_type = 0, signed = 0,
complex = 0, longness = 0,
is_self_arg = 0, templates = None
)
self.base_classes.append(cyobject_class)
base_types_list.append(cyobject_class.analyse(scope or env))
base_class_types = filter(base_ok, base_types_list)
self.entry = env.declare_cpp_class(
self.name, scope, self.pos,
self.cname, base_class_types, visibility=self.visibility, templates=template_types)
self.cname, base_class_types, visibility=self.visibility, templates=template_types, cypclass=self.cypclass)
if self.entry is None:
return
self.entry.is_cpp_class = 1
......@@ -1832,7 +1852,7 @@ class FuncDefNode(StatNode, BlockNode):
init = ""
return_type = self.return_type
if not return_type.is_void:
if return_type.is_pyobject:
if return_type.is_pyobject or return_type.is_cyp_class:
init = " = NULL"
elif return_type.is_memoryviewslice:
init = ' = ' + return_type.literal_code(return_type.default_value)
......@@ -1966,6 +1986,9 @@ class FuncDefNode(StatNode, BlockNode):
elif is_cdef and entry.cf_is_reassigned:
code.put_var_incref_memoryviewslice(entry,
have_gil=code.funcstate.gil_owned)
# We have to Cy_INCREF the nogil classes (ccdef'ed ones)
elif entry.type.is_cyp_class and len(entry.cf_assignments) > 1:
code.put_cyincref(entry.cname)
for entry in lenv.var_entries:
if entry.is_arg and entry.cf_is_reassigned and not entry.in_closure:
if entry.xdecref_cleanup:
......@@ -2148,13 +2171,16 @@ class FuncDefNode(StatNode, BlockNode):
if not entry.used or entry.in_closure:
continue
if entry.type.is_pyobject:
if entry.type.is_pyobject or entry.type.is_cyp_class:
if entry.is_arg and not entry.cf_is_reassigned:
continue
if entry.type.needs_refcounting:
assure_gil('success')
# FIXME ideally use entry.xdecref_cleanup but this currently isn't reliable
code.put_var_xdecref(entry, have_gil=gil_owned['success'])
if entry.type.is_cyp_class:
code.put_cyxdecref(entry.cname)
else:
# FIXME ideally use entry.xdecref_cleanup but this currently isn't reliable
code.put_var_xdecref(entry, have_gil=gil_owned['success'])
# Decref any increfed args
for entry in lenv.arg_entries:
......@@ -2170,9 +2196,13 @@ class FuncDefNode(StatNode, BlockNode):
continue
if entry.type.needs_refcounting:
assure_gil('success')
# FIXME use entry.xdecref_cleanup - del arg seems to be the problem
code.put_var_xdecref(entry, have_gil=gil_owned['success'])
if entry.type.is_cyp_class:
# We must check for NULL because it is possible to have
# NULL as a valid cypclass (with a typecast)
code.put_cyxdecref(entry.cname)
else:
# FIXME use entry.xdecref_cleanup - del arg seems to be the problem
code.put_var_xdecref(entry, have_gil=gil_owned['success'])
if self.needs_closure:
assure_gil('success')
code.put_decref(Naming.cur_scope_cname, lenv.scope_class.type)
......@@ -2185,6 +2215,9 @@ class FuncDefNode(StatNode, BlockNode):
if err_val is None and default_retval:
err_val = default_retval # FIXME: why is err_val not used?
code.put_xgiveref(Naming.retval_cname, return_type)
# We can always return a CythonExtensionType as it is nogil-compliant
if self.return_type.is_cyp_class:
code.put_cyxgiveref(Naming.retval_cname)
if self.entry.is_special and self.entry.name == "__hash__":
# Returning -1 for __hash__ is supposed to signal an error
......@@ -3376,8 +3409,6 @@ class DefNodeWrapper(FuncDefNode):
# different code types.
for arg in self.args:
if not arg.type.is_pyobject:
if arg.type is PyrexTypes.PyExtensionType and arg.type.nogil:
continue # XXX maybe here is not the correct place to put it...
if not arg.type.create_from_py_utility_code(env):
pass # will fail later
elif arg.hdr_type and not arg.hdr_type.is_pyobject:
......@@ -4908,7 +4939,6 @@ class CClassDefNode(ClassDefNode):
# doc string or None
# body StatNode or None
# entry Symtab.Entry
# nogil boolean
# base_type PyExtensionType or None
# buffer_defaults_node DictNode or None Declares defaults for a buffer
# buffer_defaults_pos
......@@ -4918,7 +4948,6 @@ class CClassDefNode(ClassDefNode):
buffer_defaults_pos = None
typedef_flag = False
api = False
nogil = False
objstruct_name = None
typeobj_name = None
check_size = None
......@@ -4963,7 +4992,6 @@ class CClassDefNode(ClassDefNode):
typedef_flag=self.typedef_flag,
check_size = self.check_size,
api=self.api,
nogil=self.nogil,
buffer_defaults=self.buffer_defaults(env),
shadow=self.shadow)
......@@ -5052,7 +5080,6 @@ class CClassDefNode(ClassDefNode):
visibility=self.visibility,
typedef_flag=self.typedef_flag,
api=self.api,
nogil=self.nogil,
buffer_defaults=self.buffer_defaults(env),
shadow=self.shadow)
......@@ -6192,8 +6219,6 @@ class DelStatNode(StatNode):
error(arg.pos, "Deletion of global C variable")
elif arg.type.is_ptr and arg.type.base_type.is_cpp_class:
self.cpp_check(env)
elif arg.type.is_struct_or_union and arg.type.nogil:
pass # del nogil extension
elif arg.type.is_cpp_class:
error(arg.pos, "Deletion of non-heap C++ object")
elif arg.is_subscript and arg.base.type is Builtin.bytearray_type:
......@@ -6222,9 +6247,7 @@ class DelStatNode(StatNode):
arg.generate_evaluation_code(code)
code.putln("delete %s;" % arg.result())
arg.generate_disposal_code(code)
elif arg.type.is_struct_or_union and hasattr(arg.type, "nogil") and arg.type.nogil:
code.putln("free(&%s);" % arg.result())
# else error reported earlier
# else error reported earlier
def annotate(self, code):
for arg in self.args:
......@@ -6341,6 +6364,9 @@ class ReturnStatNode(StatNode):
# Use specialised default handling for "return None".
value = None
if self.return_type.is_cyp_class:
code.put_cyxdecref(Naming.retval_cname)
if value:
value.generate_evaluation_code(code)
if self.return_type.is_memoryviewslice:
......
......@@ -2235,7 +2235,7 @@ def p_statement(s, ctx, first_statement = 0):
elif s.sy == 'IF':
return p_IF_statement(s, ctx)
elif s.sy == '@':
if ctx.level not in ('module', 'class', 'c_class', 'class_nogil', 'function', 'property', 'module_pxd', 'c_class_pxd', 'other'):
if ctx.level not in ('module', 'class', 'c_class', 'function', 'property', 'module_pxd', 'c_class_pxd', 'other'):
s.error('decorator not allowed here')
s.level = ctx.level
decorators = p_decorators(s)
......@@ -2249,21 +2249,20 @@ def p_statement(s, ctx, first_statement = 0):
return p_pass_statement(s, with_newline=1)
overridable = 0
nogil_flag = ctx.nogil
if s.sy == 'cdef':
cdef_flag = 1
s.next()
elif s.sy == 'cpdef':
if ctx.level == 'c_class_nogil':
s.error('cpdef statement not allowed in nogil extension type')
s.level = ctx.level
cdef_flag = 1
overridable = 1
s.next()
if cdef_flag:
if ctx.level not in ('module', 'module_pxd', 'function', 'c_class', 'c_class_nogil', 'c_class_pxd'):
if ctx.level not in ('module', 'module_pxd', 'function', 'c_class', 'c_class_pxd'):
s.error('cdef statement not allowed here')
s.level = ctx.level
node = p_cdef_statement(s, ctx(overridable=overridable))
node = p_cdef_statement(s, ctx(overridable=overridable, nogil=nogil_flag))
if decorators is not None:
tup = (Nodes.CFuncDefNode, Nodes.CVarDefNode, Nodes.CClassDefNode)
if ctx.allow_struct_enum_decorator:
......@@ -2280,8 +2279,6 @@ def p_statement(s, ctx, first_statement = 0):
# as part of a cdef class
if ('pxd' in ctx.level) and (ctx.level != 'c_class_pxd'):
s.error('def statement not allowed here')
if ctx.level == 'c_class_nogil':
s.error('def statement not allowed in nogil extension type, only cdef with nogil is allowed')
s.level = ctx.level
return p_def_statement(s, decorators)
elif s.sy == 'class':
......@@ -2292,7 +2289,7 @@ def p_statement(s, ctx, first_statement = 0):
if ctx.level not in ('module', 'module_pxd'):
s.error("include statement not allowed here")
return p_include_statement(s, ctx)
elif ctx.level in ('c_class', 'c_class_nogil') and s.sy == 'IDENT' and s.systring == 'property':
elif ctx.level == 'c_class' and s.sy == 'IDENT' and s.systring == 'property':
return p_property_decl(s)
elif s.sy == 'pass' and ctx.level != 'property':
return p_pass_statement(s, with_newline=True)
......@@ -3068,9 +3065,11 @@ def p_cdef_statement(s, ctx):
return p_cdef_extern_block(s, pos, ctx)
elif p_nogil(s):
ctx.nogil = 1
if ctx.overridable:
error(pos, "cdef blocks cannot be declared cpdef")
# if ctx.overridable:
# error(pos, "cdef blocks cannot be declared cpdef")
return p_cdef_block(s, ctx)
elif ctx.overridable and ctx.nogil:
error(pos, "nogil blocks cannot be declared cpdef")
elif s.sy == ':':
if ctx.overridable:
error(pos, "cdef blocks cannot be declared cpdef")
......@@ -3081,7 +3080,7 @@ def p_cdef_statement(s, ctx):
if ctx.overridable:
error(pos, "Extension types cannot be declared cpdef")
return p_c_class_definition(s, pos, ctx)
elif s.sy == 'IDENT' and s.systring == 'cppclass':
elif s.sy == 'IDENT' and s.systring in ('cppclass', 'cypclass'):
return p_cpp_class_definition(s, pos, ctx)
elif s.sy == 'IDENT' and s.systring in struct_enum_union:
if ctx.level not in ('module', 'module_pxd'):
......@@ -3276,7 +3275,7 @@ def p_c_modifiers(s):
return []
def p_c_func_or_var_declaration(s, pos, ctx):
cmethod_flag = ctx.level in ('c_class', 'c_class_pxd', 'c_class_nogil')
cmethod_flag = ctx.level in ('c_class', 'c_class_pxd')
modifiers = p_c_modifiers(s)
base_type = p_c_base_type(s, nonempty = 1, templates = ctx.templates)
declarator = p_c_declarator(s, ctx(modifiers=modifiers), cmethod_flag = cmethod_flag,
......@@ -3295,13 +3294,8 @@ def p_c_func_or_var_declaration(s, pos, ctx):
fatal=False)
s.next()
p_test(s) # Keep going, but ignore result.
if s.sy == 'nogil':
nogil = p_nogil(s)
s.next()
if ctx.level == 'c_class_nogil' and not nogil:
s.error("Only C function with nogil allowed in nogil extension")
if s.sy == ':':
if ctx.level not in ('module', 'c_class', 'module_pxd', 'c_class_pxd', 'cpp_class', 'c_class_nogil') and not ctx.templates:
if ctx.level not in ('module', 'c_class', 'module_pxd', 'c_class_pxd', 'cpp_class') and not ctx.templates:
s.error("C function definition not allowed here")
doc, suite = p_suite_with_docstring(s, Ctx(level='function'))
result = Nodes.CFuncDefNode(pos,
......@@ -3329,7 +3323,7 @@ def p_c_func_or_var_declaration(s, pos, ctx):
declarators.append(declarator)
doc_line = s.start_line + 1
s.expect_newline("Syntax error in C variable declaration", ignore_semicolon=True)
if ctx.level in ('c_class', 'c_class_pxd', 'c_class_nogil') and s.start_line == doc_line:
if ctx.level in ('c_class', 'c_class_pxd') and s.start_line == doc_line:
doc = p_doc_string(s)
else:
doc = None
......@@ -3530,12 +3524,9 @@ def p_c_class_definition(s, pos, ctx):
if ctx.visibility not in ('public', 'extern') and not ctx.api:
error(s.position(), "Name options only allowed for 'public', 'api', or 'extern' C class")
objstruct_name, typeobj_name, check_size = p_c_class_options(s)
nogil = p_nogil(s)
if s.sy == ':':
if ctx.level == 'module_pxd':
body_level = 'c_class_pxd'
elif nogil:
body_level = 'c_class_nogil'
else:
body_level = 'c_class'
doc, body = p_suite_with_docstring(s, Ctx(level=body_level))
......@@ -3574,8 +3565,7 @@ def p_c_class_definition(s, pos, ctx):
check_size = check_size,
in_pxd = ctx.level == 'module_pxd',
doc = doc,
body = body,
nogil = nogil or ctx.nogil)
body = body)
def p_c_class_options(s):
......@@ -3756,7 +3746,8 @@ def p_template_definition(s):
return name, required
def p_cpp_class_definition(s, pos, ctx):
# s.sy == 'cppclass'
# s.sy in ('cppclass', 'cypclass')
cypclass = s.systring == 'cypclass'
s.next()
module_path = []
class_name = p_ident(s)
......@@ -3787,7 +3778,7 @@ def p_cpp_class_definition(s, pos, ctx):
base_classes = []
if s.sy == '[':
error(s.position(), "Name options not allowed for C++ class")
nogil = p_nogil(s)
nogil = p_nogil(s) or cypclass
if s.sy == ':':
s.next()
s.expect('NEWLINE')
......@@ -3816,7 +3807,7 @@ def p_cpp_class_definition(s, pos, ctx):
visibility = ctx.visibility,
in_pxd = ctx.level == 'module_pxd',
attributes = attributes,
templates = templates)
templates = templates, cypclass=cypclass)
def p_cpp_class_attribute(s, ctx):
decorators = None
......
......@@ -245,6 +245,7 @@ class PyrexType(BaseType):
is_cfunction = 0
is_struct_or_union = 0
is_cpp_class = 0
is_cyp_class = 0
is_cpp_string = 0
is_struct = 0
is_enum = 0
......@@ -1325,215 +1326,6 @@ class PyObjectType(PyrexType):
return cname
class CythonObjectType(PyrexType):
#
# Base class for all nogil extension object types (reference-counted).
#
name = "cythonobject"
is_pyobject = 0
default_value = "0"
declaration_value = "0"
buffer_defaults = None
is_extern = False
is_subclassed = False
is_gc_simple = False
def __str__(self):
return "Cython object"
def __repr__(self):
return "<CythonObjectType>"
def can_coerce_to_pyobject(self, env):
return False
def can_coerce_from_pyobject(self, env):
return False
def default_coerced_ctype(self):
"""The default C type that this Python type coerces to, or None."""
return None
def assignable_from(self, src_type):
# except for pointers, conversion will be attempted
# return not src_type.is_ptr or src_type.is_string or src_type.is_pyunicode_ptr
return False
def declaration_code(self, entity_code,
for_display = 0, dll_linkage = None, pyrex = 0):
raise NotImplementedError("Calling declartion_code on Cython object")
if pyrex or for_display:
base_code = "object"
else:
base_code = public_decl("PyObject", dll_linkage)
entity_code = "*%s" % entity_code
return self.base_declaration_code(base_code, entity_code)
def as_pyobject(self, cname):
raise NotImplementedError("Calling as_pyobject on Cython object")
if (not self.is_complete()) or self.is_extension_type:
return "(PyObject *)" + cname
else:
return cname
def py_type_name(self):
raise NotImplementedError("Calling py_type_name on Cython object")
return "cythonobject"
def __lt__(self, other):
"""
Make sure we sort highest, as instance checking on py_type_name
('object') is always true
"""
return False
def global_init_code(self, entry, code):
raise NotImplementedError("Calling global_init_code on Cython object")
code.put_init_var_to_py_none(entry, nanny=False)
def check_for_null_code(self, cname):
raise NotImplementedError("Calling check_for_null_code on Cython object")
return cname
class CythonExtensionType(CythonObjectType):
#
# A Cython extension type with nogil flag
# TODO: This type may need big amend
#
# name string
# scope CClassScope Attribute namespace
# visibility string
# typedef_flag boolean
# base_type PyExtensionType or None
# nogil boolean
# module_name string or None Qualified name of defining module
# objstruct_cname string Name of PyObject struct
# objtypedef_cname string Name of PyObject struct typedef
# typeobj_cname string or None C code fragment referring to type object
# typeptr_cname string or None Name of pointer to external type object
# vtabslot_cname string Name of C method table member
# vtabstruct_cname string Name of C method table struct
# vtabptr_cname string Name of pointer to C method table
# vtable_cname string Name of C method table definition
# early_init boolean Whether to initialize early (as opposed to during module execution).
# defered_declarations [thunk] Used to declare class hierarchies in order
# check_size 'warn', 'error', 'ignore' What to do if tp_basicsize does not match
is_extension_type = 1
is_struct = 1
is_struct_or_union = 1
nogil = 1
is_pyobject = 0
has_attributes = 1
early_init = 1
objtypedef_cname = None
def __init__(self, name, typedef_flag, base_type, is_external=0, check_size=None):
self.name = name
self.scope = None
self.typedef_flag = typedef_flag
if base_type is not None:
base_type.is_subclassed = True
self.base_type = base_type
self.nogil = False
self.module_name = None
self.objstruct_cname = None
self.typeobj_cname = None
self.typeptr_cname = None
self.vtabslot_cname = None
self.vtabstruct_cname = None
self.vtabptr_cname = None
self.vtable_cname = None
self.is_external = is_external
self.check_size = check_size or 'warn'
self.defered_declarations = []
def set_scope(self, scope):
self.scope = scope
if scope:
scope.parent_type = self
def needs_nonecheck(self):
return True
def subtype_of_resolved_type(self, other_type):
if other_type.is_extension_type or other_type.is_builtin_type:
return self is other_type or (
self.base_type and self.base_type.subtype_of(other_type))
else:
return other_type is py_object_type
def typeobj_is_available(self):
# Do we have a pointer to the type object?
return self.typeptr_cname
def typeobj_is_imported(self):
# If we don't know the C name of the type object but we do
# know which module it's defined in, it will be imported.
return self.typeobj_cname is None and self.module_name is not None
def assignable_from(self, src_type):
if self == src_type:
return True
if isinstance(src_type, PyExtensionType):
if src_type.base_type is not None:
return self.assignable_from(src_type.base_type)
if isinstance(src_type, BuiltinObjectType):
# FIXME: This is an ugly special case that we currently
# keep supporting. It allows users to specify builtin
# types as external extension types, while keeping them
# compatible with the real builtin types. We already
# generate a warning for it. Big TODO: remove!
return (self.module_name == '__builtin__' and
self.name == src_type.name)
return False
def declaration_code(self, entity_code,
for_display = 0, dll_linkage = None, pyrex = 0, deref = 0):
if pyrex or for_display:
base_code = self.name
else:
if self.typedef_flag:
objstruct = self.objstruct_cname
else:
objstruct = "struct %s" % self.objstruct_cname
base_code = public_decl(objstruct, dll_linkage)
if deref:
assert not entity_code
else:
entity_code = "*%s" % entity_code
return self.base_declaration_code(base_code, entity_code)
def type_test_code(self, py_arg, notnone=False):
none_check = "((%s) == Py_None)" % py_arg
type_check = "likely(__Pyx_TypeTest(%s, %s))" % (
py_arg, self.typeptr_cname)
if notnone:
return type_check
else:
return "likely(%s || %s)" % (none_check, type_check)
def attributes_known(self):
return self.scope is not None
def __str__(self):
return self.name
def __repr__(self):
return "<CythonExtensionType %s%s>" % (self.scope.class_name,
("", " typedef")[self.typedef_flag])
def py_type_name(self):
if not self.module_name:
return self.name
return "__import__(%r, None, None, ['']).%s" % (self.module_name,
self.name)
builtin_types_that_cannot_create_refcycles = set([
'object', 'bool', 'int', 'long', 'float', 'complex',
'bytearray', 'bytes', 'unicode', 'str', 'basestring'
......@@ -2920,8 +2712,9 @@ class CPtrType(CPointerBaseType):
return self.base_type.pointer_assignable_from_resolved_type(other_type)
else:
return 0
if (self.base_type.is_cpp_class and other_type.is_ptr
and other_type.base_type.is_cpp_class and other_type.base_type.is_subclass(self.base_type)):
if (self.base_type.is_cpp_class and (other_type.is_ptr
and other_type.base_type.is_cpp_class and other_type.base_type.is_subclass(self.base_type)
or other_type.is_cyp_class and other_type.is_subclass(self.base_type))):
return 1
if other_type.is_array or other_type.is_ptr:
return self.base_type.is_void or self.base_type.same_as(other_type.base_type)
......@@ -3898,7 +3691,7 @@ class CppClassType(CType):
subtypes = ['templates']
def __init__(self, name, scope, cname, base_classes, templates=None, template_type=None):
def __init__(self, name, scope, cname, base_classes, templates=None, template_type=None, nogil=0):
self.name = name
self.cname = cname
self.scope = scope
......@@ -3912,6 +3705,7 @@ class CppClassType(CType):
else:
self.specializations = {}
self.is_cpp_string = cname in cpp_string_conversions
self.nogil=nogil
def use_conversion_utility(self, from_or_to):
pass
......@@ -4070,7 +3864,9 @@ class CppClassType(CType):
return self.specializations[key]
template_values = [t.specialize(values) for t in self.templates]
specialized = self.specializations[key] = \
CppClassType(self.name, None, self.cname, [], template_values, template_type=self)
CppClassType(self.name, None, self.cname, [], template_values, template_type=self)\
if not self.is_cyp_class else\
CypClassType(self.name, None, self.cname, [], template_values, template_type=self)
# Need to do these *after* self.specializations[key] is set
# to avoid infinite recursion on circular references.
specialized.base_classes = [b.specialize(values) for b in self.base_classes]
......@@ -4223,6 +4019,8 @@ class CppClassType(CType):
# Make it "nogil" if the base classes allow it.
nogil = True
for base in self.base_classes:
if base is cy_object_type:
continue
base_constructor = base.scope.lookup('<init>')
if base_constructor and not base_constructor.type.nogil:
nogil = False
......@@ -4237,6 +4035,50 @@ class CppClassType(CType):
error(pos, "C++ class must have a nullary constructor to be %s" % msg)
class CypClassType(CppClassType):
is_cyp_class = 1
def declaration_code(self, entity_code,
for_display = 0, dll_linkage = None, pyrex = 0,
template_params = None):
if entity_code:
entity_code = "*%s" % entity_code
return super(CypClassType, self).declaration_code(entity_code,
for_display=for_display, dll_linkage=dll_linkage,
pyrex=pyrex, template_params=template_params)
def check_nullary_constructor(self, pos, msg="stack allocated"):
# We are wrapping the constructor => we manipulate the object through pointers like PyExtTypes => don't care about this check
return
def cast_code(self, expr_code):
return "((%s)%s)" % (self.declaration_code(''), expr_code)
def assignable_from_resolved_type(self, other_type):
if other_type.is_ptr and other_type.base_type.is_cpp_class and other_type.base_type.is_subclass(self):
return 1
return super(CypClassType, self).assignable_from_resolved_type(other_type)
def get_constructor(self, pos):
# This is (currently) only called by new statements.
# In cypclass, it means direct memory allocation:
# regardless of __init__ presence/absence,
# new is forced to be called without any arguments,
# and doesn't call any __init__
# (the mapping from cython __init__ to c++ constructors isn't done
# for cypclass, instead a default empty constructor is declared,
# and calls to __init__ are performed by wrappers)
# As we do not care about declared __init__ in the scope,
# we just return a correct but fake entry.
from . import Symtab
init_type = CFuncType(c_void_type, [], nogil=1)
init_cname = "__unused__"
init_name = "<unused>"
init_entry = Symtab.Entry(init_name, init_cname, init_type, pos = pos)
init_entry.is_cfunction = 1
return init_entry
class TemplatePlaceholderType(CType):
def __init__(self, name, optional=False):
......@@ -4497,6 +4339,8 @@ unspecified_type = UnspecifiedType()
py_object_type = PyObjectType()
cy_object_type = CypClassType('cyobject', None, "CyObject", None)
c_void_type = CVoidType()
c_uchar_type = CIntType(0, UNSIGNED)
......
......@@ -17,7 +17,7 @@ from .Errors import warning, error, InternalError
from .StringEncoding import EncodedString
from . import Options, Naming
from . import PyrexTypes
from .PyrexTypes import py_object_type, unspecified_type
from .PyrexTypes import py_object_type, cy_object_type, unspecified_type
from .TypeSlots import (
pyfunction_signature, pymethod_signature, richcmp_special_methods,
get_special_method_signature, get_property_accessor_signature)
......@@ -628,7 +628,7 @@ class Scope(object):
def declare_cpp_class(self, name, scope,
pos, cname = None, base_classes = (),
visibility = 'extern', templates = None):
visibility = 'extern', templates = None, cypclass=0):
if cname is None:
if self.in_cinclude or (visibility != 'private'):
cname = name
......@@ -637,8 +637,12 @@ class Scope(object):
base_classes = list(base_classes)
entry = self.lookup_here(name)
if not entry:
type = PyrexTypes.CppClassType(
name, scope, cname, base_classes, templates = templates)
if cypclass:
type = PyrexTypes.CypClassType(
name, scope, cname, base_classes, templates = templates)
else:
type = PyrexTypes.CppClassType(
name, scope, cname, base_classes, templates = templates)
entry = self.declare_type(name, type, pos, cname,
visibility = visibility, defining = scope is not None)
self.sue_entries.append(entry)
......@@ -666,7 +670,7 @@ class Scope(object):
def declare_inherited_attributes(entry, base_classes):
for base_class in base_classes:
if base_class is PyrexTypes.error_type:
if base_class is PyrexTypes.error_type or base_class is PyrexTypes.cy_object_type:
continue
if base_class.scope is None:
error(pos, "Cannot inherit from incomplete type")
......@@ -1573,7 +1577,7 @@ class ModuleScope(Scope):
def declare_c_class(self, name, pos, defining=0, implementing=0,
module_name=None, base_type=None, objstruct_cname=None,
typeobj_cname=None, typeptr_cname=None, visibility='private',
typedef_flag=0, api=0, nogil=0, check_size=None,
typedef_flag=0, api=0, check_size=None,
buffer_defaults=None, shadow=0):
# If this is a non-extern typedef class, expose the typedef, but use
# the non-typedef struct internally to avoid needing forward
......@@ -1606,15 +1610,8 @@ class ModuleScope(Scope):
# Make a new entry if needed
#
if not entry or shadow:
if nogil:
pass
if nogil:
type = PyrexTypes.CythonExtensionType(
name, typedef_flag, base_type, visibility == 'extern', check_size=check_size)
else:
type = PyrexTypes.PyExtensionType(
name, typedef_flag, base_type, visibility == 'extern', check_size=check_size)
type.nogil = nogil
type = PyrexTypes.PyExtensionType(
name, typedef_flag, base_type, visibility == 'extern', check_size=check_size)
type.pos = pos
type.buffer_defaults = buffer_defaults
if objtypedef_cname is not None:
......@@ -2334,8 +2331,7 @@ class CClassScope(ClassScope):
defining=0, modifiers=(), utility_code=None, overridable=False):
name = self.mangle_class_private_name(name)
if get_special_method_signature(name) and not self.parent_type.is_builtin_type:
if not(hasattr(self.parent_type, "nogil") and self.parent_type.nogil and self.parent_type.is_struct_or_union):
error(pos, "Special methods must be declared with 'def', not 'cdef'")
error(pos, "Special methods must be declared with 'def', not 'cdef'")
args = type.args
if not type.is_static_method:
if not args:
......@@ -2571,6 +2567,8 @@ class CppClassScope(Scope):
type.return_type = PyrexTypes.c_void_type
if name in ('<init>', '<del>') and type.nogil:
for base in self.type.base_classes:
if base is cy_object_type:
continue
base_entry = base.scope.lookup(name)
if base_entry and not base_entry.type.nogil:
error(pos, "Constructor cannot be called without GIL unless all base constructors can also be called without GIL")
......
......@@ -198,10 +198,13 @@ class ResultRefNode(AtomicExprNode):
pass
def generate_assignment_code(self, rhs, code, overloaded_assignment=False):
if self.type.is_pyobject:
if self.type.is_pyobject or self.type.is_cyp_class:
rhs.make_owned_reference(code)
if not self.lhs_of_first_assignment:
code.put_decref(self.result(), self.ctype())
if self.type.is_pyobject:
code.put_decref(self.result(), self.ctype())
elif self.type.is_cyp_class:
code.put_cydecref(self.result())
code.putln('%s = %s;' % (
self.result(),
rhs.result() if overloaded_assignment else rhs.result_as(self.ctype()),
......@@ -248,6 +251,9 @@ class LetNodeMixin:
else:
if self.temp_type.is_pyobject:
code.put_decref_clear(self.temp, self.temp_type)
elif self.temp_type.is_cyp_class:
pass
#code.put_decref_clear(self.temp, self.temp_type)
code.funcstate.release_temp(self.temp)
......
/////////////// CyObjects.proto ///////////////
#if !defined(__GNUC__)
#define GCC_VERSION (__GNUC__ * 10000 \
+ __GNUC_MINOR__ * 100 \
+ __GNUC_PATCHLEVEL__)
/* Test for GCC > 4.9.0 */
#if GCC_VERSION < 40900
#error atomic.h works only with GCC newer than version 4.9
#endif /* GNUC >= 4.9 */
#endif /* Has GCC */
#ifdef __cplusplus
#if __cplusplus >= 201103L
#include <atomic>
using namespace std;
#define CyObject_ATOMIC_REFCOUNT_TYPE atomic_int
class CyObject {
private:
CyObject_ATOMIC_REFCOUNT_TYPE ob_refcnt;
public:
CyObject(): ob_refcnt(1) {}
virtual ~CyObject() {}
void CyObject_INCREF();
int CyObject_DECREF();
};
static inline int _Cy_DECREF(CyObject *op) {
return op->CyObject_DECREF();
}
static inline void _Cy_INCREF(CyObject *op) {
op->CyObject_INCREF();
}
/* Cast argument to CyObject* type. */
#define _CyObject_CAST(op) op
#define Cy_INCREF(op) do {if (op != NULL) {_Cy_INCREF(_CyObject_CAST(op));}} while(0)
#define Cy_DECREF(op) do {if (_Cy_DECREF(_CyObject_CAST(op))) {op = NULL;}} while(0)
#define Cy_XDECREF(op) do {if (op != NULL) {Cy_DECREF(op);}} while(0)
#define Cy_GOTREF(op)
#define Cy_XGOTREF(op)
#define Cy_GIVEREF(op)
#define Cy_XGIVEREF(op)
#endif
#endif
/////////////// CyObjects ///////////////
#ifdef __cplusplus
#include <cstdlib>
#include <cstddef>
// atomic is already included in ModuleSetupCode
// #include <atomic>
#else
#error C++ needed for cython+ nogil classes
#endif /* __cplusplus */
void CyObject::CyObject_INCREF()
{
atomic_fetch_add(&(this->ob_refcnt), 1);
}
int CyObject::CyObject_DECREF()
{
if (atomic_fetch_sub(&(this->ob_refcnt), 1) == 1) {
delete this;
return 1;
}
return 0;
}
......@@ -1851,51 +1851,6 @@ try_unpack:
return 0;
}
/////////////// CythonReferenceCounting.proto ///////////////
#include <stdlib.h>
#include <stddef.h>
#if !defined(__GNUC__)
#define GCC_VERSION (__GNUC__ * 10000 \
+ __GNUC_MINOR__ * 100 \
+ __GNUC_PATCHLEVEL__)
/* Test for GCC > 4.9.0 */
#if GCC_VERSION < 40900
#error atomic.h works only with GCC newer than version 4.9
#endif /* GNUC >= 4.9 */
#endif /* Has GCC */
// #include <stdatomic.h>
/* CyObject_HEAD defines the initial segment of every CyObject. */
#define CyObject_HEAD \
int ob_refcnt; \
void (*cdealloc)();
struct CyObject {
CyObject_HEAD
};
/* Cast argument to PyObject* type. */
#define _CyObject_CAST(op) ((struct CyObject*)(op))
// XXX: Without scope analysis this is useless...
/*
static inline void _Cy_DECREF(struct CyObject *op) {
if (atomic_fetch_sub(&(op->ob_refcnt), 1) == 1) {
op->cdealloc(op);
}
}
static inline void _Cy_INCREF(struct CyObject *op) {
atomic_fetch_add(&(op->ob_refcnt), 1);
}
#define Cy_INCREF(op) _Cy_INCREF(_CyObject_CAST(op))
#define Cy_DECREF(op) _Cy_DECREF(_CyObject_CAST(op))
*/
/////////////// UnpackUnboundCMethod.proto ///////////////
......
......@@ -32,7 +32,7 @@ from libc.stdio cimport printf
# cdef class SomeMemory:
cdef class SomeMemory nogil:
ccdef class SomeMemory:
"""
This is a cdef class which is also called
a extensino type. It is a kind of C struct
......@@ -41,7 +41,7 @@ cdef class SomeMemory nogil:
We would like to be able to define "nogil"
extension types:
cdef class SomeMemory nogil:
ccdef class SomeMemory:
where all methods are "nogil" and memory
allocation does not depend on python runtime
......@@ -54,10 +54,10 @@ cdef class SomeMemory nogil:
# self.b = b
pass
cdef void cdealloc(self) nogil:
cdef void cdealloc(self):
pass
cdef void foo1(self) nogil:
cdef void foo1(self):
"""
It is possible to define native C/Cython methods
that release the GIL (cool...)
......
......@@ -31,7 +31,7 @@ from libc.stdio cimport printf
# cdef class SomeMemory:
cdef class SomeMemory nogil:
ccdef class SomeMemory:
"""
This is a cdef class which is also called
a extensino type. It is a kind of C struct
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment