Module Manager Thread last updated on 2004-08-12 23:19:53

Posted by member 37809 on 2004-08-12 00:19:27

(Tell me what you think. I finally made this the other day and so far I think it works well in my theme, since it takes advantage of it, with components that can load and unload during runtime. Two things it cannot address are hooking, and memory/GDI leaks caused by modules. I don't know the implications of bang-loading/unloading modules often, but if it's stabler than recycling, I'm all for it.)

These are wrapper bangs for components in your theme to load and unload modules for temporary or regular (full-time) use. Should modules be unneeded, these bangs unlike !reloadmodule and !unloadmodule will prevent the unloading if they are in use elsewhere. Note this system requires that "everyone cooperates", for instance if you plan to use this with personal settings. This also implies that the modules you load this way are loaded through scripts. Most easily suited are perhaps bangmodules.

The actual unloading of modules is delayed by a 30s timer. After this idle period has elapsed the queue of modules to unload is processed. This prevents a rapid succession of loading and unloading of a module to be used temporarily; the timer is restarted on each call to the following bangs:

!modmgr_load <moduleID>
!modmgr_unload <moduleID>

You can load and unload a module by NetLoadModule (NLM) (default) or by local or absolute path to a module.

moduleID may either be an alias or a NLM-compatible name in the format:
moduleName-ver.sion
(only the part before the first '-') will be used to identify the module. The reason modules are identified by a single word is to prevent different versions of the same module from being loaded. The first version loaded by !modmgr_load will be kept loaded until all references to the module are gone.

The bangs might be confusing in their names. They might be better named as 'use' and 'unuse' instead of 'load' and 'unload'.

To use an alias, !varset mod_alias_moduleName to one of the following formats:

local:moduleName.dll - refers to "$ThemeDir$modules\moduleName.dll"
nlm:moduleName-ver.sion - refers to a different module for NLM to load instead
Anything else is assumed to be an absolute path to a module:
x:\pathtomodule\module.dll

To exclude a module from (being affected by) the Module Manager, set an mzscript variable to any value in the format:
mod_persistent_<modulename>

Module Manager Status:
To see what's happening in the backend as things happen, use !modmgr_list toggle (it only updates while it's shown)
You can toggle the zOrder of this window by double clicking on it. It may be dragged. You can roll it up by right-clicking the title.
Here you can see a list of modules loaded via !modmgr_load, and any modules in the queue for unloading. You can force modules in this queue to unload immediately by clicking 'unload'; otherwise wait for activity to cease for 30s.

The status output requires the !listadd bang from
http://www.litestep.net/index.php?section=4&action=view&catId=7&id=4250
To disable the status output entirely (it probably slows overall operation down a tiny bit), comment out/delete the areas marked ;for status output
And rid the label config.

Designed with:
xlabellight-3.0.9
timer-0.5
mzscript-0.9-beta_12

These are taken straight from my theme, so you will have to adjust paths and filenames to these two files; they each reference the other.

See the end of the script for examples on using the filters/aliases.


"$SystemDir$modmgr.cfg"
if 0; for xlabel
<b><u>Module Mana</u>g<u>er Status</u> </b> <a href='!modmgr_list toggle'><font face=Webdings size=17 color=#0000ff>r</font></a><br><br><b>handleCount : moduleID </b><br>
<br><b>modules pending <a href='!modmgr_clean'><font color=#0000ff>unload</font></a>: </b><br>
<br><b>modules pending unload:</b><br> <i><font color=#808080>(none) </font></i><br>
endif

*Timer modmgr_clean 30s !modmgr_clean ; change delay if you'd like
;for status output
*Timer modmgr_update 1024 !modmgr_list ; to prevent unneeded consecutive updates to status output

*mzScriptFile "$SystemDir$modmgr.mzs"

modmgrWidth 168
modmgrCurrentWidth $modmgrWidth$
modmgrH 320
modmgrHeight $modmgrH$
modmgrX 20
modmgrY $(ResolutionY-modmgrHeight)/2$
modmgrCurrentY $modmgrY$

modmgrUseSolidColors true
modmgrSolidColors eeeeee 000000 000000
modmgrSolidBevelSize 1
modmgrFont "Tahoma"
modmgrFontColor 000000
modmgrFontHeight 13
modmgrAlwaysOnTop false
modmgrFontAlign left
modmgrFontVertAlign top
modmgrFontColor 000000
modmgrAutoWidthMode left
modmgrAutoLineBreak false
modmgrImageMode tile
modmgrImageLeftEdge 1
modmgrImageTopEdge 1
modmgrImageRightEdge 1
modmgrImageBottomEdge 1
modmgrLeftBorder 4
modmgrTopBorder 4
modmgrRightBorder 2
modmgrBottomBorder 4
modmgrUpdateInterval 999999
modmgrStartHidden false
modmgrMoveable true
modmgrMoveModifierKey ".none"
modmgrOnLeftDoubleClick !LabelToggleAlwaysOnTop modmgr
modmgrOnRightClick !modmgr_roll
modmgrOnRightDoubleClick !modmgr_roll



