Forums » Programming with the ECS » Oberon »
Coroutine for MainLoop and Task abstraction
Added by Runar Tenfjord about 1 month ago
I made a small test module with an adaption of the obl.generators.mod
code for a MainLoop and Task abstraction.
MODULE Test;
IMPORT SYSTEM, Exceptions IN OBL;
TYPE
Coroutine = RECORD*
frame, stack: SYSTEM.PTR
END;
Loop = RECORD (Coroutine)
END;
Task = RECORD (Coroutine)
caller: POINTER TO VAR- Loop;
time : UNSIGNED32;
status: BOOLEAN
END;
VAR
tick : UNSIGNED32;
PROCEDURE* (VAR coroutine: Coroutine) Call;
PROCEDURE- (VAR coroutine: Coroutine) Transfer (VAR target: Coroutine);
CONST StackSize = ASH (16, SIZE (INTEGER) + SIZE (LENGTH));
VAR pointer: SYSTEM.PTR; exception: Exceptions.AllocationFailure;
BEGIN
SYSTEM.CODE ("mov ptr [$fp + pointer], ptr $fp");
coroutine.frame := pointer;
IF target.frame = NIL THEN
SYSTEM.NEW (target.stack, StackSize);
pointer := target.stack;
IF pointer = NIL THEN exception.Raise END;
SYSTEM.CODE ("add ptr $sp, ptr [$fp + pointer], ptr StackSize - stackdisp");
target.Call;
SYSTEM.CODE ("mov ptr $sp, ptr $fp + exception - stackdisp");
SYSTEM.DISPOSE (target.stack);
target.frame := NIL;
ELSE
pointer := target.frame;
SYSTEM.CODE ("mov ptr $fp, ptr [$fp + pointer]");
END;
END Transfer;
PROCEDURE (VAR task: Task) Sleep(time : UNSIGNED32);
VAR caller: Loop;
BEGIN
task.time := time;
caller := task.caller^;
task.Transfer(caller);
END Sleep;
PROCEDURE (VAR task: Task) Finish;
VAR caller: Loop;
BEGIN
task.time := 0;
task.status := FALSE;
caller := task.caller^;
task.Transfer(caller);
END Finish;
PROCEDURE (VAR task: Task) Call;
BEGIN
TRACE("Task.Call");
task.Sleep(50);
TRACE("Task.Call2");
task.Sleep(100);
TRACE("Task.Call3");
task.Finish;
TRACE("Not to be reached!");
HALT(1);
END Call;
PROCEDURE (VAR loop: Loop) Call;
VAR
task : Task;
i : INTEGER;
BEGIN
task.caller := PTR(loop);
task.status := TRUE;
task.time := 0;
TRACE("Loop.Call");
i := 0;
LOOP
IF ~task.status THEN EXIT END;
loop.Transfer(task);
TRACE(task.time);
TRACE(task.status);
TRACE(i);
INC(i);
END;
TRACE("Loop finished");
END Call;
PROCEDURE Test;
VAR loop : Loop;
BEGIN
loop.Call;
END Test;
BEGIN
Test;
END Test.
In order to get this to work properly I changed the definition of
the field caller to:
caller: POINTER TO VAR- Loop;
Otherwise it will not point to the correct instance of
Loop.
The code for Sleep and Finish also corrected for this and
these changes might not be optimal.
This could be an issue with obl.generators.mod also.
Replies (2)
RE: Coroutine for MainLoop and Task abstraction - Added by Florian Negele about 1 month ago
I took the liberty and rewrote your module using the existing Generators module:
MODULE Test;
IMPORT Generators IN OBL;
TYPE Task = RECORD (Generators.Generator)
time: UNSIGNED32;
END;
PROCEDURE (VAR task: Task) Sleep (time: UNSIGNED32);
BEGIN
task.time := time;
task.Yield;
END Sleep;
PROCEDURE (VAR task: Task) Finish;
BEGIN
task.time := 0;
task.Yield;
END Finish;
PROCEDURE (VAR task: Task) Call;
BEGIN
TRACE ("Task.Call");
task.Sleep (50);
TRACE ("Task.Call2");
task.Sleep (100);
TRACE ("Task.Call3");
task.Finish;
END Call;
PROCEDURE Loop;
VAR task: Task; i: INTEGER;
BEGIN
task.time := 0;
TRACE ("Loop.Call");
i := 0;
WHILE task.Await () DO
TRACE (task.time);
TRACE (i);
INC (i);
END;
TRACE ("Loop finished");
END Loop;
BEGIN
Loop;
END Test.
I don't know if this is helpful. The call to Finish is strictly speaking not necessary as the implicit return from Call actually stops the while loop. The same applies to the status and the caller which I therefore removed. I also turned Loop into a procedure as it does not need to be couroutine either.
RE: Coroutine for MainLoop and Task abstraction - Added by Runar Tenfjord about 1 month ago
An almost embarrassing simplification of my version.
Thanks.