[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