"$SystemDir$modmgr.mzs"
*script bang !modmgr_load ;moduleID
*script exec !TimerKill modmgr_clean
*script exec !setlistsep -
*script exec !varset tmodmgr_load_a1 %{\1:}
*script exec !setlistsep :
;
*script exec !varset tmodmgr_load_persi 0
*script exec !ifexist mod_persistent_%{tmodmgr_load_a1} !varset tmodmgr_load_persi 1
*script gotoif ("%{tmodmgr_load_persi}" = "1") quickclean
;
*script exec !varset tmodmgr_load_a2 %{\1}
*script exec !ifnexist mod_%{tmodmgr_load_a1} !varset mod_%{tmodmgr_load_a1} 0:%{tmodmgr_load_a2}
;
;for status output
*script exec !listadd mods %{tmodmgr_load_a1}
;
*script exec !varset tmodmgr_load_modf "%{P}{mod_%{tmodmgr_load_a1}:}"
*script exec !varset tmodmgr_load_modl "%{P}{mod_%{tmodmgr_load_a1}:_}"
*script gotoif ("%{tmodmgr_load_modf}" > "0") loaded
*script exec !ifnexist mods_unloadq !varset mods_unloadq empty
*script exec !varset tmodmgr_load_todo %{mods_unloadq}
*script exec !varset tmodmgr_load_keep empty
*script label loop
*script gotoif ("%{tmodmgr_load_todo}" = "empty") done
*script exec !ifeval ("%{tmodmgr_load_todo:}" <> "%{tmodmgr_load_a1}") '|varset tmodmgr_load_keep %{tmodmgr_load_keep}:%{tmodmgr_load_todo:}'
*script exec !varset tmodmgr_load_todo %{tmodmgr_load_todo:_}
*script goto loop
*script label done
*script exec !varset mods_unloadq %{tmodmgr_load_keep:_}:empty
*script exec !ifeval ("%{mods_unloadq}" = ":empty") '|varset mods_unloadq empty'
;
*script exec !varset tmodmgr_load_alias "%{P}{mod_alias_%{tmodmgr_load_a1}}"
*script gotoif ("%{tmodmgr_load_alias}" <> "") alias
*script exec !NetReloadModule %{tmodmgr_load_modl}
*script goto loaded
;

*script label alias
*script gotoif ("%{tmodmgr_load_alias:}" = "nlm") nlm
*script gotoif ("%{tmodmgr_load_alias:}" = "local") local
*script exec !reloadmodule "%{tmodmgr_load_alias}" ; assume absolute path
*script goto loaded
*script label local
*script exec !reloadmodule "$ThemeDir$modules\%{tmodmgr_load_alias:_}"
*script goto loaded
*script label nlm
*script exec !NetReloadModule %{tmodmgr_load_alias:_}
;
*script label loaded
*script exec !varadd tmodmgr_load_modf 1
*script exec !varset mod_%{tmodmgr_load_a1} %{tmodmgr_load_modf}:%{tmodmgr_load_modl}
*script exec !varremove tmodmgr_load_alias
*script exec !varremove tmodmgr_load_a2
*script exec !varremove tmodmgr_load_modf
*script exec !varremove tmodmgr_load_modl
*script exec !varremove tmodmgr_load_todo
*script exec !varremove tmodmgr_load_keep
*script label quickclean
*script exec !varremove tmodmgr_load_persi
*script exec !varremove tmodmgr_load_a1
*script exec !TimerStart modmgr_clean
;
;for status output
*script exec !modmgr_list
;
*script ~bang


