Syntax10.Scn.Fnt Syntax10b.Scn.Fnt Syntax10i.Scn.Fnt MODULE Coroutines; (* jr/14jul94, updated cn/20jan96 *) This module implements coroutines as known from Modula-2. Although it is machine dependent, it was programed in a way, which should make it adaptable to differen platforms by just a few changes in constant definitions. See the section marked CHANGE. There is one restriction which procedures, that you use to create processe in NEWPROCESS, have to obey. If they allocate space on the heap they must assure, that some global variable points to this space during the entiere time this space is needed. If you don't follow this rule, the garbage collector may reclaim the space, even though you still have some local variables pointing to it. On the Macintosh there is an additional restriction. The storage are used as stack for a process has itself to be on the stack. Otherwise some check code will complain about illegal stack pointer values. This module was used on the different platforms at different stages of its development. It showed some strange efects on the Oberon system, which we suspected as a problem with garbage collection. I've fixed it now for the SGI Oberon. As I don't have access to the other platforms, I don't really know if this module is still usable on them. In theory, this is expected: Macintosh: The Mac Oberon does never perform garbage collection while outside of the Oberon.Loop and therefore does not have to inspect the stack when deciding which memory space is to be marked as in use. Thus the disabling of the garbage collector is not needed, and the statements using Kernel.GCenabled can be removed, maybe even have to be removed, as Kernel doesn't export GCenabled. SGI,HP: The garbage collector may be called when executing a command. It then scans the stack to see, if there are pointer pointing to some heap ares, so it can mark this area as in use. When executing a process, the stack pointer points to the private stack of this process, and no more to the usual one. And if it points to the wrong side of the stack, the garbage collector will not inspect the stack at all. Therefore we disable the garbage collector when we are not in the main execution thread, and reenable it, when we return to the main thread. In case your command traps, while executing a coroutine, the garbage collector remains disabled, and Coroutines continues to believe, that you are not in the main thread. You should then use the Coroutines.Reset command to reenable the garbage collector, and reset Coroutines. Amiga: The same as said for SGI and HP applies to the Amiga. But I don't know, if the Amiga Kernel does export an GCenabled variable, as the Unix Kernel does. If it doesn't, you have to wait for a future relase supporting it. Later it was discovered, that the problem was cause by writing to the Log, while executing code called from the handler in the SortBasic module. Apart from the above mentioned problems, their might be others. So please be carefull when using commands which use Coroutines. Don't do this, while you have viewers with unsaved files open etc. If you can identify problems with Coroutines, please send Mail to claudio@dial.eunet.ch or joerg.straube@alcatel.ch so we can improve this module. The modifications for AXP were provided by Guenter Dotzel <100023.2527@compuserve.com> Thank you very much! IMPORT SYSTEM,Kernel; CONST (* CHANGE *) (* Amiga/Mac: *) MP=14; SP=15; negativeGrow=TRUE; align=4; (* HP: MP=30; SP=30; negativeGrow=FALSE; align=4; *) (* SGI: MP=29; SP=29; negativeGrow=TRUE; align=4; *) (* AXP: MP=29; SP=30; negativeGrow=TRUE; align=16; TYPE LONGINT=SYSTEM.SIGNED_64; *) PROC*=PROCEDURE; PROCESS*=RECORD p:PROC; stackForP:LONGINT; MP:LONGINT END; mainMP:LONGINT; proc:PROC; PROCEDURE NEWPROCESS*(p:PROC; VAR stack:ARRAY OF SYSTEM.BYTE; VAR new:PROCESS); BEGIN new.p:=p; IF negativeGrow THEN new.stackForP:=((SYSTEM.ADR(stack)+LEN(stack)) DIV align)*align; (* respect alignment requirments *) ELSE new.stackForP:=((SYSTEM.ADR(stack)+3) DIV align)*align; (* long word align *) END; new.MP:=0; END NEWPROCESS; PROCEDURE TRANSFER*(VAR from,to:PROCESS); BEGIN SYSTEM.GETREG(MP,from.MP); IF mainMP=0 THEN Kernel.GCenabled:=FALSE; mainMP:=from.MP; END; IF to.MP#0 THEN SYSTEM.PUTREG(MP,to.MP) ELSE proc:=to.p; (* copy into global variable needed when MP=SP *) SYSTEM.PUTREG(SP,to.stackForP); IF to.MP=mainMP THEN Kernel.GCenabled:=TRUE; mainMP:=0; END; proc; HALT(100); END; END TRANSFER; PROCEDURE Reset*; BEGIN mainMP:=0; Kernel.GCenabled:=TRUE; END Reset; BEGIN Reset; END Coroutines.