This diff is collapsed.
......@@ -737,6 +737,11 @@ class ExprNode(Node):
# a subnode.
return self.is_temp
def result_is_new_reference(self):
# Return true if the result is a new reference that is
# already incref-ed and will need to be decref-ed later.
return self.result_in_temp()
def target_code(self):
# Return code fragment for use as LHS of a C assignment.
return self.calculate_result_code()
......@@ -786,7 +791,7 @@ class ExprNode(Node):
Make sure we own a reference to result.
If the result is in a temp, it is already a new reference.
if not self.result_in_temp():
if not self.result_is_new_reference():
# FIXME: is this verification really necessary ?
if self.type.is_cyp_class and "NULL" in self.result():
......@@ -798,7 +803,7 @@ class ExprNode(Node):
Make sure we own the reference to this memoryview slice.
# TODO ideally this would be shared with "make_owned_reference"
if not self.result_in_temp():
if not self.result_is_new_reference():
code.put_incref_memoryviewslice(self.result(), self.type,
have_gil=not self.in_nogil_context)
......@@ -3592,7 +3597,7 @@ class IndexNode(_IndexingBaseNode):
# is_fused_index boolean Whether the index is used to specialize a
# c(p)def function
subexprs = ['base', 'index']
subexprs = ['index', 'base'] # evaluate the index first
type_indices = None
is_subscript = True
......@@ -3765,7 +3770,14 @@ class IndexNode(_IndexingBaseNode):
return py_object_type
def analyse_types(self, env):
return self.analyse_base_and_index_types(env, getting=True)
index_node = self.analyse_base_and_index_types(env, getting=True)
if index_node.type.is_cyp_class:
# If a[b] is a cypclass, a new reference is returned.
# In that case it must be stored for later decref-ing.
# XXX: future optimisation: only coerce to temporary
# when the value is not already assigned to a variable.
return index_node.coerce_to_temp(env)
return index_node
def analyse_target_types(self, env):
node = self.analyse_base_and_index_types(env, setting=True)
......@@ -4234,13 +4246,13 @@ class IndexNode(_IndexingBaseNode):
temp.use_managed_ref = False
return temp
def make_owned_reference(self, code):
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__
ExprNode.make_owned_reference(self, code)
return True
return ExprNode.result_is_new_reference(self)
gil_message = "Indexing Python object"
......@@ -5923,7 +5935,8 @@ class SimpleCallNode(CallNode):
# explicit_cpp_self bool used internally
# needs_deref bool used internally
subexprs = ['self', 'coerced_self', 'function', 'args', 'arg_tuple']
# evaluate arguments first.
subexprs = ['self', 'coerced_self', 'args', 'arg_tuple', 'function']
self = None
coerced_self = None
......@@ -7295,6 +7308,7 @@ class AttributeNode(ExprNode):
return node
def analyse_types(self, env, target = 0):
self.is_target = target
self.initialized_check = env.directives['initializedcheck']
node = self.analyse_as_cimported_attribute_node(env, target)
if node is None and not target:
......@@ -8025,7 +8039,7 @@ class SequenceNode(ExprNode):
for i in range(arg_count):
arg = self.args[i]
if c_mult or not arg.result_in_temp():
if c_mult or not arg.result_is_new_reference():
code.put_incref(arg.result(), arg.ctype())
code.putln("%s(%s, %s, %s);" % (
......@@ -14162,13 +14176,17 @@ class CoerceToLockedNode(CoercionNode):
# This node is used to lock a node of cypclass type around the evaluation of its subexpressions.
# rlock_only boolean
# needs_decref boolean used internally
def __init__(self, arg, env=None, rlock_only=False):
self.rlock_only = rlock_only
self.type = arg.type
arg = arg.coerce_to_temp(env)
arg.postpone_subexpr_disposal = True
temp_arg = arg.coerce_to_temp(env)
# 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
def result(self):
return self.arg.result()
......@@ -14206,8 +14224,12 @@ class CoerceToLockedNode(CoercionNode):
def generate_disposal_code(self, code):
# Close the scope to release the lock.
# Dispose of subexpressions.
# The subexpressions of self.arg are disposed-of and freed
# as soon as self.arg is evaluated because it is a temporary.
# 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 ProxyNode(CoercionNode):
......@@ -8597,6 +8597,9 @@ class LockCypclassNode(StatNode):
child_attrs = ["body", "obj"]
nested = False
objs = None
def analyse_declarations(self, env):
......@@ -204,7 +204,7 @@ cdef cypclass cypset[V]:
cypset[V] __ior__(self, const cypset[V] other) except ~:
if self._active_iterators == 0:
self._elements.insert(other._elements.const_begin(), other._elements.end())
self._elements.insert(other._elements.const_begin(), other._elements.const_end())
return self
with gil:
# mode: run
# tag: cpp, cpp11, pthread
# cython: experimental_cpp_class_def=True, language_level=2
from libcpp.deque cimport deque
ctypedef deque[ActhonMessageInterface] message_queue_t
cdef extern from "<semaphore.h>" nogil:
ctypedef struct sem_t:
int sem_init(sem_t *sem, int pshared, unsigned int value)
int sem_wait(sem_t *sem)
int sem_post(sem_t *sem)
int sem_destroy(sem_t* sem)
cdef cypclass BasicQueue(ActhonQueueInterface) checklock:
message_queue_t* _queue
self._queue = new message_queue_t()
del self._queue
bint is_empty(const self):
return self._queue.empty()
void push(self, ActhonMessageInterface message):
if message._sync_method is not NULL:
bint activate(self):
cdef bint one_message_processed
if self._queue.empty():
return False
# Note here that according to Cython refcount conventions,
# the front() method should have returned a new ref.
# This is obviously not the case, so if we do nothing
# we will, at the end of this function, loose a ref on the pointed object
# (as we will decref the thing pointed by next_message).
next_message = self._queue.front()
one_message_processed = next_message.activate()
if one_message_processed:
if next_message._sync_method is not NULL:
next_sync_method = next_message._sync_method
with wlocked next_sync_method:
# Don't forget to incref to avoid premature deallocation
return one_message_processed
cdef cypclass NoneResult(ActhonResultInterface) checklock:
void pushVoidStarResult(self, void* result):
void pushIntResult(self, int result):
void* getVoidStarResult(const self):
return NULL
int getIntResult(const self):
return 0
cdef cypclass WaitResult(ActhonResultInterface) checklock:
union result_t:
int int_val
void* ptr
result_t result
sem_t semaphore
self.result.ptr = NULL
sem_init(&self.semaphore, 0, 0)
ActhonResultInterface construct():
return WaitResult()
void pushVoidStarResult(self, void* result):
self.result.ptr = result
void pushIntResult(self, int result):
self.result.int_val = result
result_t _getRawResult(const self):
# We must ensure a result exists, but we can let others access it immediately
# The cast here is a way of const-casting (we're modifying the semaphore in a const method)
sem_wait(<sem_t*> &self.semaphore)
sem_post(<sem_t*> &self.semaphore)
return self.result
void* getVoidStarResult(const self):
res = self._getRawResult()
return res.ptr
int getIntResult(const self):
res = self._getRawResult()
return res.int_val
cdef cypclass ActivityCounterSync(ActhonSyncInterface) checklock:
int count
ActivityCounterSync previous_sync
__init__(self, ActivityCounterSync prev = <ActivityCounterSync> NULL):
self.count = 0
self.previous_sync = prev
void insertActivity(self, ActhonMessageInterface msg):
self.count += 1
void removeActivity(self, ActhonMessageInterface msg):
self.count -= 1
bint isCompleted(const self):
return self.count == 0
bint isActivable(const self):
cdef bint res = True
if self.previous_sync is not NULL:
prev_sync = self.previous_sync
with rlocked prev_sync:
res = prev_sync.isCompleted()
return res
cdef cypclass A checklock activable:
int a
self.a = 0
self._active_result_class = WaitResult.construct
self._active_queue_class = BasicQueue()
int getter(const self):
return self.a
void setter(self, int a):
self.a = a
def test_acthon_chain(n):
>>> test_acthon_chain(42)
cdef ActhonResultInterface res
cdef ActhonQueueInterface queue
sync1 = ActivityCounterSync()
with wlocked sync1:
after_sync1 = ActivityCounterSync(sync1)
obj = A()
with wlocked obj:
obj_actor = obj.__activate__()
with wlocked obj_actor, wlocked sync1, wlocked after_sync1:
# Pushing things in the queue
obj_actor.setter(sync1, n)
res = obj_actor.getter(after_sync1)
# Processing the queue
with rlocked obj:
queue = obj._active_queue_class
with wlocked queue:
while not queue.is_empty():
print <int> res