*script bang !modmgr_unload ;moduleID
*script exec !TimerKill modmgr_clean
*script exec !setlistsep -
*script exec !varset tmodmgr_unload_a1 %{\1:}
*script exec !setlistsep :
;
*script exec !varset tmodmgr_unload_persi 0
*script exec !ifexist mod_persistent_%{tmodmgr_unload_a1} !varset tmodmgr_unload_persi 1
*script gotoif ("%{tmodmgr_load_persi}" = "1") quickclean
;
*script exec !varset tmodmgr_unload_continue 1
*script exec !ifnexist mod_%{tmodmgr_unload_a1} !varset tmodmgr_unload_continue 0
*script gotoif ("%{tmodmgr_unload_continue}" = "0") clean1
*script exec !varset tmodmgr_unload_modf "%{P}{mod_%{tmodmgr_unload_a1}:}"
*script exec !varadd tmodmgr_unload_modf -1
*script gotoif ("%{tmodmgr_unload_modf}" < "0") clean2
*script exec !varset tmodmgr_unload_modl "%{P}{mod_%{tmodmgr_unload_a1}:_}"
*script exec !varset mod_%{tmodmgr_unload_a1} %{tmodmgr_unload_modf}:%{tmodmgr_unload_modl}
*script gotoif ("%{tmodmgr_unload_modf}" > "0") keeploaded
*script exec !ifnexist mods_unloadq !varset mods_unloadq empty
*script exec !varset tmodmgr_unload_todo %{mods_unloadq}
*script exec !varset tmodmgr_unload_found 0
*script label loop
*script gotoif ("%{tmodmgr_unload_todo}" = "empty") done
*script gotoif ("%{tmodmgr_unload_todo:}" = "%{tmodmgr_unload_a1}") found
*script exec !varset tmodmgr_unload_todo %{tmodmgr_unload_todo:_}
*script goto loop
*script label found
*script exec !varset tmodmgr_unload_found 1
*script label done
*script exec !ifeval ("%{tmodmgr_unload_found}" = "0") '|varset mods_unloadq %{tmodmgr_unload_a1}:%{mods_unloadq}'
*script label keeploaded
*script exec !varremove tmodmgr_unload_todo
*script exec !varremove tmodmgr_unload_found
*script exec !varremove tmodmgr_unload_modl
*script label clean2
*script exec !varremove tmodmgr_unload_modf
*script label clean1
*script exec !varremove tmodmgr_unload_continue
*script label quickclean
*script exec !varremove tmodmgr_unload_persi
*script exec !varremove tmodmgr_unload_a1
*script exec !TimerStart modmgr_clean
;
;for status output
*script exec !modmgr_list
;
*script ~bang


; process unload list immediately, reset timer
*script bang !modmgr_clean
*script exec !TimerKill modmgr_clean
*script exec !ifnexist mods_unloadq !varset mods_unloadq empty
*script label loop
*script gotoif ("%{mods_unloadq}" = "empty") done
*script exec !varset tmodmgr_clean_modl "%{P}{mod_%{mods_unloadq:}:_}"
;
*script exec !varset tmodmgr_clean_alias "%{P}{mod_alias_%{mods_unloadq:}}"
*script gotoif ("%{tmodmgr_clean_alias}" <> "") alias
;
*script exec !NetUnloadModule %{tmodmgr_clean_modl}
*script goto unloaded
*script label alias
*script gotoif ("%{tmodmgr_clean_alias:}" = "nlm") nlm
*script gotoif ("%{tmodmgr_clean_alias:}" = "local") local
*script exec !unloadmodule "%{tmodmgr_clean_alias}" ; assume absolute path
*script goto unloaded
*script label local
*script exec !unloadmodule "$ThemeDir$modules\%{tmodmgr_clean_alias:_}"
*script goto unloaded
*script label nlm
*script exec !NetUnloadModule %{tmodmgr_clean_alias:_}
*script label unloaded
;
*script exec !varremove mod_%{mods_unloadq:}
;
*script exec !varset mods_unloadq %{mods_unloadq:_}
*script goto loop
*script label done
*script exec !varremove tmodmgr_clean_alias
*script exec !varremove tmodmgr_clean_modl
;
;for status output
*script exec !modmgr_list
;
*script ~bang


;for status output
*script bang !modmgr_list
*script gotoif ("%{args}" = "toggle") toggle
*script gotoif ("%{modmgr_list}" <> "1") end
*script label update
*script gotoif ("%{second}" = "%{modmgr_lastupd}") wait
*script exec !SetEvar modmgrText "[line('$SystemDir$modmgr.cfg',2)]"
*script exec !varset tmodmgr_console_todo %{mods}:empty
*script gotoif ("%{tmodmgr_console_todo}" = ":empty") done1
*script label next1
*script gotoif ("%{tmodmgr_console_todo}" = "empty") done1
*script exec !varset tmodmgr_console_mod "%{P}{mod_%{tmodmgr_console_todo:}}"
*script gotoif ("%{tmodmgr_console_mod}" = "") skip1
*script exec !SetEvar modmgrText "%#modmgrText%# %{tmodmgr_console_mod:} : %{tmodmgr_console_mod:_}\n"
*script label skip1
*script exec !varset tmodmgr_console_todo %{tmodmgr_console_todo:_}
*script goto next1
*script label done1
*script exec !varset tmodmgr_console_todo %{mods_unloadq}:empty
*script gotoif ("%{tmodmgr_console_todo}" = "empty:empty") empty2
*script exec !SetEvar modmgrText "%#modmgrText%#[line('$SystemDir$modmgr.cfg',3)]"
*script goto next2
*script label empty2
*script exec !SetEvar modmgrText "%#modmgrText%#[line('$SystemDir$modmgr.cfg',4)]"
*script goto done2
*script label next2
*script gotoif ("%{tmodmgr_console_todo:}" = "empty") done2
*script exec !SetEvar modmgrText "%#modmgrText%# %{tmodmgr_console_todo:}\n"
*script exec !varset tmodmgr_console_todo %{tmodmgr_console_todo:_}
*script goto next2
*script label done2
*script exec !LabelSetText modmgr "%#modmgrText%#"
*script exec !varremove tmodmgr_console_todo
*script exec !varremove tmodmgr_console_mod
*script exec !varset modmgr_lastupd %{second}
*script label end
*script exit
*script label wait
*script exec !TimerKill modmgr_update
*script exec !TimerStart modmgr_update
*script exit
*script label toggle
*script exec !varremove args
*script gotoif ("%{modmgr_list}" = "1") disable
*script exec !LabelCreate modmgr
*script exec !varset modmgr_list 1
*script goto update
*script label disable
*script exec !ifeval ("%{modmgr_roll}" = "1") '|modmgr_roll'
*script exec !LabelDestroy modmgr
*script exec !varset modmgr_list 0
*script ~bang
*script start !varset mods empty


