summaryrefslogtreecommitdiff
path: root/lib/git/async
diff options
context:
space:
mode:
Diffstat (limited to 'lib/git/async')
-rw-r--r--lib/git/async/graph.py14
-rw-r--r--lib/git/async/pool.py2
-rw-r--r--lib/git/async/task.py10
-rw-r--r--lib/git/async/thread.py43
4 files changed, 44 insertions, 25 deletions
diff --git a/lib/git/async/graph.py b/lib/git/async/graph.py
index e3999cdc..9ee0e891 100644
--- a/lib/git/async/graph.py
+++ b/lib/git/async/graph.py
@@ -25,14 +25,24 @@ class Graph(object):
def __init__(self):
self.nodes = list()
+
+ def __del__(self):
+ """Deletes bidericational dependencies"""
+ for node in self.nodes:
+ node.in_nodes = None
+ node.out_nodes = None
+ # END cleanup nodes
+
+ # otherwise the nodes would keep floating around
+
def add_node(self, node):
"""Add a new node to the graph
:return: the newly added node"""
self.nodes.append(node)
return node
- def del_node(self, node):
+ def remove_node(self, node):
"""Delete a node from the graph
:return: self"""
try:
@@ -46,6 +56,8 @@ class Graph(object):
del(outn.in_nodes[outn.in_nodes.index(node)])
for inn in node.in_nodes:
del(inn.out_nodes[inn.out_nodes.index(node)])
+ node.out_nodes = list()
+ node.in_nodes = list()
return self
def add_edge(self, u, v):
diff --git a/lib/git/async/pool.py b/lib/git/async/pool.py
index 3fd99c7b..0aad90ae 100644
--- a/lib/git/async/pool.py
+++ b/lib/git/async/pool.py
@@ -402,7 +402,7 @@ class Pool(object):
# keep its input nodes as we check whether they were orphaned
in_tasks = task.in_nodes
- self._tasks.del_node(task)
+ self._tasks.remove_node(task)
self._taskorder_cache.clear()
finally:
self._taskgraph_lock.release()
diff --git a/lib/git/async/task.py b/lib/git/async/task.py
index a8ba5ac6..49e7e7cf 100644
--- a/lib/git/async/task.py
+++ b/lib/git/async/task.py
@@ -82,7 +82,8 @@ class OutputChannelTask(Node):
def process(self, count=0):
"""Process count items and send the result individually to the output channel"""
- # first thing: increment the writer count
+ # first thing: increment the writer count - other tasks must be able
+ # to respond properly ( even if it turns out we don't need it later )
self._wlock.acquire()
self._num_writers += 1
self._wlock.release()
@@ -191,7 +192,11 @@ class InputIteratorTaskBase(OutputChannelTask):
raise ValueError("Iterator %r needs a next() function" % iterator)
self._iterator = iterator
self._lock = self.lock_type()
- self._read = self.__read
+
+ # this is necessary to prevent a cyclic ref, preventing us from
+ # getting deleted ( and collected )
+ weakself = weakref.ref(self)
+ self._read = lambda count: weakself().__read(count)
self._empty = False
def __read(self, count=0):
@@ -201,6 +206,7 @@ class InputIteratorTaskBase(OutputChannelTask):
if self._empty:
return list()
# END early abort
+
self._lock.acquire()
try:
if count == 0:
diff --git a/lib/git/async/thread.py b/lib/git/async/thread.py
index faeda04f..b8d2e418 100644
--- a/lib/git/async/thread.py
+++ b/lib/git/async/thread.py
@@ -116,7 +116,7 @@ class WorkerThread(TerminatableThread):
t[1] = optional, tuple or list of arguments to pass to the routine
t[2] = optional, dictionary of keyword arguments to pass to the routine
"""
- __slots__ = ('inq', '_current_routine')
+ __slots__ = ('inq')
# define how often we should check for a shutdown request in case our
@@ -128,7 +128,6 @@ class WorkerThread(TerminatableThread):
self.inq = inq
if inq is None:
self.inq = Queue.Queue()
- self._current_routine = None # routine we execute right now
@classmethod
def stop(cls, *args):
@@ -141,7 +140,6 @@ class WorkerThread(TerminatableThread):
gettask = self.inq.get
while True:
- self._current_routine = None
if self._should_terminate():
break
# END check for stop request
@@ -153,22 +151,27 @@ class WorkerThread(TerminatableThread):
assert len(tasktuple) == 2, "Need tuple of function, arg - it could be more flexible, but its reduced to what we need"
routine, arg = tasktuple
- self._current_routine = routine
-
try:
- rval = None
- if inspect.ismethod(routine):
- if routine.im_self is None:
- rval = routine(self, arg)
- else:
+ try:
+ rval = None
+ if inspect.ismethod(routine):
+ if routine.im_self is None:
+ rval = routine(self, arg)
+ else:
+ rval = routine(arg)
+ elif inspect.isroutine(routine):
rval = routine(arg)
- elif inspect.isroutine(routine):
- rval = routine(arg)
- else:
- # ignore unknown items
- print >> sys.stderr, "%s: task %s was not understood - terminating" % (self.getName(), str(tasktuple))
- break
- # END make routine call
+ else:
+ # ignore unknown items
+ print >> sys.stderr, "%s: task %s was not understood - terminating" % (self.getName(), str(tasktuple))
+ break
+ # END make routine call
+ finally:
+ # make sure we delete the routine to release the reference as soon
+ # as possible. Otherwise objects might not be destroyed
+ # while we are waiting
+ del(routine)
+ del(tasktuple)
except StopProcessing:
print self.name, "stops processing" # DEBUG
break
@@ -176,12 +179,10 @@ class WorkerThread(TerminatableThread):
print >> sys.stderr, "%s: Task %s raised unhandled exception: %s - this really shouldn't happen !" % (self.getName(), str(tasktuple), str(e))
continue # just continue
# END routine exception handling
+
+ # END handle routine release
# END endless loop
- def routine(self):
- """:return: routine we are currently executing, or None if we have no task"""
- return self._current_routine
-
def stop_and_join(self):
"""Send stop message to ourselves"""
self.inq.put((self.stop, None))