MODULE Kernel;
IMPORT SYSTEM, Kernel32, Machine, Heaps, Objects;
CONST
TimerFree = 0; TimerSleeping = 1; TimerWoken = 2; TimerExpired = 3;
Second* = Machine.Second;
TYPE
Finalizer* = Heaps.Finalizer;
Enumerator* = PROCEDURE {DELEGATE} ( obj: ANY; VAR cont: BOOLEAN );
FinalizerNode = POINTER TO RECORD (Objects.FinalizerNode)
nextObj {UNTRACED} : FinalizerNode;
END;
MilliTimer* = RECORD
start, target: LONGINT
END;
TYPE
Timer* = OBJECT
VAR
timer: Objects.Timer;
state-: SHORTINT;
nofHandleTimeout-, nofHandleTimeout2- : LONGINT;
nofSleeps-, nofSleepsLeft- : LONGINT;
nofAwaits-, nofAwaitsLeft- : LONGINT;
PROCEDURE HandleTimeout;
BEGIN {EXCLUSIVE}
INC(nofHandleTimeout);
IF state # TimerFree THEN INC(nofHandleTimeout2); state := TimerExpired END
END HandleTimeout;
PROCEDURE Sleep*(ms: LONGINT);
BEGIN {EXCLUSIVE}
INC(nofSleeps);
ASSERT(state = TimerFree);
state := TimerSleeping;
Objects.SetTimeout(timer, HandleTimeout, ms);
INC(nofAwaits);
AWAIT(state # TimerSleeping);
INC(nofAwaitsLeft);
IF state # TimerExpired THEN Objects.CancelTimeout(timer) END;
state := TimerFree;
INC(nofSleepsLeft);
END Sleep;
PROCEDURE Wakeup*;
BEGIN {EXCLUSIVE}
IF state = TimerSleeping THEN state := TimerWoken END
END Wakeup;
PROCEDURE &Init*;
BEGIN
state := TimerFree; NEW(timer);
nofHandleTimeout := 0; nofHandleTimeout2 := 0;
nofSleeps := 0; nofSleepsLeft := 0;
nofAwaits := 0; nofAwaitsLeft := 0;
END Init;
END Timer;
TYPE
FinalizedCollection* = OBJECT (Objects.FinalizedCollection)
VAR root: FinalizerNode;
PROCEDURE Add*(obj: ANY; fin: Finalizer);
VAR n: FinalizerNode;
BEGIN
NEW(n); n.c := SELF; n.finalizer := fin;
Heaps.AddFinalizer(obj, n);
BEGIN {EXCLUSIVE}
n.nextObj := root.nextObj; root.nextObj := n
END
END Add;
PROCEDURE Remove*(obj: ANY);
VAR p, n: FinalizerNode;
BEGIN {EXCLUSIVE}
p := root; n := p.nextObj;
WHILE (n # NIL) & (n.objWeak # obj) DO
p := n; n := n.nextObj
END;
IF n # NIL THEN p.nextObj := n.nextObj END;
END Remove;
PROCEDURE RemoveAll*(obj: ANY);
VAR p, n: FinalizerNode;
BEGIN {EXCLUSIVE}
p := root; n := p.nextObj;
WHILE n # NIL DO
IF n.objWeak = obj THEN
p.nextObj := n.nextObj;
ELSE
p := n;
END;
n := n.nextObj
END
END RemoveAll;
PROCEDURE Enumerate*(enum: Enumerator);
VAR fn, next: FinalizerNode; cont: BOOLEAN;
BEGIN {EXCLUSIVE}
fn := root.nextObj; cont := TRUE;
WHILE fn # NIL DO
next := fn.nextObj;
enum(fn.objWeak, cont);
IF cont THEN fn := next ELSE fn := NIL END
END
END Enumerate;
PROCEDURE EnumerateN*( enum: Enumerator );
VAR fn, next: FinalizerNode; cont: BOOLEAN; obj: ANY;
BEGIN {EXCLUSIVE}
fn := root.nextObj; cont := TRUE;
WHILE fn # NIL DO
next := fn.nextObj;
obj := NIL;
Machine.Acquire(Machine.GC);
IF (fn.objWeak # NIL ) & (fn.objStrong = NIL ) THEN
obj := fn.objWeak;
END;
Machine.Release(Machine.GC);
IF obj # NIL THEN enum( obj, cont ); END;
IF cont THEN fn := next ELSE fn := NIL END
END
END EnumerateN;
PROCEDURE &Clear*;
BEGIN {EXCLUSIVE}
NEW(root); root.nextObj := NIL
END Clear;
END FinalizedCollection;
VAR
second- : LONGINT;
PROCEDURE GetTicks*() : LONGINT;
BEGIN
RETURN Kernel32.GetTickCount()
END GetTicks;
PROCEDURE GC*;
BEGIN
Heaps.LazySweepGC;
END GC;
PROCEDURE SetTimer*( VAR t: MilliTimer; ms: LONGINT );
BEGIN
IF Machine.Second # 1000 THEN
ASSERT ( (ms >= 0) & (ms <= MAX( LONGINT ) DIV Machine.Second) );
ms := ms * Machine.Second DIV 1000
END;
IF ms < 5 THEN INC( ms ) END;
t.start := Kernel32.GetTickCount(); t.target := t.start + ms
END SetTimer;
PROCEDURE Expired*( VAR t: MilliTimer ): BOOLEAN;
BEGIN
RETURN Kernel32.GetTickCount() - t.target >= 0
END Expired;
PROCEDURE Elapsed*( VAR t: MilliTimer ): LONGINT;
BEGIN
RETURN (Kernel32.GetTickCount() - t.start) * (1000 DIV Machine.Second)
END Elapsed;
PROCEDURE Left*( VAR t: MilliTimer ): LONGINT;
BEGIN
RETURN (t.target - Kernel32.GetTickCount()) * (1000 DIV Machine.Second)
END Left;
BEGIN
ASSERT (1000 MOD Machine.Second = 0);
second := Machine.Second;
Heaps.GC := Heaps.InvokeGC;
END Kernel.
(**
Notes:
o The FinalizedCollection object implements collections of finalized objects.
o Objects added to a finalized collection (with Add) are removed automatically by the garbage collector when no references to them exist any more. They can also be removed explicitly with Remove.
o All the objects currently in a collection can be enumerated by Enumerate, which takes an enumerator procedure as parameter. The enumerator can also be a method in an object, which is useful when state information is required during the enumeration. The enumerator may not call other methods of the same collection.
o An object in a finalized collection can have an finalizer procedure associated with it, which gets called by a separate process when there are no references left to the object any more. A finalizer is usually used for some cleanup functions, e.g. releasing external resources. It is executed exactly once per object. During the next garbage collector cycle the object is finally removed.
*)
(*
to do:
o cancel finalizer when removing object
o fix module free race: module containing finalizer is freed. although the finalizer list is cleared, the FinalizerCaller has already taken a reference to a finalizer, but hasn't called it yet.
o consider: a module has a FinalizedCollection, without finalizers (NIL). when the module is freed, the objects are still in the finalization list, and will get finalized in the next garbage collection. The FinalizedCollection will survive the first collection, as the objects all have references to it through their c field. After all objects have been finalized, the FinalizedCollection itself is collected. No dangling pointers occur, except the untraced module field references from the type descriptors, which are only used for tracing purposes.
o check cyclic dependencies between finalized objects.
o GetTime(): LONGINT - return current time in ms
o Delay(td: LONGINT) - wait td ms
o AwaitTime(t: LONGINT) - wait at least until time t
o Wakeup(obj: ANY) - wake up object that is waiting
*)