; for gui
*script bang !modmgr_roll
*script exec !ifnexist modmgr_roll !varset modmgr_roll 0
*script exec !varset tmodmgr_roll %#%{mousey}-modmgrCurrentY%#
*script gotoif ("%{tmodmgr_roll}" > "20") clean
*script gotoif ("%{modmgr_roll}" = "0") roll
*script exec !LabelResize modmgr $modmgrCurrentWidth$ $modmgrH$
*script exec !LabelUpdate modmgr
*script exec !varset modmgr_roll 0
*script goto clean
*script label roll
*script exec !LabelResize modmgr $modmgrCurrentWidth$ 20
*script exec !varset modmgr_roll 1
*script label clean
*script exec !varremove tmodmgr_roll
*script ~bang


; set modules not to be affected (ignored in !modmgr_load/!modmgr_unload)
*script start !execute [!varset mod_persistent_timer 1][!varset mod_persistent_xlabel 1][!varset mod_persistent_xlabellight 1][!varset mod_persistent_mzscript 1][!varset mod_persistent_skinbox 1]

; set module aliases
*script start !execute [!varset mod_alias_mzhsl2rgb local:MzHSL2RGB.dll]

Posted by member 103440 on 2004-08-12 14:12:34 link

Simply amazing! I need to test this on my theme.
- How is it works with “hooking”?
- I use hooking with xLabel, is this a problem?
- And mzScript threaded?

I think reloading modules is much faster than recycles. And, a discussion on this topic would be appreciated ;)

Posted by member 37809 on 2004-08-12 14:23:09 link

In short, don't load modules threaded unless it works...
--threading tends to break scripts.

This Module Manager is best for use with less interactive/visual directly-manipulated modules, perhaps.

It doesn't address hooking. "Rehooking", as Cerbie has experimented with can be tricky so I choose to avoid that until it can be reliably done.

For those modules that are hooked, I don't know. It's really beyond the scope and intent of this script.

The modules that do provide hooking should be left loaded all the time IMO. This is what the persistent flag mechanism does: ignore a module. In theory you could load most modules through this Module Manager but I don't think it's practical. After all, a theme is there to keep a minimum set of modules loaded.

The Module Manager ultimately loads and unloads modules by request, trying to be fair...

Some themes insist on doing that for some modules: like if you don't show a module, you may as well unload it instead of hiding it. But if you need to use the module for another reason somewhere else, whether it be in another part of the theme, or in your personal scripts (at least I have them :), then the module should be kept loaded.

But modules that aren't needed can then be unloaded, obviously to free up memory. I can't say if the overhead and complexity of this Module Manager makes it worth using to the extent that it functions for its goals; it depends how it is used.

Also, different module versions can be very different to where there are incompatibilities anyway, amongst scripts; this system doesn't do anything about that.

I said in the post that it cannot address memory leaks. Actually the system is in place to reduce recycles, which just unloads all modules and reloads them all. In this sense then, keep loaded the modules that leak on recycles. :D

Posted by member 12025 on 2004-08-12 23:19:52 link

On re-hooking, it works fine, until you want to reload lsbox/xlabel. Then it blows up on the next !recycle. Doing a reload of all modules that are hooked and then the module they are hooked to takes about as long as a recycle...so !recycle it is :/.

I'd be using such a module manager myself if I weren't doing boxed themes. It could be handy for switching between interface paradigms (any change major enough to warrant different modules), or if you do a sub-theme kind of setup.