Recursion: See recursion

The comment stripper FSM works OK, but it has a dangerous flaw. It’s a flaw that is inherent in event-driven systems, and one that also crops up in regular Visual Basic programs. The problem is reentrant code, and you might have come across it when working with data controls, Form_Resize events, or code that uses DoEvents.

Let’s have a look at a simple example of reentrancy using a data control. The program shown in Figure 14-10 on the following page (which is in CHAP14\recurse\broken\rcb.vbp) is about as simple as it gets, with a single data-bound list wired up through a data control to the Visual Basic sample database BIBLIO.MDB. Assume that the list contains a set of records you need

Figure 14-10 Recursion in the data control’s Reposition event

Clicking No when the message box pops up simply continues, and this is where you’d process the new record. Clicking Yes simulates a locking error and simply skips to the next record by calling the MoveNext method of the data control’s recordset. The idea is that you’ll reach the end of the locked page after skipping a few records and so find a record you can process. The problem here is that you’re calling MoveNext from within the Reposition event handler, which causes another reposition event before the first one has finished—this is recursion. The example program maintains a static variable to count the number of recursions; the count is displayed on the message box, and the program also prints the entry and exit traces for the reposition event to the Immediate window when you run the program in the IDE. You can also see the effects of the recursion by pressing Ctrl+Break and selecting Call Stack from the View menu.

This example, which comes from a real program, might not have particularly serious consequences because it’s a pure recursion that doesn’t nest too deeply, and it involves no static data (except for the counter, of course). Generally, however, and particularly when you’re devising code such as FSMs to control the loading and unloading of forms, the code will break as soon as you try to invoke it recursively. You might, for example, end up in a situation in which you’re trying to load a form from its own Form_Load event.

Coming back to the recursive Visual Basic program, it’s not immediately obvious how to fix the problem. It turns out that this is quite a common class of problem, and one that conveys the true flavor of event-driven code. What you want to do when you find a lock is to exit the event handler and then immediately issue a MoveNext on the recordset. Unfortunately, Visual Basic can’t do this because as soon as you exit the event handler, control passes back to the run-time system (the <Non-Basic Code> you see when you select View/Call Stack in break mode). What you need to be able to do is to post some kind of request for a MoveNext and have it execute after you’ve left the Reposition event handler.

Just because Visual Basic won’t do this kind of thing for you doesn’t mean that you can’t implement it yourself. CHAP14\recurse\fixed\rcf.vbp is a modified version of the pathological data control program that uses a simple event queue to achieve what you need. You use an unsorted list box as a convenient event queue and a timer control that continually polls the queue looking for events. There’s only one kind of event in the program, so you don’t even need to look at its value when you find it on the queue—always consider it a request for a MoveNext.

The program works like this: inside the Reposition event, instead of directly calling MoveNext when a locked record is encountered, we post an event onto the queue and then exit the event handler. The queue manager (the timer control) then comes along and, finding an event on the queue, kindly calls MoveNext for us. Now, however, the MoveNext is called from the timer’s event handler, and there’s no recursion. Notice that it doesn’t matter how fast you push event requests into the queue; you never get recursion because the events are processed one by one in sequence.