Changeset 479:0af671777bc5

Show
Ignore:
Timestamp:
09/02/08 20:41:25 (4 months ago)
Author:
Brian Warner <warner@allmydata.com>
branch:
default
Message:

PollMixin?: use LoopingCall?, not chained Deferreds. Closes #95.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • ChangeLog

    r477 r479  
     12008-09-02  Brian Warner  <warner@lothar.com> 
     2 
     3        * foolscap/test/common.py (PollMixin): replace the use of chained 
     4        Deferreds with a task.LoopingCall-based version, from Tahoe. This 
     5        avoids the weird and annoying maximum-recursion-depth-exceeded 
     6        error that occurs when the check function is called more than 
     7        about 600 times. Closes #95. 
     8 
    192008-08-29  Brian Warner  <warner@lothar.com> 
    210 
  • foolscap/test/common.py

    r449 r479  
    11# -*- test-case-name: foolscap.test.test_pb -*- 
    22 
    3 import re 
     3import re, time 
    44from zope.interface import implements, implementsOnly, implementedBy, Interface 
    55from twisted.python import log 
    6 from twisted.internet import defer, reactor 
     6from twisted.internet import defer, reactor, task 
    77from foolscap import broker 
    88from foolscap import Referenceable, RemoteInterface 
     
    153153        return None 
    154154 
     155class TimeoutError(Exception): 
     156    pass 
     157 
     158class PollComplete(Exception): 
     159    pass 
     160 
    155161class PollMixin: 
    156     def poll(self, check_f, pollinterval=0.01): 
     162 
     163    def poll(self, check_f, pollinterval=0.01, timeout=None): 
    157164        # Return a Deferred, then call check_f periodically until it returns 
    158165        # True, at which point the Deferred will fire.. If check_f raises an 
    159         # exception, the Deferred will errback. 
    160         d = defer.maybeDeferred(self._poll, None, check_f, pollinterval) 
     166        # exception, the Deferred will errback. If the check_f does not 
     167        # indicate success within timeout= seconds, the Deferred will 
     168        # errback. If timeout=None, no timeout will be enforced, and the loop 
     169        # will poll forever (or really until Trial times out). 
     170        cutoff = None 
     171        if timeout is not None: 
     172            cutoff = time.time() + timeout 
     173        lc = task.LoopingCall(self._poll, check_f, cutoff) 
     174        d = lc.start(pollinterval) 
     175        def _convert_done(f): 
     176            f.trap(PollComplete) 
     177            return None 
     178        d.addErrback(_convert_done) 
    161179        return d 
    162180 
    163     def _poll(self, res, check_f, pollinterval): 
     181    def _poll(self, check_f, cutoff): 
     182        if cutoff is not None and time.time() > cutoff: 
     183            raise TimeoutError() 
    164184        if check_f(): 
    165             return True 
    166         # N.B.: this chain-the-deferreds pattern breaks when the loop must be 
    167         # run more than about 300 times, because when check_f() finally 
    168         # returns True and stops recursing, all of the Deferreds that have 
    169         # stacked up must be fired, and that runs into python's recursion 
    170         # limit. 
    171         d = defer.Deferred() 
    172         d.addCallback(self._poll, check_f, pollinterval) 
    173         reactor.callLater(pollinterval, d.callback, None) 
    174         return d 
     185            raise PollComplete() 
    175186 
    176187class StallMixin: