<html><body>On 04:35 pm, daniel@keystonewood.com wrote:<br />&gt;On Apr 3, 2007, at 8:42 PM, Itamar Shtull-Trauring wrote:<br />&gt;&gt;On Tue, 2007-04-03 at 17:07 -0400, Daniel Miller wrote:<br />&gt;&gt;&gt;&gt;twisted.internet.defer.DeferredLock and some of the related classes<br />&gt;&gt;&gt;&gt;are<br />&gt;&gt;&gt;&gt;what you ought to be using.<br />&gt;&gt;&gt;<br />&gt;&gt;&gt;Unfortunately that only gets me half way there. DeferredLock.acquire<br />&gt;&gt;&gt;() returns a deferred. How do I return the result of a deferred from<br />&gt;&gt;&gt;a PB remote_xxx() function?<br />&gt;&gt;<br />&gt;&gt;Just return the Deferred from the remote_xxx() function.<br />&gt;<br />&gt;Thanks, I didn't know I could return a deferred from a PB remote_xxx () <br />&gt;method. That detail doesn't seem to be documented in the &#160;Perspective Broker <br />&gt;documentation, which I have read quite a few &#160;times.<br /><br />The PB documentation is not too great. &#160;Perhaps this paper would be helpful to you, if you haven't seen it:<br /><br />http://www.lothar.com/tech/papers/PyCon-2003/pb-pycon/pb.html#auto7<br /><br />&#160; &#160; """<br />&#160; &#160; In addition, the remote method can itself return a Deferred instead of an<br />&#160; &#160; actual return value. When that Deferreds fires, the data given to the<br />&#160; &#160; callback will be serialized and returned to the original caller.<br />&#160; &#160; """<br /><br />&gt; Maybe this could be <br />&gt;highlighted in the "Complete Example" [0] &#160;section of the PB usage <br />&gt;documentation? The examples use the &#160;TwistedQuotes application, and the <br />&gt;IQuoter.getQuote() method always &#160;returns a string (at least I couldn't find <br />&gt;any implementations that &#160;return a deferred).<br /><br />Please feel free to write some patches for the documentation, or open a doc bug describing this issue in more detail. &#160;It's definitely an under-documented feature of PB.<br /><br />&gt;However, that would require <br />&gt;rewriting most if not &#160;all implementations of IQuoter to return deferred's <br />&gt;and/or the code &#160;that calls IQuoter.getQuote(), which demonstrates the viral <br />&gt;nature of &#160;twisted when used in conjunction with other libraries.<br /><br />I don't think that would really be the terrible burden that you suggest, considering the relatively small amount of tutorial documentation that implements or calls IQuoter. &#160;One could also propose a separate interface, IDeferredQuoter, to make the distinction clearer.<br /><br />&gt;So anyway, I rewrote my server-side library to do it the twisted way &#160;and <br />&gt;return deferred's instead trying rig up some way of waiting for &#160;them. I <br />&gt;still think it would be super useful to be able to pseudo- block on a <br />&gt;deferred (i.e. allow the reactor to process other events &#160;while waiting for <br />&gt;the deferred). It is very annoying to have to &#160;rewrite many layers of code <br />&gt;when twisted is introduced into a &#160;program. I did find gthreadless.py, and <br />&gt;maybe that would do it. &#160;Unfortunately discussion on that seems to have been <br />&gt;dropped some time &#160;ago...<br /><br />I'm afraid that the feature you want doesn't make any sense and is, in a broad sense, impossible. &#160;There are some things like it which might be possible - for example, http://twistedmatrix.com/trac/ticket/2545 - but the reactor is not reentrant and in some sense could not be made reentrant.<br /><br />Consider this innocuous looking block of code:<br /><br />&#160; &#160; from twisted.internet.protocol import Protocol<br />&#160; &#160; from make_believe import magicallyBlockOn<br /><br />&#160; &#160; class MagicalProtocol(Protocol):<br />&#160; &#160; &#160; &#160; def dataReceived(self, data):<br />&#160; &#160; &#160; &#160; &#160; &#160; commands = (self.buf + data).split()<br />&#160; &#160; &#160; &#160; &#160; &#160; self.buf = commands[-1]<br />&#160; &#160; &#160; &#160; &#160; &#160; for command in commands[:-1]:<br />&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; if command == 'QUIT':<br />&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; self.transport.loseConnection()<br />&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; return<br />&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; else:<br />&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; # Deferreds are hard, let's go shopping<br />&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; page = magicallyBlockOn(getPage("http://example.com/%s" %<br />&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; (command,)))<br />&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; self.transport.write("SIZE:"+len(page))<br /><br />If you were using Deferreds to track the result of the 'getPage' operation, you could cancel the callbacks that write to the transport in connectionLost. &#160;However, with magical blocking, one dataReceived method might be interleaved with another. &#160;That means that every time through the loop, you have to check to see if the transport has already been disconnected - the code as presented here is buggy and will spuriously fail depending on the order of the connection being lost and the remote page being retrieved.<br /><br />In this example I've been careful to accumulate all the buffer-management and parsing logic at the top of the method, before any potential re-entrancy can happen, but other code (most everything in Twisted's existing protocol implementations, not to mention just about all application code) would not be so lucky.<br /><br />It might be quite feasible to implement a microthreaded runtime environment that lived on _top_ of Twisted and explicitly accounted for issues like these, but that would not really be substantially different than 2.5+inlineCallbacks.<br /><br />&gt;For the record, I've included updated versions of the previously &#160;posted <br />&gt;code below. I'd be happy if someone pointed out if I'm doing &#160;anything wrong <br />&gt;(with respect to twisted) in this code.<br /><br />Nothing immediately jumps out at me. &#160;I've had to write similar code in the past, though, and when I've had to do that, an explicit state machine for the state of the subprocess (or whatever asynchronous resource must be acquired) has been easier to deal with than a lock-oriented approach to it.<br /><br /></body></html>