Im Gegensatz zu Modula-2 sieht der Oberon-Report keine Koroutinen vor. Dies mag im Rahmen des übersichtlichen Züricher Oberon-Systems noch vertretbar sein - in einem allgemeinen Umfeld würde dies jedoch die Erweiterbarkeit behindern. Aus diesem Grund wird analog zu Modula-2 ein Koroutinenkonzept von SYSTEM exportiert, das allerdings deutlich vereinfacht wurde und somit leichter anzuwenden ist. NEWPROCESS und TRANSFER wurden durch CRSPAWN, CRSWITCH und dem Modul Coroutines (siehe unten) ersetzt:
PROCEDURE CRSPAWN(VAR newcr: COROUTINE); PROCEDURE CRSWITCH(dest: COROUTINE);
Im Gegensatz zu Modula-2 können Prozeduren, die eine Koroutine repräsentieren, sich selbst zu Koroutinen deklarieren, wie folgendes Beispiel zeigt:
PROCEDURE Coroutine(myparams: CoroutineParameters; VAR newcr: SYSTEM.COROUTINE); BEGIN (* Start als normale Prozedur *) SYSTEM.CRSPAWN(newcr); (* Ruecksprung zu Setup *) (* hier wird die Koroutine nach CRSWITCH(newcr) fortgesetzt *) END Coroutine; PROCEDURE Setup; VAR cr: SYSTEM.COROUTINE; BEGIN Coroutine(crparams, cr); (* ... *) END Setup;
Im Gegensatz zu NEWPROCESS in Modula-2 dürfen Koroutinen Parameter besitzen, da sie selbst nicht als Parameter übergeben werden und damit einem vorgegebenen Prozedurtyp entsprechen müssen. Darüber hinaus muß weder ein Stack übergeben noch die Größe des Stacks festgelegt werden.
CRSPAWN ist dadurch natürlich etwas aufwendiger als NEWPROCESS und führt folgende Schritte durch:
Auch CRSWITCH ist in der Anwendung gegenüber TRANSFER vereinfacht worden. Da die Koroutinenzeiger konstant bleiben, ist es nur noch notwendig, die Zielkoroutine anzugeben. Die Zeiger auf die zuletzt aktive Koroutine und die aktuelle Koroutine werden laufend unterhalten und sind über das Modul Coroutines zugänglich:
DEFINITION Coroutines; IMPORT SYS := SYSTEM, Types; TYPE Coroutine = SYS.COROUTINE; VAR defaultsize: Types.Size; (* die Voreinstellung fuer die Anfangsgroesse einer Koroutine *) source: Coroutine; (* die Koroutine, die zuletzt aktiv war *) current: Coroutine; (* die Koroutine, die zur Zeit aktiv ist *) END Coroutines.
Die Zeiger source und current werden dabei von CRSWITCH gesetzt. Auf diese Weise ist es recht einfach, zur zuletzt aktiven Koroutine wieder zurückzuwechseln. In Modula-2 wurde hierfür der source-Parameter von TRANSFER verwendet.
Auf dieser Basis läßt sich leicht das Produzenten/Konsumenten-Problem lösen, wie folgendes Beispiel zeigt:
PROCEDURE Produce(VAR newtoken: Token; VAR producer, consumer: Coroutines.Coroutine); (* produziere jeweils ein Token, lege es in `newtoken' ab und wechsle dann zu `consumer' *) VAR token: Token; BEGIN SYSTEM.CRSPAWN(producer); LOOP (* produziere `token' *) newtoken := token; SYSTEM.CRSWITCH(consumer); END; END Produce; PROCEDURE Consume(VAR newtoken: Token; VAR producer, consumer: Coroutines.Coroutine); (* hole von `newtoken' jeweils ein neues Token, konsumiere es und wechsle dann zu `producer' *) VAR token: Token; BEGIN SYSTEM.CRSPAWN(consumer); LOOP token := newtoken; (* konsumiere `token' *) SYSTEM.CRSWITCH(producer); END; END Consume; PROCEDURE Setup; VAR token: Token; producer, consumer: Coroutines.Coroutine; BEGIN Produce(token, producer, consumer); Consume(token, producer, consumer); SYSTEM.CRSWITCH(producer); END Setup;