Example #1
0
class AbstractEntry(object):
    def currentsystime():
        return time.time()

    currentsystime = staticmethod(currentsystime)

    def currentuptime():
        return uptime.secs()

    currentuptime = staticmethod(currentuptime)

    def __init__(self, schedule, target, args):
        super(AbstractEntry, self).__init__()
        self._schedule = schedule
        self._target = target
        self._args = args
        self._setupflags()
        self._statelock = Lock()
        self.computewhen()

    def runswhen(self):
        """
            Return up-time value at which entry is 
            scheduled to run.  This value is calculated 
            and updated when 'computewhen()' is invoked; 
            value returned is always the value configured 
            by most recent computewhen() call.
        """
        return self._targetuptime

    def computewhen(self):
        """
            Calculate, or recalculate, target run time 
            value in terms of system up time.
            
            NOTE - this method uses dynamic 'runuptime()' 
            method to get up to date run time target value.
        """
        self._targetuptime = self.runuptime()

    def runuptime(self):
        """
            Dynamically generated target up-time value.  
            This value is used interally by 'computewhen()' 
            in order to configure the entry's run time 
            target.
            
            NOTE - this method uses dynamic 'rundelay()' 
            method to get offset from current up-time and 
            calculate target up-time.
        """
        return self.currentuptime() + self.rundelay()

    def rundelay(self, currenttime=None):
        """
            Return delay, in seconds, between now and when entry runs.
        """
        raise NotImplementedError('abstract rundelay() must be overridden')

    def _setupflags(self):
        self._cancelled = Flag(False)
        self._executed = Flag(False)
        self._executing = Flag(False)
        self._failed = Flag(False)
        self._scheduled = Flag(True)

    def scheduled(self):
        self._statelock.acquire()
        try:
            return self._scheduled.isSet()
        finally:
            self._statelock.release()

    def cancelled(self):
        self._statelock.acquire()
        try:
            return self._cancelled.isSet()
        finally:
            self._statelock.release()

    def executing(self):
        self._statelock.acquire()
        try:
            return self._executing.isSet()
        finally:
            self._statelock.release()

    def executing_caller(self):
        """
            Returns True if caller is currently running 
            as entry's execution 
        """
        self._statelock.acquire()
        try:
            executing = self._executing.isSet()
            return executing and self._schedule is current_thread()
        finally:
            self._statelock.release()

    def isdue(self):
        return self.rundelay() <= 0

    def executable(self):
        """
            Return True if entry may still be executed.  An 
            entry may be executed if it is not currently 
            executing and has not yet been executed.
        """
        self._statelock.acquire()
        try:
            return self._executable()
        finally:
            self._statelock.release()

    def overdue(self):
        """
            A entry is overdue iff it may still be executed 
            and the current uptime is greater than the target 
            uptime at which the entry was scheduled to execute.
        """
        # Order chosen to avoid race-condition.
        self._statelock.acquire()
        try:
            return self._overdue()
        finally:
            self._statelock.release()

    def executed(self):
        self._statelock.acquire()
        try:
            return self._executed.isSet()
        finally:
            self._statelock.release()

    def expired(self):
        self._statelock.acquire()
        try:
            return self._expired()
        finally:
            self._statelock.release()

    ##
    # No semi-private test-method can implement locking.
    # This allows indescriminate use of methods versus
    # direct invocation of flag instances without
    # making it easy to accidentally block on double-lock.
    def _expired(self):
        return self._executed() or self._cancelled()

    def _executable(self):
        return not (self._executing() or self._expired())

    def _overdue(self):
        return (self.currentuptime() > self.runuptime()) and self._executable()

    def runsystime(self):
        """
            Return time at which entry is supposed to run.
        """
        return self.rundelay() + self.currentsystime()

    def execute(self):
        self._statelock.acquire()
        try:
            if not self._executable():
                raise EActionExpired('Scheduled action %s has expired' % self)
            else:
                self._executing.set()
        finally:
            self._statelock.release()
        try:
            try:
                value = self._target(*self._args)
            except:
                self._failed.set()
                message = 'Entry Exception running: "%s%s"'
                msglog.log('broadway', msglog.types.DB,
                           message % (self._target, self._args))
                raise
            else:
                self._failed.clear()
        finally:
            self._statelock.acquire()
            # Ordering chosen to prefer overlap than gap.
            self._executed.set()
            self._executing.clear()
            self._scheduled.clear()
            self._statelock.release()
        return value

    def cancel(self):
        self._statelock.acquire()
        try:
            self._cancelled.set()
            if self._scheduled():
                try:
                    self._schedule.remove_entry(self)
                except IndexError:
                    message = 'IndexError removing %s (at %#x)'
                    self._schedule.debugout(message, 1, self, id(self))
            self._scheduled.clear()
        finally:
            self._statelock.release()
        self._schedule.debugout('%s cancelled', 1, self)

    def __bigstr__(self):
        status = ['%s' % type(self).__name__]
        status.append(self._get_actionstr())
        if self._executable():
            execution = 'pending'
        if self._executed():
            status.append('executed')
            if self._failed():
                status.append('failed')
            else:
                status.append('succeeded')
        elif self._executing():
            status.append('executing')
        elif self._cancelled():
            status.append('cancelled')
        elif self._overdue():
            status.append('overdue')
        runtime = self.runuptime()
        status.append('due %0.2f' % runtime)
        rundelay = runtime - self.currentuptime()
        if rundelay < 0:
            sign = '-'
        else:
            sign = '+'
        status.append('(now %s %0.2f sec)' % (sign, abs(rundelay)))
        return ' '.join(status)

    def __littlestr__(self):
        state = 'Pending'
        if self._executed:
            state = 'Executed'
            if self._failed:
                state += ', Failed'
            else:
                state += ', Succeeded'
        elif self._cancelled:
            state = 'Cancelled'
        elif self._overdue():
            state = 'Overdue'
        return ('Scheduled Action: %s%s due %s -> %s' %
                (self._target, self._args, self.runswhen(), state))

    __str__ = __bigstr__

    def _get_actionstr(self):
        action = self._target
        description = [str(action)]
        try:
            if inspect.ismethod(action):
                if action.im_self is not None:
                    description.insert(0, type(action.im_self).__name__)
                description[-1] = action.im_func.__name__
            elif inspect.isfunction(action):
                description[-1] = action.__name__
            elif inspect.isclass(action):
                description.insert(0, action.__name__)
                description.pop()
        except:
            pass
        return '%s%s' % ('.'.join(description), self._args)
