Doevents

Top  Previous  Next

Although under Tibbo Basic, event-driven programming is the norm, there may be special cases in which you just have to linger an overly long time in one event handler. This will block execution of other events. They will just keep accumulating in the queue (see System Components).

To resolve this, a doevents statement has been provided. When this statement is invoked within a procedure, the execution of this procedure is interrupted. The VM then handles events which were present in the queue as of the moment of doevents invocation. Once these events are handled, control is returned to the procedure which invoked doevents.

If new events are added to the queue while doevents is executing, they will not be processed on the same doevents 'round'. Doevents only processes those events present in the queue at the moment it was invoked.

To summarize, doevents provides a way for a procedure to let other events execute while the procedure is doing something lengthy.

 

 

'calculate sum of all array elements -- this will surely take time!

dim sum, f as word

sum = 0

for f = 0 to 49999

       sum = sum + arr(f)

       doevents 'don't want to stall other events so allow their execution while we are crunching numbers

next f

 

Multiple Doevents Calls

 

Let us say we are in event handler on_event_1. This event handler executes a doevents call. The VM begins processing other events in the queue, and starts executing an event handler on_event_3, which also contains a doevents statement.

In this case, a new doevents round will begin. Only when it completes, control will be returned to event handler on_event_3, which will complete, and then return control to the previous doevents (the one from event handler on_event_1).

 

 

tide_doevents_3

 

The point here is that control will not be returned to on_event_1 until on_event_3 fully completes execution, since on_event_3 contains a doevents statement in itself.

Memory Allocation When Using Doevents

When a procedure utilizes doevents, it means its execution gets interrupted, while other procedures get control. We have no way to know which other procedures will get control, as this depends on the events which will wait in the queue.

As a result, a procedure which utilizes doevents, and any procedures calling it (directly or through other procedures) cannot share any memory space with any other procedure in the project. This would lead to variable corruption -- procedures newly executed will use the shared memory and corrupt variables which are still needed for the procedures previously interrupted by doevents.

tide_doevents_1

Above we have the same two chains of procedures which appear under Memory Allocation for Procedures, with one noticeable difference: Procedure C includes a doevents statement. The on_event_1 chain takes up 11 bytes. But on_event_2 and procedure C (which together take up 5 bytes) cannot share the same space with the on_event_1 chain, because when the doevents statement is invoked, the state of variables for on_event_2 and procedure C must be preserved. So these two get their own exclusive memory space.

Procedure D, which is also a part of the on_event_2 chain, does not get its own exclusive memory space. This is because it comes later on the chain than the procedure which contains the doevents statement. There will never be a situation where the variables of procedure D must be preserved while other chains are executing.

Thus, the total memory requirements of the project depicted above would be 16 bytes -- 11 shared bytes plus 5 exclusive bytes. This is more than would have been required had we not used doevents.

Sharing Procedures Which Utilize Doevents

Procedures which contain doevents statements, as well as all procedures which call them (directly or through other procedures) cannot be shared between chains.

Let us say event handler on_event_1 calls procedure A. Procedure A calls procedure B. Procedure B contains a doevents statement, and also calls procedure C.

Next, we have event handler on_event_2. It cannot call procedure A or B, because procedure B contains a doevents statement (and A calls B). If it could call procedure A, we might get a situation whereby event handler on_event_1 fires, calls procedure A. In it, procedure B is called, and doevents is executed. During doevents, an event handler on_event_2 fires, and calls procedure A again -- while the previous instance of A still holds variables in memory, waiting for control to return to it. This would corrupt the values of variables used by the first A (if you try to do something like this, the compiler will raise an error).

tide_doevents_2

However, note that in the example above we also have procedure C (which is called by procedure B). This procedure can be shared by everyone -- because it is later on the chain than the procedure which contains the doevents statement.

Doevents for Events of The Same Type

For some events, only one instance of the event may be present in the queue at any given time. The next event of the same kind may only be generated after the current one has completed processing. For other events, multiple instances in the queue are allowed.

Let us say that for event_1, multiple instances are allowed, and that this event's handler contains a doevents statement. When this statement executes, it may happen that another instance of event_1 will be found on the queue, waiting to be processed. If this happens, this new instance will just be skipped -- execution will move on to the next event on the queue. Otherwise, we would once again get recursion (execution of an event handler while a previous instance of this event handler is already executing), which is not allowed under Tibbo Basic.