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

Reply via email to