On Tue, Feb 24, 2015 at 8:25 AM, Alan Conway <[email protected]> wrote:
> On Tue, 2015-02-24 at 07:59 -0500, Rafael Schloming wrote:
> > This commit doesn't make sense to me for a couple of reasons. First, the
> > way applications deal with the effects of events *is* by handling them.
> > Secondly, if there is a reason to exit the event processing loop prior to
> > processing all outstanding events, there is an API for doing that, you
> can
> > call pn_reactor_yield(...) in one of your event handlers. I'm not sure I
> > quite understand the reason for this commit, but I suspect there is a
> less
> > brittle fix. Can you explain what is going on here? As is, I believe this
> > change may introduce a potential stall.
>
> Gotcha, will revert and replace with a yield as you suggest.
>
> The scenario is blocking use of proton, which is why it doesn't show up
> in most of the existing examples. The SyncRequestResponse utility uses a
> BlockingConnection to send a request, and has an on_message that records
> the response. It uses BlockingConnection.wait() to wait for the
> response, which calls reactor.process() in a loop until the response has
> appeared. The problem was that although on_message was called correctly
> by process, instead of returning control it carries on to process a
> quiesce event and blocks up to the reactor timeout before returning.
>
> Putting a reactor.yield in on_message will solve my problem just as well
> and with less clutter in the reactors process loop. We should document
> the need for it somewhere where we discuss blocking use of proton...
>
It's an interesting case. Now that you describe it there are some other
ways to deal with it also. You can take advantage of two nice properties of
how the reactor and event dispatch works:
1) It is really easy to create an event handler that intercepts just one or
a few events from another handler. This pattern is very similar to
inheriting/overriding, except more flexible and it will allow you to
"extend" handlers defined in C:
class MyInterceptor:
def __init__(self, base):
self.base = base
def on_foo(self, event):
# do foo logic
if iWant():
self.base.do_foo()
# do more foo logc
def on_unhandled(self, name, event):
# delegate all unhandled events to base
event.dispatch(self.base)
2) The I/O for the reactor is provided by just an ordinary handler (it's in
the handlers package, and it's called IOHandler), so you can do something
like this:
reactor.global_handler = BlockingInterceptor(IOHandler())
Your BlockingInterceptor can then override on_reactor_quiesced and choose
based on whatever logic it wants to whether or not to handle the quiesced
event by delegating to the default IOHandler (which will block if there is
nothing to do) or whether it should yield. This will basically allow you do
customize the reactor's processing loop exactly like you did in your
commit, except just for your particular configuration.
--Rafael