[PEAK] Re: Infinite loop in events.subscribe()
Phillip J. Eby
pje at telecommunity.com
Mon Feb 2 21:33:28 EST 2004
At 05:42 PM 2/2/04 -0800, John Landahl wrote:
>[this didn't make it to the list -- it seems the attachment is too large]
>
>Just tried events.subscribe() for the first time today, and it seems to
>generate an infinite loop when the event source fires. Attached is the
>traceback from something like:
>
> condition.set(True)
>
>where 'condition' is an instance of events.Condition to which some other
>component has subscribed via events.subscribe().
subscribe() is actually obeying its contract here, given the behavior of
Condition. IConditionals fire whenever they are true, which means that
callbacks added to a True condition are fired immediately, in much the same
way as a Deferred in Twisted calls any new callbacks immediately if the
Deferred has fired.
So what do you want instead? Probably you want to subscribe to the
condition's 'value' attribute instead, which fires only when the value
*changes*, not whenever the value is true.
In general, 'events.subscribe()' isn't all that worthwhile, used by
itself. It's mainly a crutch to ease the implementation of DerivedValue
and DerivedCondition, and I'd recommend avoiding its use until you're sure
that there's no other way to accomplish what you want. In most cases, I've
found that things I previously implemented using the equivalent of
subscribed callbacks worked better when rewritten as 'events.Thread's. In
particular, it made it easier to control behavior when there were other
events involved.
For example, if you look at the stuff I did recently on the supervisor
tool, I changed lots of callbacks that were "subscribed" (not using
events.subscribe(), but the logical equivalent in pre-'events' terms) into
threads that wait on a variety of events, not just the one event they
previously "subscribed" to. As a result, the code is not only more
readable, but the functionality was enhanced as well.
So, I guess to make a long story short, if you think you need to
'subscribe()', double check to make sure that a Thread isn't the
appropriate solution. 'subscribe()' has comparable overhead, but doesn't
have comparable features.
Actually, in general, when in doubt, use more 'events.Thread'
objects. They don't use *any* resources while they're waiting, except
memory for the actual thread object: about 52 bytes + ~360 per active
generator. Often it's hard to shake the mental model that a thread is
something very very expensive and complicated and not to be used lightly,
but incredibly light is exactly what 'events.Thread' objects are. Their
task switch overhead is only slightly more than the cost of issuing the
same callback that you'd do in a callback-based program.
So splurge on threads, especially if they make it easier for you to tell
what's going on. There are probably some threads in the supervisor tool
that could be combined into single threads, but I've intentionally kept
them separate since each then has its own well-defined purpose and is easy
to read. Sure, several of them probably could've been done with callbacks
and 'events.subscribe()', but gosh they'd be harder to follow.
I suppose maybe I should update the 'events.subscribe()' doc to mention all
this... I tend to assume when I write reference docs that *of course*
people know when it's appropriate to use. :)
More information about the PEAK
mailing list