Changeset 479:0af671777bc5
- 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
| r477 |
r479 |
|
| | 1 | 2008-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 | |
|---|
| 1 | 9 | 2008-08-29 Brian Warner <warner@lothar.com> |
|---|
| 2 | 10 | |
|---|
| r449 |
r479 |
|
| 1 | 1 | # -*- test-case-name: foolscap.test.test_pb -*- |
|---|
| 2 | 2 | |
|---|
| 3 | | import re |
|---|
| | 3 | import re, time |
|---|
| 4 | 4 | from zope.interface import implements, implementsOnly, implementedBy, Interface |
|---|
| 5 | 5 | from twisted.python import log |
|---|
| 6 | | from twisted.internet import defer, reactor |
|---|
| | 6 | from twisted.internet import defer, reactor, task |
|---|
| 7 | 7 | from foolscap import broker |
|---|
| 8 | 8 | from foolscap import Referenceable, RemoteInterface |
|---|
| … | … | |
| 153 | 153 | return None |
|---|
| 154 | 154 | |
|---|
| | 155 | class TimeoutError(Exception): |
|---|
| | 156 | pass |
|---|
| | 157 | |
|---|
| | 158 | class PollComplete(Exception): |
|---|
| | 159 | pass |
|---|
| | 160 | |
|---|
| 155 | 161 | class PollMixin: |
|---|
| 156 | | def poll(self, check_f, pollinterval=0.01): |
|---|
| | 162 | |
|---|
| | 163 | def poll(self, check_f, pollinterval=0.01, timeout=None): |
|---|
| 157 | 164 | # Return a Deferred, then call check_f periodically until it returns |
|---|
| 158 | 165 | # 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) |
|---|
| 161 | 179 | return d |
|---|
| 162 | 180 | |
|---|
| 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() |
|---|
| 164 | 184 | 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() |
|---|
| 175 | 186 | |
|---|
| 176 | 187 | class StallMixin: |
|---|