Esempio n. 1
0
    def testIncrInterval(self):
        """\
        Mock time can be auto incremented
        """
        import dvbcss.monotonic_time as monotonic_time

        mockUnderTest = MockTime()
        mockUnderTest.install(monotonic_time)

        try:
            mockUnderTest.timeNow = 5

            # no auto increment by Default
            for i in range(0, 10000):
                self.assertEquals(5, monotonic_time.time())

            mockUnderTest.enableAutoIncrementBy(0.1,
                                                numReadsBetweenIncrements=5)
            for i in range(0, 100):
                for j in range(0, 5):
                    self.assertAlmostEquals(5 + i * 0.1,
                                            monotonic_time.time(),
                                            delta=0.0001)

        finally:
            mockUnderTest.uninstall()
Esempio n. 2
0
 def testTime(self):
     import dvbcss.monotonic_time as monotonic_time
 
     mockUnderTest = MockTime()
     mockUnderTest.install(monotonic_time)
     try:
         mockUnderTest.timeNow = 1234
         self.assertEquals(monotonic_time.time(), 1234)
         
         mockUnderTest.timeNow = -485.2701
         self.assertEquals(monotonic_time.time(), -485.2701)
     finally:
         mockUnderTest.uninstall()
Esempio n. 3
0
    def testTime(self):
        import dvbcss.monotonic_time as monotonic_time

        mockUnderTest = MockTime()
        mockUnderTest.install(monotonic_time)
        try:
            mockUnderTest.timeNow = 1234
            self.assertEquals(monotonic_time.time(), 1234)

            mockUnderTest.timeNow = -485.2701
            self.assertEquals(monotonic_time.time(), -485.2701)
        finally:
            mockUnderTest.uninstall()
Esempio n. 4
0
    def test_time_increments(self):
        """Check time flows at roughly the right rate"""
        t=self.t
        m=self.m

        start_t = t.time()
        start_m = m.time()
        t.sleep(5)
        end_t = t.time()
        end_m = m.time()
        
        diff_t = (end_t - start_t)
        diff_m = (end_m - start_m)

        diff = diff_t - diff_m
        
        self.assertLess(abs(diff), 0.05, "Time is within 1% between monotonic_time and time")
Esempio n. 5
0
    def test_time_increments(self):
        """Check time flows at roughly the right rate"""
        t = self.t
        m = self.m

        start_t = t.time()
        start_m = m.time()
        t.sleep(5)
        end_t = t.time()
        end_m = m.time()

        diff_t = (end_t - start_t)
        diff_m = (end_m - start_m)

        diff = diff_t - diff_m

        self.assertLess(abs(diff), 0.05,
                        "Time is within 1% between monotonic_time and time")
Esempio n. 6
0
def algorithmWrapper(dest, measureClock, algorithm):
    """\
    UdpRequestResponseClient handler function that wraps up the act of sending and receiving a WallClockMessage.
    Also handles the optional "follow-up" response type of message. If a response is received that indicates
    a follow-up is due, it will also wait for the follow-up. However the total time it will wait since the
    original request will not exceed the specified timeout. If only a response-promising-follow-up is received
    then that is what shall be returned.
    
    In turn, you plug an algorithm into it that is also a generator function. However that algorithm
    need only yield the timeout, and the return value will be a :class:`~dvbcss.protocol.wc.Candidate`
    object in units of nanoseconds. If timeout occurs the return value is None instead of a dict.
    
    :param dest: ("<ip-addr>",port) The destination address of the server (to which the requests should be sent)
    :param measureClock: The :mod:`~dvbcss:Clock` from which the readings are taken (being `t1` and `t4` in the resulting candidate)
    :param algorithm: A generator that yields to request a WallClock request be sent, and acts on the responses.
    
    The generator function you provide, should use `yield` as follows:
    * to pass the timeout for waiting for a response as the yield value
    * to receive a :class:`~dvbcss.protocol.wc.Candidate` object representing the response.
                
    Example algorithm:
    
    .. code-block:: python
    
      def algorithm():
          timeoutSecs=0.2
          while True:
              candidate=(yield timeoutSecs)
              if candidate is not None:
                  print "Candidate received! ",candidate
              else:
                  print "Timeout"
              print "Now waiting 1 second"
              time.sleep(1)
              
       sysClock = SysClock()
       wallClock = CorrelatedClock(sysClock, tickRate=1000000000)
              
       algWrapper = algorithmWrapper(destIpPort, sysClock, wallClock, algorithm())
       
    """
    try:
        # hand control to the algorithm. when it wants a request sent, it will
        # return and supply the timeout for waiting for a response
        timeoutSecs = algorithm.next()
        while True:
            # assemble a request
            reqMsg = WCMessage(WCMessage.TYPE_REQUEST, 0, 0,
                               measureClock.nanos, 0, 0)
            toSend = reqMsg.pack(), dest

            # we'll send the request then seek the best quality response
            # until timeout, or terminating early if we get a quality > 2
            # response (meaning that it was a non-followed-up response or a
            # follow-up response). A lower quality response is one where
            # a follow-up is expected or if it related to a previous request

            # this design means it will pick the best response, but in the
            # absence of a reasonable one, it will still make do with whatever
            # it can get (e.g. a response relating to an earlier request)

            responseQuality = -999
            responseMsg = None
            responseRecvNanos = None

            timeoutBy = time.time() + timeoutSecs
            remainingTime = timeoutBy - time.time()

            while responseQuality < 3 and remainingTime > 0:
                # wait for a response. if first time round, send the request too
                latestResponse, src = (yield toSend, remainingTime)
                toSend = None

                # note when response was received
                latestResponseNanos = measureClock.nanos

                # did we get a response? did it come from the server we sent
                # the request to?
                if latestResponse is not None and src == dest:

                    # assess the response and work out if better than any previous
                    # response we're received
                    latestResponseMsg = WCMessage.unpack(latestResponse)
                    newQuality = calcQuality(reqMsg, latestResponseMsg)
                    if newQuality >= responseQuality:
                        responseQuality = newQuality
                        responseMsg = latestResponseMsg
                        responseRecvNanos = latestResponseNanos

                # work out how long left until timeout
                remainingTime = timeoutBy - time.time()

            if responseMsg is not None:
                candidate = Candidate(responseMsg, responseRecvNanos)
            else:
                # no message, no candidate
                candidate = None
            # pass the result to the algorithm, and wait for it to return when it
            # next wants a request to be sent (again supplying the response timeout)
            timeoutSecs = algorithm.send(candidate)
    except StopIteration:
        pass
