blob: c1c98c411cd14e47c3945aa068842723b77c64f5 [file] [log] [blame]
# Copyright 1999-2009 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
import sys
from _emerge.SlotObject import SlotObject
from collections import deque
class SequentialTaskQueue(SlotObject):
__slots__ = ("max_jobs", "running_tasks") + \
("_dirty", "_scheduling", "_task_queue")
def __init__(self, **kwargs):
SlotObject.__init__(self, **kwargs)
self._task_queue = deque()
self.running_tasks = set()
if self.max_jobs is None:
self.max_jobs = 1
self._dirty = True
def add(self, task):
self._task_queue.append(task)
self._dirty = True
def addFront(self, task):
self._task_queue.appendleft(task)
self._dirty = True
def schedule(self):
if not self._dirty:
return False
if not self:
return False
if self._scheduling:
# Ignore any recursive schedule() calls triggered via
# self._task_exit().
return False
self._scheduling = True
task_queue = self._task_queue
running_tasks = self.running_tasks
max_jobs = self.max_jobs
state_changed = False
while task_queue and \
(max_jobs is True or len(running_tasks) < max_jobs):
task = task_queue.popleft()
cancelled = getattr(task, "cancelled", None)
if not cancelled:
running_tasks.add(task)
task.addExitListener(self._task_exit)
task.start()
state_changed = True
self._dirty = False
self._scheduling = False
return state_changed
def _task_exit(self, task):
"""
Since we can always rely on exit listeners being called, the set of
running tasks is always pruned automatically and there is never any need
to actively prune it.
"""
self.running_tasks.remove(task)
if self._task_queue:
self._dirty = True
def clear(self):
self._task_queue.clear()
running_tasks = self.running_tasks
while running_tasks:
task = running_tasks.pop()
task.removeExitListener(self._task_exit)
task.cancel()
self._dirty = False
def __bool__(self):
return bool(self._task_queue or self.running_tasks)
if sys.hexversion < 0x3000000:
__nonzero__ = __bool__
def __len__(self):
return len(self._task_queue) + len(self.running_tasks)