Commit 98a93bc2 authored by Xavier Thompson's avatar Xavier Thompson

Change cypclass reference counting convention

This changes the reference counting convention for passing cyobjects
as arguments in a function call.

Before this commit, cyobjects used the same convention as pyobjects:
- The function borrows a reference on the argument from the caller
  and the caller keeps ownership of the object passed as argument,
  and must eventually decref it, even if it is a temporary rvalue
  that will not be reachable in the caller's scope after the call.
- If the function needs to take ownership of the argument, e.g. to
  store it, it must increment its reference count first, at which
  point the caller and callee both own a reference to the object.
- If the callee does not take ownership of the argument, it should
  not decrement its reference count at any point.

After this commit, the convention for cyobjects is as follows:
- The function steals the reference on the argument from the caller,
  and the caller should not decrement its reference count after the
  call.
- If the object will still be reachable in the caller's scope after
  the call the caller must increment its reference count __before__
  the call to retain ownership of its reference after the call.
- The function has ownership of the reference received as argument,
  and must decref it when if goes out of scope.

The main reason for this change is to make it possible to 'consume'
an argument from within a function. Before this change there was no
reliable way to determine from within the function whether the caller
would retain a reference on the argument after the call, which is
required when the 'consume' operation needs a runtime check.

The 'self' argument in a cypclass method is currently still an
exception to this change: it still follows the previous convention.
parent 0790e362
...@@ -849,7 +849,7 @@ class FunctionState(object): ...@@ -849,7 +849,7 @@ class FunctionState(object):
elif type.is_cfunction: elif type.is_cfunction:
from . import PyrexTypes from . import PyrexTypes
type = PyrexTypes.c_ptr_type(type) # A function itself isn't an l-value type = PyrexTypes.c_ptr_type(type) # A function itself isn't an l-value
if not type.is_pyobject and not type.is_memoryviewslice and not type.is_cyp_class: if not type.is_pyobject and not type.is_memoryviewslice:
# Make manage_ref canonical, so that manage_ref will always mean # Make manage_ref canonical, so that manage_ref will always mean
# a decref is needed. # a decref is needed.
manage_ref = False manage_ref = False
......
...@@ -3966,6 +3966,8 @@ class IndexNode(_IndexingBaseNode): ...@@ -3966,6 +3966,8 @@ class IndexNode(_IndexingBaseNode):
self.type = func_type.return_type.as_returned_type() self.type = func_type.return_type.as_returned_type()
if setting and not self.type.is_reference: if setting and not self.type.is_reference:
error(self.pos, "Can't set non-reference result '%s'" % self.type) error(self.pos, "Can't set non-reference result '%s'" % self.type)
if not setting and self.type.is_cyp_class:
self.is_temp = True
return self return self
def analyse_as_cyp_class(self, env, setting, deleting): def analyse_as_cyp_class(self, env, setting, deleting):
...@@ -3996,8 +3998,13 @@ class IndexNode(_IndexingBaseNode): ...@@ -3996,8 +3998,13 @@ class IndexNode(_IndexingBaseNode):
elif self.exception_check == '~': elif self.exception_check == '~':
self.is_temp = True self.is_temp = True
self.index = self.index.coerce_to(function.type.args[0].type, env) arg_type = function.type.args[0].type
self.index = self.index.coerce_to(arg_type, env)
if arg_type.is_cyp_class:
self.index = CoerceToArgAssmtNode(self.index, env)
self.type = func_type.return_type.as_returned_type() self.type = func_type.return_type.as_returned_type()
if self.type.is_cyp_class:
self.is_temp = True
return self return self
def analyse_cyp_class_setitem(self, env): def analyse_cyp_class_setitem(self, env):
...@@ -4016,7 +4023,10 @@ class IndexNode(_IndexingBaseNode): ...@@ -4016,7 +4023,10 @@ class IndexNode(_IndexingBaseNode):
if self.exception_check == '+' and self.exception_value is None: if self.exception_check == '+' and self.exception_value is None:
env.use_utility_code(UtilityCode.load_cached("CppExceptionConversion", "CppSupport.cpp")) env.use_utility_code(UtilityCode.load_cached("CppExceptionConversion", "CppSupport.cpp"))
self.index = self.index.coerce_to(function.type.args[0].type, env) arg_type = function.type.args[0].type
self.index = self.index.coerce_to(arg_type, env)
if arg_type.is_cyp_class:
self.index = CoerceToArgAssmtNode(self.index, env)
self.type = func_type.args[1].type self.type = func_type.args[1].type
return self return self
...@@ -4036,7 +4046,10 @@ class IndexNode(_IndexingBaseNode): ...@@ -4036,7 +4046,10 @@ class IndexNode(_IndexingBaseNode):
if self.exception_check == '+' and self.exception_value is None: if self.exception_check == '+' and self.exception_value is None:
env.use_utility_code(UtilityCode.load_cached("CppExceptionConversion", "CppSupport.cpp")) env.use_utility_code(UtilityCode.load_cached("CppExceptionConversion", "CppSupport.cpp"))
self.index = self.index.coerce_to(function.type.args[0].type, env) arg_type = function.type.args[0].type
self.index = self.index.coerce_to(arg_type, env)
if arg_type.is_cyp_class:
self.index = CoerceToArgAssmtNode(self.index, env)
self.type = func_type.return_type.as_returned_type() self.type = func_type.return_type.as_returned_type()
return self return self
...@@ -4243,23 +4256,6 @@ class IndexNode(_IndexingBaseNode): ...@@ -4243,23 +4256,6 @@ class IndexNode(_IndexingBaseNode):
# This is a bug # This is a bug
raise InternalError("Couldn't find the right signature") raise InternalError("Couldn't find the right signature")
def coerce_to_temp(self, env):
temp = ExprNode.coerce_to_temp(self, env)
if self.type.is_cyp_class and not (self.base.type.is_array or self.base.type.is_ptr):
# This is already a new reference
# either via cpp operator[]
# or via cypclass __getitem__
temp.use_managed_ref = False
return temp
def result_is_new_reference(self):
if self.type.is_cyp_class and not (self.base.type.is_array or self.base.type.is_ptr):
# This is already a new reference
# either via cpp operator[]
# or via cypclass __getitem__
return True
return ExprNode.result_is_new_reference(self)
gil_message = "Indexing Python object" gil_message = "Indexing Python object"
def calculate_result_code(self): def calculate_result_code(self):
...@@ -4355,7 +4351,7 @@ class IndexNode(_IndexingBaseNode): ...@@ -4355,7 +4351,7 @@ class IndexNode(_IndexingBaseNode):
function = "__Pyx_GetItemInt_ByteArray" function = "__Pyx_GetItemInt_ByteArray"
error_value = '-1' error_value = '-1'
utility_code = UtilityCode.load_cached("GetItemIntByteArray", "StringTools.c") utility_code = UtilityCode.load_cached("GetItemIntByteArray", "StringTools.c")
elif not (self.base.type.is_cpp_class and self.exception_check in ('+', '~')): elif not (self.base.type.is_cpp_class and (self.exception_check in ('+', '~') or self.type.is_cyp_class)):
assert False, "unexpected type %s and base type %s for indexing" % ( assert False, "unexpected type %s and base type %s for indexing" % (
self.type, self.base.type) self.type, self.base.type)
...@@ -4381,6 +4377,8 @@ class IndexNode(_IndexingBaseNode): ...@@ -4381,6 +4377,8 @@ class IndexNode(_IndexingBaseNode):
exc_check_code = self.type.error_condition(self.result()) exc_check_code = self.type.error_condition(self.result())
goto_error = code.error_goto_if(exc_check_code, self.pos) goto_error = code.error_goto_if(exc_check_code, self.pos)
code.putln("%s %s" % (evaluation_code, goto_error)) code.putln("%s %s" % (evaluation_code, goto_error))
elif self.type.is_cyp_class:
code.putln("%s" % evaluation_code)
else: else:
error_check = '!%s' if error_value == 'NULL' else '%%s == %s' % error_value error_check = '!%s' if error_value == 'NULL' else '%%s == %s' % error_value
code.putln( code.putln(
...@@ -4456,6 +4454,9 @@ class IndexNode(_IndexingBaseNode): ...@@ -4456,6 +4454,9 @@ class IndexNode(_IndexingBaseNode):
exception_check=None, exception_value=None): exception_check=None, exception_value=None):
self.generate_subexpr_evaluation_code(code) self.generate_subexpr_evaluation_code(code)
if self.type.is_cyp_class:
rhs.make_owned_reference(code)
if self.type.is_pyobject: if self.type.is_pyobject:
self.generate_setitem_code(rhs.py_result(), code) self.generate_setitem_code(rhs.py_result(), code)
elif self.base.type.is_cyp_class: elif self.base.type.is_cyp_class:
...@@ -4482,12 +4483,11 @@ class IndexNode(_IndexingBaseNode): ...@@ -4482,12 +4483,11 @@ class IndexNode(_IndexingBaseNode):
code.putln( code.putln(
"%s = %s;" % (self.result(), rhs.result())) "%s = %s;" % (self.result(), rhs.result()))
if (self.base.type.is_array or self.base.type.is_ptr) and self.type.is_cyp_class:
# XXX This is a good place for refcounting optimisation:
# if the rhs is a cypclass temporary we are doing an INCREF and a DECREF.
code.put_incref(self.result(), self.type)
self.generate_subexpr_disposal_code(code) self.generate_subexpr_disposal_code(code)
self.free_subexpr_temps(code) self.free_subexpr_temps(code)
if self.type.is_cyp_class:
rhs.generate_post_assignment_code(code)
else:
rhs.generate_disposal_code(code) rhs.generate_disposal_code(code)
rhs.free_temps(code) rhs.free_temps(code)
...@@ -6174,7 +6174,11 @@ class SimpleCallNode(CallNode): ...@@ -6174,7 +6174,11 @@ class SimpleCallNode(CallNode):
# C methods must do the None checks at *call* time # C methods must do the None checks at *call* time
arg = arg.as_none_safe_node( arg = arg.as_none_safe_node(
"cannot pass None into a C function argument that is declared 'not None'") "cannot pass None into a C function argument that is declared 'not None'")
if arg.is_temp: if arg.type.is_cyp_class:
if i > 0:
some_args_in_temps = True
arg = CoerceToArgAssmtNode(arg, env)
elif arg.is_temp:
if i > 0: if i > 0:
# first argument in temp doesn't impact subsequent arguments # first argument in temp doesn't impact subsequent arguments
some_args_in_temps = True some_args_in_temps = True
...@@ -6193,10 +6197,6 @@ class SimpleCallNode(CallNode): ...@@ -6193,10 +6197,6 @@ class SimpleCallNode(CallNode):
if i > 0: # first argument doesn't matter if i > 0: # first argument doesn't matter
some_args_in_temps = True some_args_in_temps = True
arg = arg.coerce_to_temp(env) arg = arg.coerce_to_temp(env)
elif arg.type.is_cyp_class:
if i > 0:
some_args_in_temps = True
arg = arg.coerce_to_temp(env)
args[i] = arg args[i] = arg
# handle additional varargs parameters # handle additional varargs parameters
...@@ -11413,6 +11413,8 @@ class ConsumeNode(ExprNode): ...@@ -11413,6 +11413,8 @@ class ConsumeNode(ExprNode):
self.type = PyrexTypes.cyp_class_qualified_type(operand_type, 'iso~') self.type = PyrexTypes.cyp_class_qualified_type(operand_type, 'iso~')
self.operand_is_named = self.operand.is_name or self.operand.is_attribute self.operand_is_named = self.operand.is_name or self.operand.is_attribute
self.is_temp = self.operand_is_named or (self.generate_runtime_check and not self.operand.is_temp) self.is_temp = self.operand_is_named or (self.generate_runtime_check and not self.operand.is_temp)
if self.operand_is_named:
self.operand.entry.is_consumed = True
if not self.operand_is_named and not self.generate_runtime_check: if not self.operand_is_named and not self.generate_runtime_check:
return self.operand return self.operand
return self return self
...@@ -11746,6 +11748,8 @@ class BinopNode(ExprNode): ...@@ -11746,6 +11748,8 @@ class BinopNode(ExprNode):
else: else:
self.operand1 = self.operand1.coerce_to(func_type.args[0].type, env) self.operand1 = self.operand1.coerce_to(func_type.args[0].type, env)
self.operand2 = self.operand2.coerce_to(func_type.args[1].type, env) self.operand2 = self.operand2.coerce_to(func_type.args[1].type, env)
if self.operand2.type.is_cyp_class:
self.operand2 = CoerceToArgAssmtNode(self.operand2, env)
self.type = func_type.return_type.as_returned_type() self.type = func_type.return_type.as_returned_type()
if self.type.is_cyp_class: if self.type.is_cyp_class:
self.is_temp = 1 self.is_temp = 1
...@@ -13448,6 +13452,8 @@ class PrimaryCmpNode(ExprNode, CmpNode): ...@@ -13448,6 +13452,8 @@ class PrimaryCmpNode(ExprNode, CmpNode):
else: else:
self.operand1 = self.operand1.coerce_to(func_type.args[0].type, env) self.operand1 = self.operand1.coerce_to(func_type.args[0].type, env)
self.operand2 = self.operand2.coerce_to(func_type.args[1].type, env) self.operand2 = self.operand2.coerce_to(func_type.args[1].type, env)
if self.operand2.type.is_cyp_class:
self.operand2 = CoerceToArgAssmtNode(self.operand2, env)
self.type = func_type.return_type.as_returned_type() self.type = func_type.return_type.as_returned_type()
if self.type.is_cyp_class: if self.type.is_cyp_class:
self.is_temp = True self.is_temp = True
...@@ -14279,17 +14285,12 @@ class CoerceToLockedNode(CoercionNode): ...@@ -14279,17 +14285,12 @@ class CoerceToLockedNode(CoercionNode):
# This node is used to lock a node of cypclass type around the evaluation of its subexpressions. # This node is used to lock a node of cypclass type around the evaluation of its subexpressions.
# exclusive boolean # exclusive boolean
# needs_decref boolean used internally
def __init__(self, arg, env=None, exclusive=True): def __init__(self, arg, env=None, exclusive=True):
self.exclusive = exclusive self.exclusive = exclusive
self.type = arg.type self.type = arg.type
temp_arg = arg.coerce_to_temp(env) temp_arg = arg.coerce_to_temp(env)
temp_arg.postpone_subexpr_disposal = True temp_arg.postpone_subexpr_disposal = True
# Avoid incrementing the reference count when assigning to the temporary
# but ensure it will be decremented if it was already incremented previously.
self.needs_decref = not temp_arg.use_managed_ref
temp_arg.use_managed_ref = False
super(CoerceToLockedNode,self).__init__(temp_arg) super(CoerceToLockedNode,self).__init__(temp_arg)
def result(self): def result(self):
...@@ -14331,9 +14332,43 @@ class CoerceToLockedNode(CoercionNode): ...@@ -14331,9 +14332,43 @@ class CoerceToLockedNode(CoercionNode):
# Dispose of and free subexpressions. # Dispose of and free subexpressions.
self.arg.generate_subexpr_disposal_code(code) self.arg.generate_subexpr_disposal_code(code)
self.arg.free_subexpr_temps(code) self.arg.free_subexpr_temps(code)
# Decref only if previously incref-ed.
if self.needs_decref:
code.put_xdecref_clear(self.result(), self.ctype(), have_gil=not self.in_nogil_context) class CoerceToArgAssmtNode(CoercionNode):
# This node is used to pass the result of a node as argument
# to a called function as if it were a simple assignment.
# In particular, temporaries are not decremented on disposal.
def __init__(self, arg, env=None):
CoercionNode.__init__(self, arg.coerce_to_simple(env))
self.type = self.arg.type.as_argument_type()
def analyse_types(self, env):
return self
def may_be_none(self):
return self.arg.may_be_none()
def is_simple(self):
return True
def result_in_temp(self):
return self.arg.result_in_temp()
def make_owned_reference(self, code):
pass
def calculate_result_code(self):
return self.arg.result()
def generate_result_code(self, code):
self.arg.make_owned_reference(code)
def generate_post_assignment_code(self, code):
self.arg.generate_post_assignment_code(code)
def generate_disposal_code(self, code):
self.arg.generate_post_assignment_code(code)
class ProxyNode(CoercionNode): class ProxyNode(CoercionNode):
......
...@@ -1061,9 +1061,9 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -1061,9 +1061,9 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.putln("this->%s->push(message);" % queue_attr_cname) code.putln("this->%s->push(message);" % queue_attr_cname)
code.putln("Cy_UNWLOCK(%s);" % queue_attr_cname) code.putln("Cy_UNWLOCK(%s);" % queue_attr_cname)
code.putln("} else {") code.putln("} else {")
code.putln("Cy_DECREF(message);")
code.putln('fprintf(stderr, "Acthon error: No queue to push to for %s remote call !\\n");' % reified_function_entry.name) code.putln('fprintf(stderr, "Acthon error: No queue to push to for %s remote call !\\n");' % reified_function_entry.name)
code.putln("}") code.putln("}")
code.putln("Cy_DECREF(message);")
# Return result object # Return result object
code.putln("return result_object;") code.putln("return result_object;")
code.putln("}") code.putln("}")
......
...@@ -2199,10 +2199,12 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -2199,10 +2199,12 @@ class FuncDefNode(StatNode, BlockNode):
is_cdef = isinstance(self, CFuncDefNode) is_cdef = isinstance(self, CFuncDefNode)
for entry in lenv.arg_entries: for entry in lenv.arg_entries:
if not entry.type.is_memoryviewslice: if not entry.type.is_memoryviewslice:
if (acquire_gil or entry.cf_is_reassigned) and not entry.in_closure: if entry.type.is_cyp_class:
if entry.type.is_cyp_class and entry.is_self_arg: # CyObjects use another refcounting convention.
pass # Except for the 'self' argument.
else: if entry.is_self_arg and (entry.is_consumed or entry.cf_is_reassigned) and not entry.in_closure:
code.put_var_incref(entry)
elif (acquire_gil or entry.cf_is_reassigned) and not entry.in_closure:
code.put_var_incref(entry) code.put_var_incref(entry)
# Note: defaults are always incref-ed. For def functions, we # Note: defaults are always incref-ed. For def functions, we
# we acquire arguments from object conversion, so we have # we acquire arguments from object conversion, so we have
...@@ -2418,6 +2420,11 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -2418,6 +2420,11 @@ class FuncDefNode(StatNode, BlockNode):
# functions, but not borrowed slices from cdef functions. # functions, but not borrowed slices from cdef functions.
if is_cdef and not entry.cf_is_reassigned: if is_cdef and not entry.cf_is_reassigned:
continue continue
elif entry.type.is_cyp_class:
# CyObject arguments are systematically decrefed.
# Except for the 'self' argument when it has not been reassigned or consumed.
if entry.is_self_arg and not (entry.is_consumed or entry.cf_is_reassigned):
continue
else: else:
if entry.in_closure: if entry.in_closure:
continue continue
...@@ -2425,9 +2432,6 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -2425,9 +2432,6 @@ class FuncDefNode(StatNode, BlockNode):
continue continue
if entry.type.needs_refcounting: if entry.type.needs_refcounting:
assure_gil('success') assure_gil('success')
if entry.type.is_cyp_class and entry.is_self_arg:
pass
else:
# FIXME use entry.xdecref_cleanup - del arg seems to be the problem # FIXME use entry.xdecref_cleanup - del arg seems to be the problem
code.put_var_xdecref(entry, have_gil=gil_owned['success']) code.put_var_xdecref(entry, have_gil=gil_owned['success'])
if self.needs_closure: if self.needs_closure:
...@@ -3782,19 +3786,11 @@ class DefNodeWrapper(FuncDefNode): ...@@ -3782,19 +3786,11 @@ class DefNodeWrapper(FuncDefNode):
# ----- Non-error return cleanup # ----- Non-error return cleanup
code.put_label(code.return_label) code.put_label(code.return_label)
for entry in lenv.var_entries: for entry in lenv.var_entries:
if entry.is_arg: if entry.is_arg and not entry.type.is_cyp_class:
# The conversion from PyObject to CyObject always creates a new CyObject reference. if entry.xdecref_cleanup:
# This decrements all arguments-as-variables converted straight from an actual argument.
# This includes CyObjects converted directly from a corresponding PyObject argument.
if entry.xdecref_cleanup or entry.type.is_cyp_class:
code.put_var_xdecref(entry) code.put_var_xdecref(entry)
else: else:
code.put_var_decref(entry) code.put_var_decref(entry)
for entry in lenv.arg_entries:
if entry.type.is_cyp_class:
# The conversion from PyObject to CyObject always creates a new CyObject reference.
# This decrements CyObjects converted from generic PyObject args passed via tuple and kw dict.
code.put_var_xdecref(entry)
code.put_finish_refcount_context() code.put_finish_refcount_context()
if not self.return_type.is_void: if not self.return_type.is_void:
...@@ -6580,7 +6576,7 @@ class DelStatNode(StatNode): ...@@ -6580,7 +6576,7 @@ class DelStatNode(StatNode):
arg.free_temps(code) arg.free_temps(code)
elif arg.type.is_cyp_class: elif arg.type.is_cyp_class:
arg.generate_evaluation_code(code) arg.generate_evaluation_code(code)
code.put_decref_clear(arg.result(), arg.type) code.put_xdecref_clear(arg.result(), arg.type)
arg.generate_disposal_code(code) arg.generate_disposal_code(code)
arg.free_temps(code) arg.free_temps(code)
# else error reported earlier # else error reported earlier
......
...@@ -3796,12 +3796,10 @@ class CFuncTypeArg(BaseType): ...@@ -3796,12 +3796,10 @@ class CFuncTypeArg(BaseType):
accept_none = True accept_none = True
accept_builtin_subtypes = False accept_builtin_subtypes = False
annotation = None annotation = None
original_template = None
has_cypclass_specialisation = False
subtypes = ['type'] subtypes = ['type']
def __init__(self, name, type, pos, cname=None, annotation=None, original_template=None): def __init__(self, name, type, pos, cname=None, annotation=None):
self.name = name self.name = name
if cname is not None: if cname is not None:
self.cname = cname self.cname = cname
...@@ -3812,8 +3810,6 @@ class CFuncTypeArg(BaseType): ...@@ -3812,8 +3810,6 @@ class CFuncTypeArg(BaseType):
self.type = type self.type = type
self.pos = pos self.pos = pos
self.needs_type_test = False # TODO: should these defaults be set in analyse_types()? self.needs_type_test = False # TODO: should these defaults be set in analyse_types()?
self.original_template = original_template or self
self.original_template.has_cypclass_specialisation = type.is_cyp_class
def __repr__(self): def __repr__(self):
return "%s:%s" % (self.name, repr(self.type)) return "%s:%s" % (self.name, repr(self.type))
...@@ -3821,14 +3817,10 @@ class CFuncTypeArg(BaseType): ...@@ -3821,14 +3817,10 @@ class CFuncTypeArg(BaseType):
def declaration_code(self, for_display = 0, pyrex = 0, cname = None): def declaration_code(self, for_display = 0, pyrex = 0, cname = None):
if cname is None: if cname is None:
cname = self.cname cname = self.cname
if self.type.is_template_typename and self.has_cypclass_specialisation:
base_code = "Cy_Raw<%s>" % self.type.empty_declaration_code()
return self.base_declaration_code(base_code, self.cname)
return self.type.declaration_code(cname, for_display, pyrex) return self.type.declaration_code(cname, for_display, pyrex)
def specialize(self, values): def specialize(self, values):
return CFuncTypeArg(self.name, self.type.specialize(values), self.pos, self.cname, return CFuncTypeArg(self.name, self.type.specialize(values), self.pos, self.cname)
None, self.original_template)
class ToPyStructUtilityCode(object): class ToPyStructUtilityCode(object):
...@@ -4725,15 +4717,13 @@ class CypClassType(CppClassType): ...@@ -4725,15 +4717,13 @@ class CypClassType(CppClassType):
code.putln("Cy_DECREF(%s);" % cname) code.putln("Cy_DECREF(%s);" % cname)
def generate_decref_clear(self, code, cname, **kwds): def generate_decref_clear(self, code, cname, **kwds):
code.putln("Cy_DECREF(%s);" % cname) code.putln("Cy_DECREF(%s); %s = NULL;" % (cname, cname))
code.putln("%s = NULL;" % cname)
def generate_xdecref(self, code, cname, **kwds): def generate_xdecref(self, code, cname, **kwds):
code.putln("Cy_XDECREF(%s);" % cname) code.putln("Cy_XDECREF(%s);" % cname)
def generate_xdecref_clear(self, code, cname, **kwds): def generate_xdecref_clear(self, code, cname, **kwds):
code.putln("Cy_XDECREF(%s);" % cname) code.putln("Cy_XDECREF(%s); %s = NULL;" % (cname, cname))
code.putln("%s = NULL;" % cname)
def generate_gotref(self, code, cname): def generate_gotref(self, code, cname):
code.putln("Cy_GOTREF(%s);" % cname) code.putln("Cy_GOTREF(%s);" % cname)
......
...@@ -180,6 +180,8 @@ class Entry(object): ...@@ -180,6 +180,8 @@ class Entry(object):
# original_name string The original name of a cpp or cypclass method # original_name string The original name of a cpp or cypclass method
# #
# active_entry Entry Entry for the active version of an asyncable cypclass method # active_entry Entry Entry for the active version of an asyncable cypclass method
#
# is_consumed boolean The entry is the operand of a 'consume' expression.
# TODO: utility_code and utility_code_definition serves the same purpose... # TODO: utility_code and utility_code_definition serves the same purpose...
...@@ -258,6 +260,7 @@ class Entry(object): ...@@ -258,6 +260,7 @@ class Entry(object):
static_cname = None static_cname = None
original_name = None original_name = None
active_entry = None active_entry = None
is_consumed = False
def __init__(self, name, cname, type, pos = None, init = None): def __init__(self, name, cname, type, pos = None, init = None):
self.name = name self.name = name
......
...@@ -141,13 +141,7 @@ ...@@ -141,13 +141,7 @@
constexpr Cy_Ref_impl() noexcept = default; constexpr Cy_Ref_impl() noexcept = default;
// constexpr Cy_Ref_impl(std::nullptr_t null) noexcept : uobj(null) {} constexpr Cy_Ref_impl(T* const& uobj) noexcept : uobj(uobj) {}
Cy_Ref_impl(T* const& uobj) : uobj(uobj) {
if (uobj != nullptr) {
uobj->CyObject_INCREF();
}
}
constexpr Cy_Ref_impl(T* && uobj) noexcept : uobj(uobj) {} constexpr Cy_Ref_impl(T* && uobj) noexcept : uobj(uobj) {}
...@@ -280,6 +274,7 @@ ...@@ -280,6 +274,7 @@
} }
template <typename U = T, typename std::enable_if<Cy_has_equality<U>::value, int>::type = 0> template <typename U = T, typename std::enable_if<Cy_has_equality<U>::value, int>::type = 0>
bool operator()(const Cy_Ref_impl<T>& lhs, const Cy_Ref_impl<T>& rhs) const { bool operator()(const Cy_Ref_impl<T>& lhs, const Cy_Ref_impl<T>& rhs) const {
Cy_INCREF(rhs.uobj);
return lhs.uobj->operator==(rhs.uobj); return lhs.uobj->operator==(rhs.uobj);
} }
}; };
...@@ -420,6 +415,12 @@ ...@@ -420,6 +415,12 @@
return ob->CyObject_GETREF(); return ob->CyObject_GETREF();
} }
static inline int Cy_GETREF(const CyObject *ob) {
int refcnt = ob->CyObject_GETREF();
ob->CyObject_DECREF();
return refcnt;
}
static inline void _Cy_RLOCK(const CyObject *ob, const char *context) { static inline void _Cy_RLOCK(const CyObject *ob, const char *context) {
if (ob != NULL) { if (ob != NULL) {
ob->CyObject_RLOCK(context); ob->CyObject_RLOCK(context);
...@@ -473,7 +474,9 @@ ...@@ -473,7 +474,9 @@
template <typename T, typename O> template <typename T, typename O>
static inline int isinstanceof(O ob) { static inline int isinstanceof(O ob) {
static_assert(std::is_convertible<T, CyObject *>::value, "wrong type 'T' for isinstanceof[T]"); static_assert(std::is_convertible<T, CyObject *>::value, "wrong type 'T' for isinstanceof[T]");
return dynamic_cast<const typename std::remove_pointer<T>::type *>(ob) != NULL; bool result = dynamic_cast<const typename std::remove_pointer<T>::type *>(ob) != NULL;
Cy_DECREF(ob);
return result;
} }
/* /*
...@@ -482,7 +485,6 @@ ...@@ -482,7 +485,6 @@
template <typename T> template <typename T>
static inline T * activate(T * ob) { static inline T * activate(T * ob) {
static_assert(std::is_convertible<T *, ActhonActivableClass *>::value, "wrong type for activate"); static_assert(std::is_convertible<T *, ActhonActivableClass *>::value, "wrong type for activate");
Cy_INCREF(ob);
return ob; return ob;
} }
...@@ -608,7 +610,6 @@ ...@@ -608,7 +610,6 @@
/* Cast argument to CyObject* type. */ /* Cast argument to CyObject* type. */
#define _CyObject_CAST(ob) ob #define _CyObject_CAST(ob) ob
#define Cy_GETREF(ob) (_Cy_GETREF(_CyObject_CAST(ob)))
#define Cy_GOTREF(ob) #define Cy_GOTREF(ob)
#define Cy_XGOTREF(ob) #define Cy_XGOTREF(ob)
#define Cy_GIVEREF(ob) #define Cy_GIVEREF(ob)
......
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