On 2022-12-20 15:48, Paul Dupuis via use-livecode wrote:
I'm just using this post to raise awareness on the list (if anyone
reading this doesn't know this already)
When using 'wait 0 with messages' any messages in the pendingMessages
(who are not delayed in time) get processed. This includes
(potentially) user clicks (mouseDown and mosueUp events). In a
multi-window application, this can be a click on another window,
changing the defaultSTack. If the original script with the 'wait 0
with messages' does not use fully qualified object references, script
execution errors can result. Example:
My (admittedly quick!) tests indicate that this isn't entirely
accurate...
Engine generated messages (e.g. mouseUp) behave differently in this
regard to pending messages - and even in that case it requires specific
action in script for it to occur.
So imagine you have a button on stack "S1" which has a single field on
it. The button has script:
on mouseUp
repeat forever
local tStackIds
put the defaultStack into tStackIds
wait for 10 milliseconds with messages -- the time doesn't
matter here
put space & the defaultStack after tStackIds
put tId into field 1
end repeat
end mouseUp
Along with this, there is a button with mouseUp script 'go stack "S2"'
Then stack S2 has another button B1 which does:
on mouseUp
set the defaultStack to "S3"
end mouseUp
And then another button B2 which does:
on mouseUp
send "_mouseUP" to me in 0 seconds
end mouseUp
on _mouseUp
local tOldDefaultStack
put the defaultStack into tOldDefaultStack
set the defaultStack to "S3"
--set the defaultStack to tOldDefaultStack
end _mouseUp
Then, finally, there is a third stack S3.
Start the wait loop by clicking on the button in stack S1 - observe that
the field contains S1 and S1 - i.e. the target of the original mouseUp
which started the wait loop.
Now click the button on stack S1 (the one which changes focus to stack
S2) - notice that the field in stack S1 does not change - i.e. focus is
not preserved. This shows that engine generated events do not (in this
case at least) change the defaultStack.
Then click button B1 on stack S2 - the one which explicitly changes the
defaultStack as a result of a mouseUp message - again observe that the
ids in the field on stack S1 do not change. This shows that engine
generated events always preserve the defaultStack - even if it is
explicitly changed while running the handler the engine event causes to
run.
Finally click button B2 on stack S2 - the one which changes the
defaultStack in a pending message... Then observe an error - this is the
case Paul describes.
Now comment out the restorative 'set' line in the pending message
handler and try the process again - notice that, this time - in the
final case, the defaultStack in the root wait mouseUP handler is
preserved.
TL;DR version: If the defaultStack is *explicitly* changed and then not
restored before exit in a pending message handler then the defaultStack
will remain as the value it was changed to even after the return from
the `wait` which caused it to run.
I haven't had any time to dig into the engine code that deeply as yet to
see why this is the case - although I'm pretty sure it has been this way
forever.
Interestingly though - this is exactly the behavior of normal send...
Contrast:
on mouseUp
send "changeDefault" to me
answer the defaultStack
end mouseUp
on changeDefault
set the defaultStack to "SomeOtherStack"
end changeDefault
Here - the dialog will show "SomeOtherStack" - whereas if you change
send to call, it will show whatever the stack is which has the button
which is handling the mouseUp.
Upshot: pendingMessages are truly deferred sends - they are identical to
send, except that the send occurs at the next wait point and not at the
point the send is invoked. Specifically, `send "foo" to tTarget in 0` is
'as if' `send "foo" to tTarget` has been executed in the handler calling
the 'wait with messages'.
[ As an aside - the trick with changing send to call when you have an in
time clause *does not* change this behavior - `call "foo" to me in 0`
*does* parse, but it identical to 'send in time' ]
Outcome: So assuming that my brief reading of the engine code is correct
(and, indeed, my not complicated but somewhat convoluted example is also
correct) then the question is whether the 'sendiness' of pending
messages should be changed to 'calliness' - i.e. whether pending
messages should always preserve the defaultStack of the calling context.
I'm inclined to say probably it should - however I'm always aware that
changing (even rather esoteric?) semantics of things which have been
that way for as long as time has a huge risk of breaking existing
code...
Advice: So, anyway, regardless of whether the current behavior is
correct/incorrect/desirable/undesirable or whatever, it is what it is
and has been that way (I think at least!) forever - the problem that
this lengthy post describes can be completely mitigated by ensuring you
always reset the defaultStack on exit of handlers which are called in
time - i.e. like the _mouseUp handler above - with the restoration of
the defaultStack at the end.
Warmest Regards,
Mark.
P.S.
Sometimes evaluating whether long standing behavior should be changed is
really hard and is always a balance between whether it is at all likely
to break *any* existing code (and if so, how much), and the benefit of
the change will bring. However, in this case, I *think* (but do need to
consider more) that the chance of it breaking existing code is virtually
zero, and the chance of it fixing really hard to track down 'seemingly
non-deterministic' bugs in existing code is quite high.
Why? In order for existing code to *rely* on the current behavior would
require these things to be true:
1) the app in question would need to use nested, dispatching wait
loops
2) the app in question would need to use pending messages which
explicitly change the defaultStack and not change it back
3) the app would have to have been written such that there were
instances of wait with messages, with code following them which
explicitly relied on a pending message which changed the defaultStack
explicitly to have been run
So the number of app using (1) is probably actually quite high - some
engine syntax uses nested dispatching waits, and its common to do that
in any script which is long running and wants to provide progress
updates without the displaying the progress updates taking far longer
than the process it is showing progress for.
The number of apps doing (2) is hard to assess - having to change the
defaultStack explicitly is needed, but how often is another question.
However, I'd probably guess that most handlers which do set it
explicitly and called as pending message do not - as its not obvious
that you need to (due to the not ideal current behavior of pending
messags) - so this case is probably reasonable common too.
So that leaves (3) - which I struggle to even begin to imagine ever
existing! After all the only guarantee with pending messages is that
they will be sent as close to the requested time as possible - so for
code to explicitly rely on a pending message being dispatched at a
specific 'wait for messages' would be so tied to timings of things as to
be so fragile that I can't see such code surviving as a 'good idea' for
long in the course of an app being built.
Of course, there's always the mantra of 'famous last words'...
--
Mark Waddingham ~ m...@livecode.com ~ http://www.livecode.com/
LiveCode: Everyone can create apps
_______________________________________________
use-livecode mailing list
use-livecode@lists.runrev.com
Please visit this url to subscribe, unsubscribe and manage your subscription
preferences:
http://lists.runrev.com/mailman/listinfo/use-livecode