!7 cmodule FrontendUnloader > DynPrintLogAndEnabled { switchable double timeout = 60.0; // in seconds after last activity switchable double checkInterval = 10.0; switchable bool actuallyUnload; switchable bool verbose; transient bool firstCheck = true; transient volatile bool unloading; start { dm_registerAs_direct chatBotFrontendUnloader(); dm_doEvery(1.0, checkInterval, r check); } void check enter { if (!enabled || dm_isBooting()) ret; // stayLoaded -> module ID. also has a null key! new MultiMap mm; for (O mod : vmBus_queryAll chatBotFrontend()) { Bool b = cast dm_call(mod, 'shouldStayLoaded, timeout); mm.put(b, dm_moduleID(mod)); } if (empty(mm)) ret; int nPlus = l(mm.get(true)); int nMinus = l(mm.get(false)); int nIndifferent = l(mm.get(null)); if (verbose) printWithTime("Results for timeout " + timeout + "s: " + nPlus + " should stay loaded, " + nMinus + " can be unloaded, " + nIndifferent + " indifferent"); if (actuallyUnload) if (firstCheck) firstCheck = false; else unloadModules(mm.get(false)); } void unloadModules(LS modules) { if (empty(modules)) ret; temp dm_tempSetField(unloading := true); print("Unloading " + nModules(modules)); for (S mod : modules) { print(" Unloading " + dm_moduleIDAndName(mod)); dm_pcall(mod, 'deleteYourself); } } afterVisualize { addComponentInFront(buttons, dm_checkBox actuallyUnload()); } // API bool isUnloading() { ret unloading; } }