Self-unloading With Modules.Free?
Posted: Tue Jun 01, 2021 9:23 am
Is there any reason why, or a situation when, this would not work?
That is, a module unloading itself. The reasoning here is that Modules.Free just sets the module name to the empty string, and adjusts the references, but the module stays in memory unchanged otherwise, so 'Run' in the example terminates correctly. (In fact, the logic of Modules.Load even makes use of this fact for the module number, ie. the index into the module table, if I read the code correctly[1].)
Based on studying the code, and running tests, I am pretty confident that this self-unloading works. However, I might be overlooking something.
Background: my use case is a memory-constrained controller, which has major modes of operation, each with its set of processes (or tasks)[2]. To switch to another mode, the current control program starts a next one, unloading itself to make space. The first program's main process terminates and removes all its (sub/child) processes, sets up the successor, uses Modules.Free on itself (and possibly on other modules belonging to the program, in the right order), terminates and removes itself, and then a loader process permanently in memory will start the new program with its processes. Obviously, the above test case is not a representation of this machinery.
As an aside, I have modified Modules.Free to keep no slots of freed modules "on the top" of the linked modules list (Modules.root), so the above "program chaining" can be done forever with careful module loading and unloading, without module memory space fragmentation.
[1] Search for 'mod.num' in Modules.Load.
[2] Not having "dormant", ie. currently inactive processes is also useful for autonomous error recovery, even without said memory restrictions.
Code: Select all
MODULE M;
IMPORT Modules, Oberon;
PROCEDURE Run*;
BEGIN
Modules.Free("M");
Oberon.Collect(0)
END Run;
END M.
Based on studying the code, and running tests, I am pretty confident that this self-unloading works. However, I might be overlooking something.
Background: my use case is a memory-constrained controller, which has major modes of operation, each with its set of processes (or tasks)[2]. To switch to another mode, the current control program starts a next one, unloading itself to make space. The first program's main process terminates and removes all its (sub/child) processes, sets up the successor, uses Modules.Free on itself (and possibly on other modules belonging to the program, in the right order), terminates and removes itself, and then a loader process permanently in memory will start the new program with its processes. Obviously, the above test case is not a representation of this machinery.
As an aside, I have modified Modules.Free to keep no slots of freed modules "on the top" of the linked modules list (Modules.root), so the above "program chaining" can be done forever with careful module loading and unloading, without module memory space fragmentation.
Code: Select all
PROCEDURE Free*(name: ARRAY OF CHAR);
VAR mod, imp: Module; p, q: INTEGER;
BEGIN
mod := root; res := 0;
WHILE (mod # NIL) & (mod.name # name) DO mod := mod.next END;
IF mod # NIL THEN
IF mod.refcnt = 0 THEN
mod.name[0] := 0X; p := mod.imp; q := mod.cmd;
WHILE p < q DO SYSTEM.GET(p, imp); DEC(imp.refcnt); INC(p, 4) END;
IF mod = root THEN
WHILE root.name[0] = 0X DO
AllocPtr := SYSTEM.VAL(INTEGER, root); root := root.next
END
END
ELSE
res := 1
END
END
END Free;
[2] Not having "dormant", ie. currently inactive processes is also useful for autonomous error recovery, even without said memory restrictions.