On Jun 3, 2008, at 3:14 PM, Joshua Whalen wrote:
This routine controls a fade-out timer for itunes. It works nicely most of the time, but on slower machines, accumulated latency will cause it to run a second or two too long, with catastrophic results: The new playlist starts, sets the volume to 100, and the fader never reaches zero, thus causing the playlist to fadeout on launch. Not good.
To compensate for this, I added a variable and a check for the variable's status. The playlist sets NewShow to true on launch, and the fader sets it to false, and then checks on each loop to see if it's true or false. If it's true, it should quit the loop. There's just one problem: It doesn't.
I have tried setting it as a property that defaults to false, I have tried setting it as a property that defaults to true. I have tried putting the check in different parts of the loop, I have tried putting multiple checks all through the loop, I have tried all kinds of variations, it never gets the variable status.
The basic problem is that the second script invocation fires while the first one is still running, and they start stomping on each other's changes. The usual solution is to create some sort of "semaphore" that allows the two invocations to communicate state and keep out of each other's hair. That much you already figured out.
The trick is, script properties make terrible cross-process semaphores. You're assuming that the property value is shared between different simultaneous executions of the script, and that isn't the case. Properties (and, by what I believe to be an accident, global variables) are "persistent", that is, their values are written back to the script file, but not until the script finishes running, and the whole point here is that the first script *hasn't* finished running yet.
If you're going to use a semaphore, it needs to be in some location that's accessible to both processes, and, more to the point, if one process changes it, the other process needs to be able to see the change. The old-school way to do this is to write a temporary file somewhere and then delete it when you're done -- the existence of the file itself can be the semaphore. Another possibility would be to use a user preference (a la "defaults write" or some such).
Alternatively, it might also be possible to write your script to be less vulnerable to interference. For instance, your fader could check if the volume is still the last thing it set it to. If it's not, then obviously someone else is changing the volume, and it should quit trying to fade. Or you could write an actual application to do the scheduling instead of making cron(8) do it, and then you aren't obliged to run each script as a separate process.
--Chris Nebel
AppleScript Engineering