Lock screen and animated gif

Mark Waddingham mark at livecode.com
Tue Dec 20 13:10:51 EST 2022


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 ~ mark at livecode.com ~ http://www.livecode.com/
LiveCode: Everyone can create apps



More information about the use-livecode mailing list