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.)
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). 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.
 Search for 'mod.num' in Modules.Load.
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 := 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 = 0X DO AllocPtr := SYSTEM.VAL(INTEGER, root); root := root.next END END ELSE res := 1 END END END Free;
 Not having "dormant", ie. currently inactive processes is also useful for autonomous error recovery, even without said memory restrictions.