idle timing and events: avoid inf loops?

I’m the developer of Cast Away, a podcast removal and auto-player for iTunes. Although it is an AppleScript studio app, I imagine this question applies to any stay open AS app, so I am posting it in the general thread.

One of the things I’ve never been satisfied with is the way it handles podcast auto-playback, and I am continually looking for ways to make it better. The trick is I don’t want CA to hog resources with a quick idle time return, but I also want it to be responsive to changes in iTunes. With the kind help of jobu here in these forums, I implemented an iTunes events notifier attached to a button.

Anyway, the purpose of this thread is to get some feedback and expert info from you all about how on idle handlers work. Specifically, if a long idle handler is being executed and an event occurs, then what happens to the progress of that idle? At what point does it return to the middle of the idle, where it was? Or does it? Similarly, if there is another handler being executed in a delay loop, would on idle ever get triggered? Would another user-generated event interrupt an idle or “wake up” a seemingly dead idle?

The problem is the whole app often gets stuck in some kind of loop where it doesn’t do anything. My assumptions are flags that are set to true and meant to be set back to false never get set back to false because the handlers in question never finish. The odd thing is usually that if I click on a window (not even a connected button) it wakes up the whole thing – brings the idle back to life at least.

Any ideas welcome!!

The schematic being used now is roughly as follows:


on idle
 my idlenow()
 return idlesecs
end idle

on idlenow()
if not isidling then
  set isidling to true
-- do something very long, adjusting the idlesecs value according to to need, sometimes as low as 5, sometimes as high as 60
-- possibly call on fadeup and similar handlers
end if
set isidling to false
end

on clicked
 if name of the Object is ... (etc) [the invisible notification button] then
    my checkitunes()
 end if
end clicked

on checkitunes()
    -this would happen when an iTunes event happens
if isidling and not fadeupflag then
 repeat
     -- wait for about 15 seconds checking isidling value every 5 secs and then give up if still true, setting idlecs to 15
  end repeat
end if
if not isidling
 my idlenow()
end if
end

on fadeup()
 -- would be called from "idlenow", but would trigger an iTunes event that would call checknow
 set fadeupflag to true
 -- play track and fade up itunes volume in a loop
 set fadeupflag to false
end fadeup

Just a quick response to my own thread:

I have determined with some logging that in fact idle handlers CAN get interrupted by events — never to return to where they were. In my case this iTunes event button would interrupt an idle; however menu commands would also. I managed to work around the button event by turning off notifications at the top of “on idlenow()” and turning them back on at the end of “on idlenow()”

Worse though, is that my efforts to “force” a return to “on idlenow()” after “isidling” had been set to true but not set back to false, had some very weird effects. I can’t be sure of this, but circumstantial evidence suggests that doing this (basically forcing to return to idle code that wasn’t finished) creates some strange problems, maybe memory leaks or stack errors. The noticable outcome of this for me included: hung system events and iTunes knocking off firewire volumes during an iPod sync (literally forcing volumes to come unmounted). I can’t be 100% sure of the connection, but suffice it to say this happened on two separate occasions on different days after I had introduced some code to force this return to idle after waiting for “isidling” to get set back to false for a certain amount of time.

Hi WoodenBrain,

Are you sure the idle handler is stopping and not that there is a long idle delay? Note that if an untrapped error occurs in the idle handler, the return time will be the default value of 30 seconds.

gl,

Yep, I’m 99% sure the handler is stopping. At the end of the idlenow() handler, isidling is set to false. But if an event occurs before it gets to that point, it is not set to false and the next idle will not execute, becuse of the condition that isidling is false. instead i get log entries which show that isidling is true each time the actual idle comes around.

Hi WoodBrain,

I’m still not completly sure about what you’re doing with the isidling part, but here’s an example of something like what you describe.


on idle
	beep
	return 2
end idle

on quit
	try
		display dialog "Really quit?"
		continue quit
	end try
end quit

When you run this app, quit it, and Cancel the dialog, the app just hangs until try to rerun it. I haven’t found a way to restart the idle by script. If you could control the ui as you can in AS Studio, you can turn of all ui elements until the idle occurs within the idle handler loop. It seems that the app hangs when you interrupt it when it’s out of an idle loop. This app doesn’t hang when you try quitting in the repeat loop and press Cancel in the dialog:


on idle
	repeat with i from 1 to 20
		say (i as string)
	end repeat
	return 2
end idle
--
on quit
	try
		display dialog "Really quit?"
		continue quit
	end try
end quit

I think that’s why your app is intermittent. When the delay is long, chances are you will interrupt the idling when it’s giving up the cpu time.

gl,

I’m a bit confused by this – my issue is the opposite ; my app gets stuck (never returns to idle) when it is interrupted by an event while INISDE the idle loop. This is also because of the “isidling” flag I’m using – it depends on that flag being reset at the end of the idle handler. I’m not sure but I think part of the difference might have to do with the way you’re trying to test this with Cancel within the on quit handler. Cancel in and of itself I’ve always found has some widely varying results in terms of what code continues to get executed unless trapped somehow. I’m also not sure of what the key difference you’re indicating with the two scripts actually is. Both have “return 2” as the idle value?

Furthermore, I did try your scripts and wrote a similar one of my own, only to determine that it seems a stay-open application CANNOT be interrupted during the idle. If you try to quit (from the dock) during the counter sequence, it waits until the end of it to respond. Is this what you’re seeing?

http://www.woodenbrain.com/sw/Misc/isidlingtest.app.sit

In my applescript studio application (Cast Away), this does not seem to be the case. So I tried making a simple AS Studio app to test this:

www.woodenbrain.com/sw/Misc/idle_interrupt.dmg

However, here I ALSO found that the idle could NOT be interrupted.

So, I’m baffled. The fact is that with Cast Away, there are occasions that the idle CAN be interrupted, because the isidling flag is never set to false at the end of the handler. So I’m not sure what could cause that. With the test project above, neither a button nor a menu command can interrupt it. With Cast Away, I had suspected that some external C code that responds to iTunes notifications and clicks an invisible button (courtesy of jobu) was the culprit. But even when I disable that code at the beginning of an idle and re-enable it at the end of one, from time to time (though now much more rarely) I STILL see the idle interrupted.

Anyway, I will continue to investigate. Thanks.