Esempio n. 7
0
 def ticks(self):
     return int(time.time() * self._freq)
Esempio n. 8
0
def algorithmWrapper(dest,clock,algorithm):
    """\
    UdpRequestResponseClient handler function that wraps up the act of sending and receiving a WallClockMessage.
    Also handles the optional "follow-up" response type of message. If a response is received that indicates
    a follow-up is due, it will also wait for the follow-up. However the total time it will wait since the
    original request will not exceed the specified timeout. If only a response-promising-follow-up is received
    then that is what shall be returned.
    
    In turn, you plug an algorithm into it that is also a generator function. However that algorithm
    need only yield the timeout, and the return value will be a dict containing 2 candidate objects. One in
    nanoseconds, and the other the same but converted to tick units according to the tick rate of the
    supplied clock object. If timeout occurs the return value is None instead of a dict.
    
    Arguments:
    dest = ("<ip-addr>",port)
    clock = clock that will be used for tick rate conversion
    algorithm = a generator that yields to request a WallClock request be sent, and supplies the timeout
                for waiting for a response as the yield value
                then expects to be sent the candidate object representing the result of the request response
                
    Example algorithm:
    
      def algorithm():
          timeoutSecs=0.2
          while True:
              candidate=(yield timeoutSecs)
              if candidate is not None:
                  print "Candidate received! ",candidate["ticks"]
              else:
                  print "Timeout"
              print "Now waiting 1 second"
              time.sleep(1)
    """
    try:
        # hand control to the algorithm. when it wants a request sent, it will
        # return and supply the timeout for waiting for a response
        timeoutSecs = algorithm.next()
        while True:
            # assemble a request
            reqMsg=WCMessage(WCMessage.TYPE_REQUEST, 0, 0, clock.nanos, 0, 0)
            toSend=reqMsg.pack(), dest
            
            # we'll send the request then seek the best quality response
            # until timeout, or terminating early if we get a quality > 2
            # response (meaning that it was a non-followed-up response or a
            # follow-up response). A lower quality response is one where
            # a follow-up is expected or if it related to a previous request
            
            # this design means it will pick the best response, but in the
            # absence of a reasonable one, it will still make do with whatever
            # it can get (e.g. a response relating to an earlier request)  
             
            responseQuality   = -999
            responseMsg       = None
            responseRecvNanos = None

            timeoutBy     = time.time() + timeoutSecs
            remainingTime = timeoutBy - time.time()

            while responseQuality < 3 and remainingTime > 0:
                # wait for a response. if first time round, send the request too
                latestResponse,src = (yield toSend,remainingTime)
                toSend=None      
                
                # note when response was received
                latestResponseNanos=clock.nanos
                
                
                # did we get a response? did it come from the server we sent
                # the request to?
                if latestResponse is not None and src == dest:

                    # assess the response and work out if better than any previous
                    # response we're received
                    latestResponseMsg = WCMessage.unpack(latestResponse)
                    newQuality=calcQuality(reqMsg, latestResponseMsg) 
                    if newQuality >= responseQuality:
                        responseQuality=newQuality
                        responseMsg=latestResponseMsg
                        responseRecvNanos=latestResponseNanos
                        
                # work out how long left until timeout
                remainingTime=timeoutBy - time.time()
                        

            if responseMsg is not None:
                candidate=Candidate(responseMsg,responseRecvNanos)
                candidate={"nanos":candidate, "ticks":candidate.toTicks(clock)}
            else:
                # no message, no candidate
                candidate=None
            # pass the result to the algorithm, and wait for it to return when it
            # next wants a request to be sent (again supplying the response timeout)
            timeoutSecs=algorithm.send(candidate)
    except StopIteration:
        pass
