@Mikey: Heh - yes. Armed with the knowledge I extracted from the 4D docs yesterday, and with your very eloquent description of your system, I have a much greater understanding of both. Indeed, I *think* it has helped me finally solve a problem (or at least put me on a path to solve a problem) which has been causing me irritation for years (to do with event handling but that's definitely for another day!).
Now I notice you did delete some stuff from your original post (if you hadn't had said that you had I probably wouldn't remembered!) - that actually gave me cause to check the version of the page I had on my phone which was the original. I kinda get why you deleted it - but failed experiments (or thoughts) still give useful information (in this case, potentially use-cases which could cause a potential solution to just be plain wrong). The other reason I dug it up was your comment about 'waitDepth' which deserves a significant enough digression as to require me split my response into at least 2 parts...
Part (1) - on waitDepth issues:
The issue you see with waitDepth is down to what I keep referring to as 'the recursive wait problem'. Actually I should be more accurate here - the recursive wait problemS
- there are actually two. Although perhaps I should just start referring to them as 'the wait problemS
' as only one is only really due to recursion, the other isn't really to do with recursion, but can't be solved because of something which is recursive which ideally would not be. Anyway, that is largely not entirely relevant to any description of either...
The first of these problems can perhaps simply be expressed as 'in an expected strictly ordered pair of actions 1 and 2, action 2 fires when action 1 has not finished':
Code: Select all
... process stuff ...
get url <some very large file> <-- this does an implicit wait with messages
... process result ...
... do stuff which really shouldn't happen until after mouseDown has finished ...
... oh dear, typically it will be called before mouseDown has finished ...
I use mouseUp/mouseDown here because it is easy to visualize, but it basically occurs when you have two actions which are fired as a result of an event, the second is expected to occur strictly after the first. The problem occurs when the first action calls something which uses 'wait with messages' either implicitly or explicitly.
The problem this causes is that the second action can (and will!) fire before the state it expects on is actually correct - i.e. because the first action hasn't actually completed and thus set the state to the expected values.
Why is this related to waitDepth? Because if in the second action you also call something which either implicitly or explicit calls 'wait with messages', you end up with waitDepth > 1 - which you would not (necessarily by looking at the code) expect it to be.
The second problem is probably actually the main cause of waitDepth issues - but is usually made worse / triggered by some form of the first problem. (In actually fact these two cases can get so intertwined, its primarily why I label this as 'the recursive wait problem' even though that's not strictly accurate). Essentially as soon as an action causes a wait with messages, any action which is a result of an event which occurs during that wait with messages must resolve *before* the action which caused the wait with messages in the first place can.
Whilst this might seem like an 'oh well that's obvious' kind of thing - it arises in unexpected scenarios, mainly because of implicit wait with messages; or because codepaths can get pretty deep and it can easily occur without realizing as code evolves / gets refactored and components get improved/rewritten/made more powerful (although 'get url' is usually the main culprit in more simple projects!).
By far the best visualizable example of where this form of the problem occurs is:
Code: Select all
-- In button of one stack
answer "Hello World!" as sheet
-- In button of another stack
answer "Goodbye World!" as sheet
Due to the recursive (C-stack wise) nature of the engine's interpreter, and the fact that events are dispatched *at any wait with message point* if you click on the button in the first stack - you'll get a dialog. You will then be able to click in the button of the second stack and also get a dialog. Now, here, you must close the *second* dialog before you can close the first dialog - even though the *expectation* is that they should be logically independent!
Note - that *exactly* the same effect can occur with complex sequences of pure code actions which do non-simple things with wait with messages - it is essentially a deadlock of sorts (well only of sorts in the dialog version, since you can click to remove the dialogs - but it can be true deadlock in code if there is no way to explicitly break it).
Why is this related to waitDepth? Well if you have opened both dialogs - the waitDepth will be at least 3 - until the second dialog is dismissed, at which point it will go down to 2, then returning to 1 again (which is the minimum it can be when running script as all scripts run as a result of an event) when the first dialog is dismissed.
So anyway, in more complicated projects which push LC's model to the limit you can essentially end up with a recursive stack of handlers which is the equivalent of nested spaghetti (uncooked tagliatelle perhaps?) internally - not actually knotted (because such pasta isn't ever knotted because of how it is made, it just *looks* that way) and usually results in the IDE going exceptionally wonky too. In serious case of this, if you do manage to coax the message box into appearing and functioning and finally manage to get it to do 'put the waitDepth', you find waitDepth > 1 and probably make lots of sounds which would not be family friendly. In this case I tend to find rapid repeated depressing of Cmd+. (on Mac / Ctrl on Win) can often unwind the mess - although not in all cases, and that's when 'Force Quit' can be the only option.
So before you ask 'why if you know about this have you not fixed it?!?!'. There are three reasons for this.
The first is that this kind of thing only really arises when you start to push the bounds of what LiveCode's version of the xTalk event handling / script mode to beyond what it was originally really designed for - in most developers use of it, when these things arise it is because of small coding errors you did not intend and when you fix those, it goes away (this is usually the case when waitDepth appears wonky at very sparse random points). Indeed, most people tend to find 'something wonky happened when I did that' so they don't do it again.
The second is that if you are a 'power-user' that wants to push the envelope, then you should be able to follow appropriate sanitised coding techniques to be able to do stuff like this without causing the problem to occur. Of course, in order to do this, you either have been told what these techniques are; or have realized there is a limitation in the wait model of LC and figured out yourself how to write code which does not encounter it.
The third is that fixing it correctly/completely/properly is a very very hard thing to do! All solutions require quite time consuming, tedious and extensive transformations of the engine's C++ code, or introduction of quite a large amount of code 'machinery' in lots of places in the code which would only serve to make the code of the engine appear to be more convoluted. Essentially all come down to either eliminating the recursive event handling nature of the engine; or working around it by clever use of real threads (but ones constrained to be co-operative, and not pre-emptive)... And even then it only partially solves the problem! (Oh and some solutions have quite large implications for externals / extension writers who need to use native code!).
Anyway, this has already got too long - so I shall actually leave it here for now and come back to the *actual* point of this thread in a subsequence post (which will likely be tomorrow - I have 9hrs to kill at JFK airport tomorrow afternoon!).