Example #2
0
class AbstractEntry(object):
    def currentsystime():
        return time.time()

    currentsystime = staticmethod(currentsystime)

    def currentuptime():
        return uptime.secs()

    currentuptime = staticmethod(currentuptime)

    def __init__(self, schedule, target, args):
        super(AbstractEntry, self).__init__()
        self._schedule = schedule
        self._target = target
        self._args = args
        self._setupflags()
        self._statelock = Lock()
        self.computewhen()

    def runswhen(self):
        """
            Return up-time value at which entry is 
            scheduled to run.  This value is calculated 
            and updated when 'computewhen()' is invoked; 
            value returned is always the value configured 
            by most recent computewhen() call.
        """
        return self._targetuptime

    def computewhen(self):
        """
            Calculate, or recalculate, target run time 
            value in terms of system up time.
            
            NOTE - this method uses dynamic 'runuptime()' 
            method to get up to date run time target value.
        """
        self._targetuptime = self.runuptime()

    def runuptime(self):
        """
            Dynamically generated target up-time value.  
            This value is used interally by 'computewhen()' 
            in order to configure the entry's run time 
            target.
            
            NOTE - this method uses dynamic 'rundelay()' 
            method to get offset from current up-time and 
            calculate target up-time.
        """
        return self.currentuptime() + self.rundelay()

    def rundelay(self, currenttime=None):
        """
            Return delay, in seconds, between now and when entry runs.
        """
        raise NotImplementedError("abstract rundelay() must be overridden")

    def _setupflags(self):
        self._cancelled = Flag(False)
        self._executed = Flag(False)
        self._executing = Flag(False)
        self._failed = Flag(False)
        self._scheduled = Flag(True)

    def scheduled(self):
        self._statelock.acquire()
        try:
            return self._scheduled.isSet()
        finally:
            self._statelock.release()

    def cancelled(self):
        self._statelock.acquire()
        try:
            return self._cancelled.isSet()
        finally:
            self._statelock.release()

    def executing(self):
        self._statelock.acquire()
        try:
            return self._executing.isSet()
        finally:
            self._statelock.release()

    def executing_caller(self):
        """
            Returns True if caller is currently running 
            as entry's execution 
        """
        self._statelock.acquire()
        try:
            executing = self._executing.isSet()
            return executing and self._schedule is current_thread()
        finally:
            self._statelock.release()

    def isdue(self):
        return self.rundelay() <= 0

    def executable(self):
        """
            Return True if entry may still be executed.  An 
            entry may be executed if it is not currently 
            executing and has not yet been executed.
        """
        self._statelock.acquire()
        try:
            return self._executable()
        finally:
            self._statelock.release()

    def overdue(self):
        """
            A entry is overdue iff it may still be executed 
            and the current uptime is greater than the target 
            uptime at which the entry was scheduled to execute.
        """
        # Order chosen to avoid race-condition.
        self._statelock.acquire()
        try:
            return self._overdue()
        finally:
            self._statelock.release()

    def executed(self):
        self._statelock.acquire()
        try:
            return self._executed.isSet()
        finally:
            self._statelock.release()

    def expired(self):
        self._statelock.acquire()
        try:
            return self._expired()
        finally:
            self._statelock.release()

    ##
    # No semi-private test-method can implement locking.
    # This allows indescriminate use of methods versus
    # direct invocation of flag instances without
    # making it easy to accidentally block on double-lock.
    def _expired(self):
        return self._executed() or self._cancelled()

    def _executable(self):
        return not (self._executing() or self._expired())

    def _overdue(self):
        return (self.currentuptime() > self.runuptime()) and self._executable()

    def runsystime(self):
        """
            Return time at which entry is supposed to run.
        """
        return self.rundelay() + self.currentsystime()

    def execute(self):
        self._statelock.acquire()
        try:
            if not self._executable():
                raise EActionExpired("Scheduled action %s has expired" % self)
            else:
                self._executing.set()
        finally:
            self._statelock.release()
        try:
            try:
                value = self._target(*self._args)
            except:
                self._failed.set()
                message = 'Entry Exception running: "%s%s"'
                msglog.log("broadway", msglog.types.DB, message % (self._target, self._args))
                raise
            else:
                self._failed.clear()
        finally:
            self._statelock.acquire()
            # Ordering chosen to prefer overlap than gap.
            self._executed.set()
            self._executing.clear()
            self._scheduled.clear()
            self._statelock.release()
        return value

    def cancel(self):
        self._statelock.acquire()
        try:
            self._cancelled.set()
            if self._scheduled():
                try:
                    self._schedule.remove_entry(self)
                except IndexError:
                    message = "IndexError removing %s (at %#x)"
                    self._schedule.debugout(message, 1, self, id(self))
            self._scheduled.clear()
        finally:
            self._statelock.release()
        self._schedule.debugout("%s cancelled", 1, self)

    def __bigstr__(self):
        status = ["%s" % type(self).__name__]
        status.append(self._get_actionstr())
        if self._executable():
            execution = "pending"
        if self._executed():
            status.append("executed")
            if self._failed():
                status.append("failed")
            else:
                status.append("succeeded")
        elif self._executing():
            status.append("executing")
        elif self._cancelled():
            status.append("cancelled")
        elif self._overdue():
            status.append("overdue")
        runtime = self.runuptime()
        status.append("due %0.2f" % runtime)
        rundelay = runtime - self.currentuptime()
        if rundelay < 0:
            sign = "-"
        else:
            sign = "+"
        status.append("(now %s %0.2f sec)" % (sign, abs(rundelay)))
        return " ".join(status)

    def __littlestr__(self):
        state = "Pending"
        if self._executed:
            state = "Executed"
            if self._failed:
                state += ", Failed"
            else:
                state += ", Succeeded"
        elif self._cancelled:
            state = "Cancelled"
        elif self._overdue():
            state = "Overdue"
        return "Scheduled Action: %s%s due %s -> %s" % (self._target, self._args, self.runswhen(), state)

    __str__ = __bigstr__

    def _get_actionstr(self):
        action = self._target
        description = [str(action)]
        try:
            if inspect.ismethod(action):
                if action.im_self is not None:
                    description.insert(0, type(action.im_self).__name__)
                description[-1] = action.im_func.__name__
            elif inspect.isfunction(action):
                description[-1] = action.__name__
            elif inspect.isclass(action):
                description.insert(0, action.__name__)
                description.pop()
        except:
            pass
        return "%s%s" % (".".join(description), self._args)
Example #3
0
 def _setupflags(self):
     self._cancelled = Flag(False)
     self._executed = Flag(False)
     self._executing = Flag(False)
     self._failed = Flag(False)
     self._scheduled = Flag(True)
Example #4
0
 def _setupflags(self):
     self._cancelled = Flag(False)
     self._executed = Flag(False)
     self._executing = Flag(False)
     self._failed = Flag(False)
     self._scheduled = Flag(True)