Esempio n. 9
0
    def run(self):
        r"""\
        Main runloop of the scheduler.
        
        While looping:
        
          1. Checks the queue of tasks to be added to the scheduler
          
             The time the task is due to be executed is calculated and used
             as the sort key when the task is inserted into a priority queue.
          
          2. Checks any queued requests to reschedule tasks (due to clock adjustments)
            
             The existing task in the scheduler priority queue is "deprecated"
             And a new task is scheduled with the revised time of execution
          
          3. checks any tasks that need to now be executed
            
            Dequeues them and executes them, or ignores them if they are marked as deprecated
        """
        while self.running:
            self.updateEvent.clear()
            
            # check for tasks to add to the scheduler
            while not self.addQueue.empty():
                clock, whenTicks, callBack, args, kwargs = self.addQueue.get_nowait()
                task = _Task(clock, whenTicks, callBack, args, kwargs)
                heapq.heappush(self.taskheap, (task.when, task))

                if clock not in self.clock_Tasks:
                    self.clock_Tasks[clock] = { task:True }
                    clock.bind(self)
                else:
                    self.clock_Tasks[clock][task] = True
            
            # check if there are any requests to reschedule tasks (due to changes in clocks)
            while not self.rescheduleQueue.empty():
                clock = self.rescheduleQueue.get_nowait()
                tasksMap=self.clock_Tasks.get(clock, {})
                for task in tasksMap.keys():
                    newTask=task.regenerateAndDeprecate()
                    heapq.heappush(self.taskheap,(newTask.when, newTask))
                    tasksMap[newTask] = True
                    del tasksMap[task]
 
                    
            # process pending tasks
            while self.taskheap and (self.taskheap[0][1].deleted or time.time() >= self.taskheap[0][0]):
                (_,task) = heapq.heappop(self.taskheap)

                if not task.deleted:
                    try:
                        task.callBack(*task.args, **task.kwargs)
                    except Exception, e:
                        self.log.error("Exception in scheduling thread: " + str(e))

                    # remove from list of tasks that clock notification may need to reschedule    
                    del self.clock_Tasks[task.clock][task]
                    if not self.clock_Tasks[task.clock]:
                        task.clock.unbind(self)
                        del self.clock_Tasks[task.clock]
                    
            # wait for next tasks's scheduled time, or to be woken by the update event
            # due to new tasks being scheduled or a rescheduling of a clock
            if self.taskheap:
                self.updateEvent.wait(self.taskheap[0][0] - time.time())
            else:
                self.updateEvent.wait()
Esempio n. 10
0
    def run(self):
        r"""\
        Main runloop of the scheduler.
        
        While looping:
        
          1. Checks the queue of tasks to be added to the scheduler
          
             The time the task is due to be executed is calculated and used
             as the sort key when the task is inserted into a priority queue.
          
          2. Checks any queued requests to reschedule tasks (due to clock adjustments)
            
             The existing task in the scheduler priority queue is "deprecated"
             And a new task is scheduled with the revised time of execution
          
          3. checks any tasks that need to now be executed
            
            Dequeues them and executes them, or ignores them if they are marked as deprecated
        """
        while self.running:
            self.updateEvent.clear()

            # check for tasks to add to the scheduler
            while not self.addQueue.empty():
                clock, whenTicks, callBack, args, kwargs = self.addQueue.get_nowait(
                )
                task = _Task(clock, whenTicks, callBack, args, kwargs)
                if not math.isnan(task.when):
                    heapq.heappush(self.taskheap, (task.when, task))

                if clock not in self.clock_Tasks:
                    self.clock_Tasks[clock] = {task: True}
                    clock.bind(self)
                else:
                    self.clock_Tasks[clock][task] = True

            # check if there are any requests to reschedule tasks (due to changes in clocks)
            while not self.rescheduleQueue.empty():
                clock = self.rescheduleQueue.get_nowait()
                tasksMap = self.clock_Tasks.get(clock, {})
                for task in tasksMap.keys():
                    newTask = task.regenerateAndDeprecate()
                    if not math.isnan(newtask.when):
                        heapq.heappush(self.taskheap, (newTask.when, newTask))
                    tasksMap[newTask] = True
                    del tasksMap[task]

            # process pending tasks
            while self.taskheap and (self.taskheap[0][1].deleted
                                     or time.time() >= self.taskheap[0][0]):
                (_, task) = heapq.heappop(self.taskheap)

                if not task.deleted:
                    try:
                        task.callBack(*task.args, **task.kwargs)
                    except Exception, e:
                        self.log.error("Exception in scheduling thread: " +
                                       str(e))

                    # remove from list of tasks that clock notification may need to reschedule
                    del self.clock_Tasks[task.clock][task]
                    if not self.clock_Tasks[task.clock]:
                        task.clock.unbind(self)
                        del self.clock_Tasks[task.clock]

            # wait for next tasks's scheduled time, or to be woken by the update event
            # due to new tasks being scheduled or a rescheduling of a clock
            if self.taskheap:
                self.updateEvent.wait(self.taskheap[0][0] - time.time())
            else:
                self.updateEvent.wait()