![]() |
![]() |
![]() |
![]() |
11 The Coroutines Module (coroutines.hhf)
HLA provides a powerful coroutines class that lets you easily use coroutines in your programs. The coroutine class provides three procedures and methods you can use to initialize a coroutine, transfer control between coroutines, and free up the storage associated with a coroutine when it completes execution. The coroutine class also has several data fields, but you should treat these as private fields and never disturb their values.
In addition to these class procedures and methods, the coroutine package provides a coret procedure that is useful for returning from a coroutine to whomever "cocalled" the coroutine. This makes it very easy to implement Generators using coroutines.
Finally, the coroutine module provides a special coroutine variable, mainPgm, that you can use to cocall the "coroutine" corresponding to the main HLA program.
procedure coroutine.create( size:uns32; theProc:procedure );The coroutine.create procedure is the typical HLA class constructor for the coroutine class. Since this is a class procedure, you can call create one of two different ways:
(1) You can call it via the statement "coroutine.create( size, proc);" This form assumes that you wish to create a dynamic coroutine object on the heap. When called this way, the coroutine.create procedure allocates storage for a coroutine object on the heap and returns a pointer to this new coroutine object in the ESI register. Otherwise it behaves identically to the second form of the coroutine.create procedure.
(2) You can call coroutine.create using an invocation of the form "objectName.create( size,proc);" where "objectName" is the name of a coroutine variable or a pointer to a coroutine object (that, presumably, has been initialized with a valid pointer to a coroutine object).
Either form of the call to create will initialize the coroutine object, allowing subsequent cocalls to the coroutine object.
Coroutines execute using their own stack (independent of other coroutine stacks and independent of the stack the main program uses). The size parameter specifies the number of bytes of stack space to reserve for the coroutine. A good minimum value for a coroutine stack is between 256 and 1,024 bytes. If the coroutine allocates lots of local/automatic variables, or calls other procedures that allocate lots of local/automatic storage, you will need to allocate a larger stack as appropriate. Likewise, if your coroutine calls procedures that are recursive, additional stack space may be necessary.
The theProc parameter is a pointer to a procedure. This procedure is the code that will execute when you cocall this coroutine. The only thing special about the procedure is that it should never be possible to return to the procedure's caller by executing a RET instruction. You exit coroutine using the coroutine.cocall procedure or the coroutine.coret procedure. If your code accidentally "falls off the end of the procedure" or otherwise attempts to return to the caller via a RET instruction, the coroutine will go into a special state in which any attempt to cocall it forces an immediate return by the coroutine to the cocaller.
procedure coroutine.cocall;This is the mechanism you use to invoke a coroutine. Note that this is a procedure for performance reasons. You should never invoke the static procedure coroutine.cocall as this will raise a run-time exception. Instead, you should always invoke this procedure using an object invocation of the form "objectName.cocall();" This will switch the thread of execution from the current coroutine (or the main program) to the coroutine code associated with "objectName". Note that coroutines rarely begin execution at the first statement of the procedure associated with the coroutine (in fact, this happens exactly once, when you invoke the coroutine for the very first time).
The cocall mechanism provides the standard way of leaving a coroutine. Cocalling some other coroutine switches the execution context from the current coroutine to that other coroutine. The next time some code cocalls a coroutine that leaves via cocall, execution continues with the first statement following the cocall (it's almost as though you had called that other coroutine using a CALL instruction).
procedure coret;coret is nearly identical to coroutine.cocall with two major exceptions. First, note that this procedure is not a member of the coroutine class. Therefore, you do not specify an object name in front of the call to the coret procedure. Second, coret returns control to whomever cocalled the current coroutine. The current coroutine does not have to know who called it; coret figures this out and cocalls the appropriate coroutine.
Note that coret is not a "return" in the usual sense that the coroutine completes execution upon calling coret. coret is identical to a coroutine.cocall to the coroutine that called the current coroutine. In particular, after a coroutine returns to another, any future cocalls to this coroutine will continue execution with the first statement following the coret call.
method coroutine.cofree;When you are done with a coroutine, you should call the coroutine.cofree method to free up the stack space associated with that coroutine. You must not call coroutine.cofree from inside the coroutine you're cleaning up since it still needs its stack to transfer control to some other coroutine.
static mainPgm:coroutine; external( "?MainPgmCoroutine" );This is a special coroutine variable that contains the control information for the main program. If, inside a coroutine, you wish to cocall the main program, just use a cocall of the form "MainPgm.cocall();" and control in the main program will continue at the point of the last cocall executed in the main program. (Note: the term "main program" here does not imply that the cocall has to be in the actual main program of an HLA program, it simply refers to the thread of execution that starts in the main program. Your main program can call a procedure that transfers control to some coroutine via cocall. MainPgm.cocall will transfer control back into that procedure.)
![]() |
![]() |
![]() |
![]() |
![]() |