Example #1
0
class CronDaemon():
    __metaclass__ = Singleton

    def __init__(self, *events):
        self.events = list(events)
        self.lock = threading.Lock()
        self.thread = StoppableThread(
        )  # the thread the manager loop is running in

    def register(self, event):
        self.lock.acquire()
        try:
            self.events.append(event)
        finally:
            self.lock.release()

    def remove(self, event):
        self.lock.acquire()
        try:
            self.events.remove(event)
        finally:
            self.lock.release()

    def start(self):
        """Start the process loop in a thread."""
        if self.thread.is_alive():
            return
        self.thread = StoppableThread(target=self.run,
                                      name=self.__class__.__name__ +
                                      timestamp())
        self.thread.start()

    def stop(self, timeout=None):
        """Stop the process loop."""
        self.thread.stop(timeout=timeout)

    def run(self):
        logging.getLogger().info('Starting Cron Daemon.')
        t = datetime(*datetime.now().timetuple()[:5])
        while 1:

            self.lock.acquire()
            try:
                logging.getLogger().log(
                    5, 'Checking events at ' + str(datetime.now()) + ' with ' +
                    str(t) + '.')
                for e in self.events:
                    e.check(t)
            finally:
                self.lock.release()

            t += timedelta(minutes=1)
            while datetime.now() < t:
                delta = (t - datetime.now()).total_seconds()
                self.thread.stop_request.wait(delta)
                if self.thread.stop_request.isSet():
                    return
        logging.getLogger().info('Shutting down Cron Daemon.')
Example #2
0
class JobManager(
):  # ToDo: In principle this need not be a singleton. Then there could be different job managers handling different sets of resources. However currently we need singleton since the JobManager is called explicitly on ManagedJob class.
    __metaclass__ = Singleton
    """Provides a queue for starting and stopping jobs according to their priority."""
    def __init__(self):
        self.thread = StoppableThread(
        )  # the thread the manager loop is running in
        self.lock = threading.Condition(
        )  # lock to control access to 'queue' and 'running'
        self.queue = []
        self.running = None
        self.refresh_interval = 0.1  # seconds

    def submit(self, job):
        """
        Submit a job.
        
        If there is no job running, the job is appended to the queue.

        If the job is the running job or the job is already in the queue, do nothing.
        
        If job.priority =< priority of the running job,
            the job is appended to the queue and the queue sorted according to priority.
        
        If job.priority > priority of the running job,
            the job is inserted at the first position of the queue, the running job is stopped
            and inserted again at the first position of the queue.
        """

        logging.debug('Attempt to submit job ' + str(job))
        self.lock.acquire()

        running = self.running
        queue = self.queue

        if job is running or job in queue:
            logging.info('The job ' + str(job) +
                         ' is already running or in the queue.')
            self.lock.release()
            return

        queue.append(job)
        queue.sort(cmp=lambda x, y: cmp(x.priority, y.priority),
                   reverse=True)  # ToDo: Job sorting not thoroughly tested
        job.state = 'wait'

        logging.debug('Notifying process thread.')
        self.lock.notify()

        self.lock.release()
        logging.debug('Job ' + str(job) + ' submitted.')

    def remove(self, job):
        """
        Remove a job.
        
        If the job is running, stop it.
        
        If the job is in the queue, remove it.
        
        If the job is not found, this will result in an exception.
        """

        logging.debug('Attempt to remove job ' + str(job))
        self.lock.acquire()

        try:
            if job is self.running:
                logging.debug('Job ' + str(job) + ' is running. Attempt stop.')
                job.stop()
                logging.debug('Job ' + str(job) + ' removed.')
            else:
                if not job in self.queue:
                    logging.debug('Job ' + str(job) +
                                  ' neither running nor in queue. Returning.')
                else:
                    logging.debug('Job ' + str(job) +
                                  ' is in queue. Attempt remove.')
                    self.queue.remove(job)
                    logging.debug('Job ' + str(job) + ' removed.')
                    job.state = 'idle'  # ToDo: improve handling of state. Move handling to Job?
        finally:
            self.lock.release()

    def start(self):
        """Start the process loop in a thread."""
        if self.thread.is_alive():
            return
        logging.getLogger().info('Starting Job Manager.')
        self.thread = StoppableThread(target=self._process,
                                      name=self.__class__.__name__ +
                                      timestamp())
        self.thread.start()

    def stop(self, timeout=None):
        """Stop the process loop."""
        self.thread.stop_request.set()
        self.lock.acquire()
        self.lock.notify()
        self.lock.release()
        self.thread.stop(timeout=timeout)

    def _process(self):
        """
        The process loop.
        
        Use .start() and .stop() methods to start and stop processing of the queue.
        """

        while True:

            self.thread.stop_request.wait(self.refresh_interval)
            if self.thread.stop_request.isSet():
                break

            # ToDo: jobs can be in queue before process loop is started
            # what happens when manager is stopped while jobs are running?

            self.lock.acquire()
            if self.running is None:
                if self.queue == []:
                    logging.debug(
                        'No job running. No job in queue. Waiting for notification.'
                    )
                    self.lock.wait()
                    logging.debug('Caught notification.')
                    if self.thread.stop_request.isSet():
                        self.lock.release()
                        break
                logging.debug('Attempt to fetch first job in queue.')
                self.running = self.queue.pop(0)
                logging.debug('Found job ' + str(self.running) + '. Starting.')
                self.running.start()
            elif not self.running.thread.is_alive():
                print '\x07'  # beep
                logging.debug('Job ' + str(self.running) + ' stopped.')
                self.running = None
                if self.queue != []:
                    logging.debug('Attempt to fetch first job in queue.')
                    self.running = self.queue.pop(0)
                    logging.debug('Found job ' + str(self.running) +
                                  '. Starting.')
                    self.running.start()
            elif self.queue != [] and self.queue[
                    0].priority > self.running.priority:
                logging.debug(
                    'Found job ' + str(self.queue[0]) +
                    ' in queue with higher priority than running job. Attempt to stop running job.'
                )
                self.running.stop()
                if self.running.state != 'done':
                    logging.debug('Reinserting job ' + str(self.running) +
                                  ' in queue.')
                    self.queue.insert(0, self.running)
                    self.queue.sort(
                        cmp=lambda x, y: cmp(x.priority, y.priority),
                        reverse=True
                    )  # ToDo: Job sorting not thoroughly tested
                    self.running.state = 'wait'
                self.running = self.queue.pop(0)
                logging.debug('Found job ' + str(self.running) + '. Starting.')
                self.running.start()
            self.lock.release()