<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office"><head><!--[if gte mso 9]><xml><o:OfficeDocumentSettings><o:AllowPNG/><o:PixelsPerInch>96</o:PixelsPerInch></o:OfficeDocumentSettings></xml><![endif]--></head><body><div style="color:#000; background-color:#fff; font-family:Helvetica Neue, Helvetica, Arial, Lucida Grande, sans-serif;font-size:13px"><div id="yui_3_16_0_ym19_1_1480848504398_2940" dir="ltr">Given that - thanks also to the input received from this community - the semantics of "safe module unloading", as implemented in Experimental Oberon, has somewhat evolved over time, I have been asked to provide an updated description of the current status quo:</div><div id="yui_3_16_0_ym19_1_1480848504398_2940" dir="ltr"><br></div><div id="yui_3_16_0_ym19_1_1480848504398_2940"><br></div><div id="yui_3_16_0_ym19_1_1480848504398_2940">Safe module unloading</div><div id="yui_3_16_0_ym19_1_1480848504398_2940"><br></div><div id="yui_3_16_0_ym19_1_1480848504398_2940"><br></div><div id="yui_3_16_0_ym19_1_1480848504398_3119">The semantics of unloading a module has been refined as follows. If clients exist, a module is never unloaded. If no references to the module exist in the remaining modules and data structures, it is unloaded and its associated memory is released. If there are references, however, the specified module is initially removed only from the list of loaded modules, but will later be automatically removed from memory as soon as there are no more references to it. Such references can be in the form of type tags (addresses of type descriptors) in dynamic (heap) objects of other modules pointing to descriptors of types declared in the specified module, or in the form of procedure variables installed in static or dynamic objects of other modules referring to procedures declared in that module.</div><div id="yui_3_16_0_ym19_1_1480848504398_3119"><br></div><div id="yui_3_16_0_ym19_1_1480848504398_3120"><br id="yui_3_16_0_ym19_1_1480848504398_3121"></div><div id="yui_3_16_0_ym19_1_1480848504398_3122">Modules that have been removed only from the list of loaded modules (hidden modules) are marked with an asterisk in the output of the user command System.ShowModules. To achieve their automatic final removal from memory, the Oberon background task handling garbage collection includes a call to the command Modules.Collect. Thus, module data is kept in memory as long as needed and is removed from it as soon as possible.</div><div id="yui_3_16_0_ym19_1_1480848504398_3122"><br></div><div id="yui_3_16_0_ym19_1_1480848504398_3123"><br id="yui_3_16_0_ym19_1_1480848504398_3124"></div><div id="yui_3_16_0_ym19_1_1480848504398_3125">In sum, unloading a module affects only future references to it, while past references from other modules remain completely unaffected, For example, older versions of the module’s code can still be executed if they are referenced by procedure variables in other loaded modules, even if a newer version of the module has been loaded in the meantime.</div><div id="yui_3_16_0_ym19_1_1480848504398_3125"><br></div><div id="yui_3_16_0_ym19_1_1480848504398_3126"><br id="yui_3_16_0_ym19_1_1480848504398_3127"></div><div id="yui_3_16_0_ym19_1_1480848504398_3128">A simple mark-scan scheme is used to check dynamic references to a module. In the mark phase, dynamic records reachable by all other loaded modules are marked. This excludes records that are reachable only by the module itself. The scan phase scans the heap element by element, unmarks marked objects and verifies whether the type tags of the encountered marked records point to descriptors of types declared in the module to be unloaded, or whether procedure variables in these records refer to procedures declared in that module. The latter check is also performed for all static procedure variables.</div><div id="yui_3_16_0_ym19_1_1480848504398_3128"><br></div><div id="yui_3_16_0_ym19_1_1480848504398_3129"><br id="yui_3_16_0_ym19_1_1480848504398_3130"></div><div id="yui_3_16_0_ym19_1_1480848504398_3131">In order to make such a validation pass possible, type descriptors for dynamic records and descriptors of global module data have been extended with a list of procedure variable offsets, adopting an approach employed in one of the earlier implementations of Oberon. These additional offsets are simply appended to the existing fields of each descriptor, i.e. their offsets are greater than those of the fields of the pointer variables needed for the garbage collector, in order not to impact its performance. The compiler generating these modified descriptors, the format of the Oberon object file containing them and the module loader transferring them into memory have been adjusted accordingly.</div><div id="yui_3_16_0_ym19_1_1480848504398_3131"><br></div><div id="yui_3_16_0_ym19_1_1480848504398_3132"><br id="yui_3_16_0_ym19_1_1480848504398_3133"></div><div id="yui_3_16_0_ym19_1_1480848504398_3134">The following code excerpt (of procedure Modules.Check) shows a possible realization of such a reference-checking scheme:</div><div id="yui_3_16_0_ym19_1_1480848504398_3134"><br></div><div id="yui_3_16_0_ym19_1_1480848504398_3135"><br id="yui_3_16_0_ym19_1_1480848504398_3136"></div><div id="yui_3_16_0_ym19_1_1480848504398_4095"> PROCEDURE Check*(mod: Module; VAR res: INTEGER);</div><div id="yui_3_16_0_ym19_1_1480848504398_4096"> VAR M: Module; pref, pvadr, r: LONGINT;</div><div id="yui_3_16_0_ym19_1_1480848504398_4097"> BEGIN (*mod # NIL*) M := root;</div><div id="yui_3_16_0_ym19_1_1480848504398_4098"> WHILE M # NIL DO</div><div id="yui_3_16_0_ym19_1_1480848504398_4099"> IF (M # mod) & (M.name[0] # 0X) THEN Kernel.Mark(M.ptr) END ;</div><div id="yui_3_16_0_ym19_1_1480848504398_4100"> M := M.next</div><div id="yui_3_16_0_ym19_1_1480848504398_4101"> END ;</div><div id="yui_3_16_0_ym19_1_1480848504398_4102"> Kernel.Check(mod.data, mod.var, mod.code, mod.imp, res); (*dynamic refs*)</div><div id="yui_3_16_0_ym19_1_1480848504398_4103"> IF res = 0 THEN M := root;</div><div id="yui_3_16_0_ym19_1_1480848504398_4104"> WHILE (M # NIL) & (res = 0) DO (*static refs*)</div><div id="yui_3_16_0_ym19_1_1480848504398_4105"> IF (M # mod) & (M.name[0] # 0X) THEN</div><div id="yui_3_16_0_ym19_1_1480848504398_4106"> pref := M.pvar; SYSTEM.GET(pref, pvadr);</div><div id="yui_3_16_0_ym19_1_1480848504398_4107"> WHILE pvadr # 0 DO (*procedure variables*) SYSTEM.GET(pvadr, r);</div><div id="yui_3_16_0_ym19_1_1480848504398_4108"> IF (mod.code <= r) & (r < mod.imp) THEN res := 3 END ;</div><div id="yui_3_16_0_ym19_1_1480848504398_4109"> INC(pref, 4); SYSTEM.GET(pref, pvadr)</div><div id="yui_3_16_0_ym19_1_1480848504398_4110"> END</div><div id="yui_3_16_0_ym19_1_1480848504398_4111"> END ;</div><div id="yui_3_16_0_ym19_1_1480848504398_4112"> M := M.next</div><div id="yui_3_16_0_ym19_1_1480848504398_4113"> END</div><div id="yui_3_16_0_ym19_1_1480848504398_4114"> END</div><div id="yui_3_16_0_ym19_1_1480848504398_4115"> END Check;</div><div id="yui_3_16_0_ym19_1_1480848504398_3158"><br id="yui_3_16_0_ym19_1_1480848504398_3159"></div><div id="yui_3_16_0_ym19_1_1480848504398_3158"><br></div><div id="yui_3_16_0_ym19_1_1480848504398_3160">where procedure Kernel.Check implements the scan phase for dynamic references to the module, i.e. references from objects located in the heap:</div><div id="yui_3_16_0_ym19_1_1480848504398_3160"><br></div><div id="yui_3_16_0_ym19_1_1480848504398_3161"><br id="yui_3_16_0_ym19_1_1480848504398_3162"></div><div id="yui_3_16_0_ym19_1_1480848504398_4175"> PROCEDURE Check*(type0, type1, code0, code1: LONGINT; VAR res: INTEGER);</div><div id="yui_3_16_0_ym19_1_1480848504398_4176"> VAR p, r, mark, tag, size, offadr, offset: LONGINT;</div><div id="yui_3_16_0_ym19_1_1480848504398_4177"> BEGIN p := heapOrg; res := 0;</div><div id="yui_3_16_0_ym19_1_1480848504398_4178"> REPEAT SYSTEM.GET(p+4, mark);</div><div id="yui_3_16_0_ym19_1_1480848504398_4179"> IF mark < 0 THEN (*free*) SYSTEM.GET(p, size)</div><div id="yui_3_16_0_ym19_1_1480848504398_4180"> ELSE (*allocated*) SYSTEM.GET(p, tag); SYSTEM.GET(tag, size);</div><div id="yui_3_16_0_ym19_1_1480848504398_4181"> IF mark > 0 THEN SYSTEM.PUT(p+4, 0); (*unmark*)</div><div id="yui_3_16_0_ym19_1_1480848504398_4182"> IF (type0 <= tag) & (tag < type1) THEN (*types*) res := 1</div><div id="yui_3_16_0_ym19_1_1480848504398_4183"> ELSIF res = 0 THEN offadr := tag + 16; SYSTEM.GET(offadr, offset);</div><div id="yui_3_16_0_ym19_1_1480848504398_4184"> WHILE offset # -1 DO (*pointers*) INC(offadr, 4); SYSTEM.GET(offadr, offset) END ;</div><div id="yui_3_16_0_ym19_1_1480848504398_4185"> INC(offadr, 4); SYSTEM.GET(offadr, offset);</div><div id="yui_3_16_0_ym19_1_1480848504398_4186"> WHILE offset # -1 DO (*procedure variables*) SYSTEM.GET(p+8+offset, r);</div><div id="yui_3_16_0_ym19_1_1480848504398_4187"> IF (code0 <= r) & (r < code1) THEN res := 2 END ;</div><div id="yui_3_16_0_ym19_1_1480848504398_4188"> INC(offadr, 4); SYSTEM.GET(offadr, offset)</div><div id="yui_3_16_0_ym19_1_1480848504398_4189"> END</div><div id="yui_3_16_0_ym19_1_1480848504398_4190"> END</div><div id="yui_3_16_0_ym19_1_1480848504398_4191"> END</div><div id="yui_3_16_0_ym19_1_1480848504398_4192"> END ;</div><div id="yui_3_16_0_ym19_1_1480848504398_4193"> INC(p, size)</div><div id="yui_3_16_0_ym19_1_1480848504398_4194"> UNTIL p >= heapLim</div><div id="yui_3_16_0_ym19_1_1480848504398_4195"> END Check;</div><div dir="ltr" id="yui_3_16_0_ym19_1_1480848504398_4196"><br id="yui_3_16_0_ym19_1_1480848504398_4197"></div><div dir="ltr" id="yui_3_16_0_ym19_1_1480848504398_4196"><br></div><div dir="ltr" id="yui_3_16_0_ym19_1_1480848504398_4196">Complete source code (modules Kernel, Modules):</div><div dir="ltr" id="yui_3_16_0_ym19_1_1480848504398_4196"><br></div><div dir="ltr" id="yui_3_16_0_ym19_1_1480848504398_4196"><a href="http://github.com/andreaspirklbauer/Oberon-experimental" id="yui_3_16_0_ym19_1_1480848504398_4361">http://github.com/andreaspirklbauer/Oberon-experimental</a><br></div><div dir="ltr" id="yui_3_16_0_ym19_1_1480848504398_4196"><br></div><div dir="ltr" id="yui_3_16_0_ym19_1_1480848504398_4196"><br></div><div dir="ltr" id="yui_3_16_0_ym19_1_1480848504398_4196"><br></div><div dir="ltr" id="yui_3_16_0_ym19_1_1480848504398_4196"><br></div><div dir="ltr" id="yui_3_16_0_ym19_1_1480848504398_4196"><br></div><div dir="ltr" id="yui_3_16_0_ym19_1_1480848504398_4196"><br></div><div dir="ltr" id="yui_3_16_0_ym19_1_1480848504398_4196"><br></div><div dir="ltr" id="yui_3_16_0_ym19_1_1480848504398_4196"><br></div><div dir="ltr" id="yui_3_16_0_ym19_1_1480848504398_4196"><br></div><div id="yui_3_16_0_ym19_1_1480848504398_3163"><br></div><div dir="ltr" id="yui_3_16_0_ym19_1_1480848504398_3184"><br id="yui_3_16_0_ym19_1_1480848504398_3185"></div></div></body></html>