1610 Corel WebServer: Using sleep() in code
VERSIONS: Paradox 8 and later

SUBJECT: Corel WebServer - using sleep()

BY: VLADIMIR MENKIN, and DMITRY VULIS
Date: 28 January 2002

Today I got a very strange error in one of my Paradox-WEB applications. After digging the code I've concluded that there is (very strange) an invalid code execution sequence. Digging further gave me understanding how CWS works in some particular cases and understanding, what the purple light means in the CWS. Consider the following situation:

CWS obtains the first query. Event handler (custom method, usually in a library) starts its execution. CWS shows red light for this query. If during the execution of this code CWS gets the second query, this query will be placed in the queue and its handling will be delayed until the first handler will end its processing. But if the second query stays in the queue when the first handler is executing one of the following methods (may be it isn't the full list):

sleep
formvar.open
reportvar.open
msgstop, msginfo, msgquestion
view

the code execution of the first handler temporary stops and the handler of the second query (which was in the queue) starts its execution. CWS now shows *purple* light for the first query. From this point the first query handler will not resume its execution until the second query handler finishes (doesn't matter if the second handler has the methods mentioned above or not).

So, the purple light in the CWS indicates that the corresponding query handling is delayed until the next query (queries) will be processed.

I believe that this information is important for all who develops complex Paradox-WEB applications.


Further to what Vladimir discovered, it becomes absolutely evident now, _why_ it behaves this way, and not another. Indeed, what happens, in particular, if you call, e.g., a sleep(). The documentation says, that"When sleep is called with no argument, it... causes the current method to yield to Windows to let a single pending message be processed".

This is the key to understanding, what's going on. Every time CWS receives a request, the request generates such Windows message, which is getting handled immediately (if Paradox is free) or is queued by the OS until Paradox becomes free.

Now, imagine that Paradox was busy, and the message generated by the client's request was patiently standing in the queue, but then a sleep() command was issued in Paradox. Next message from the queue is passed to the addressee (Paradox) by the OS. Paradox has no other option, but start handling the new incoming message, it does not have any means to "set is aside for a while". Instead, Paradox has to push the active task into the stack and start handling the new message, which obviously generates new process.

From this point the stack is handled in the standard LIFO (Last In First Out) manner. If the new process, which has got the control also issues a sleep() and "yields to Windows to let a single pending message be processed", it gives another potential request the chance to push itself (the active process) into the stack, and those processes already in the stack - to push even deeper, but, of course, under no conditions, the order of the stack can be by passed.

Paradox with CWS actually works like a convertor of a queue (FIFO) into stack (LIFO). A request is passed from the queue to the stack under very specific conditions (single pending Windows message is processed). Obviously, all operators from Vladimir's list cause this; obviously, same effect would be caused by a suspension of Paradox by a breakpoint in the code or by an error message (if the error is not crashing everything completely).

PS. Looks, like the icon in the CWS monitor becomes green, if Paradox has completed handling the request and the data is being transmitted back to the client. Once this is completed, the icon turns gray.


Should Sleep() not be considered at all in code that will run while the OCX is active?
Generally, not recommendations, but precautions when you use sleep() or open other forms from the CWS query handler. Sometimes it's hard or impossible to avoid using these operators. Moreover, there may be situations, when sleep() is useful. For example, you have queries from the CWS (note, I mean not the QBE or SQL queries) which are executed rare enough, but each take a long time. All this time other requests from the CWS will be queued. But you can place (with precautions) sleep() in several places inside the long query handler to give the CWS a chance to reply to other short requests.

To index