Lua is able to load .dll dynamically either by using require, or by package.loadlib. There’s however no function to unload the module.
I want to be able to update the module in running program. The program itself would be notified about the new version of the .dll. Then it would load the new one, change the module table and unload the previous one.
I’ve managed to do it by using some strange code snippet with unrequire function, and forcing the garbage collection on code. I’d like to be able to do it in a more civilized way. I know that there are functions dlclose and FreeLibrary, on windows and linux. There’s no way I could call them, though, because I have no access to the actual handle of the library.
The lack of unload function is motivated by the fact, that the lua interpreter can’t really know if it won’t be using the C functions from the module anymore. This is not the case in my situation, as I’m going only to update the library, rather than remove it completely.
There is a way to do it, but there will be limitations to making it work.
The basic idea is to create a layer of indirection between the Lua functions and scripts that
requireand use the module, and the actual C functions that you’re trying to change behind the scenes. This is best done in your module itself. That is, rather than having Lua code do the replacing of functions and such, have the C functions do the replacing themselves.That way, you’re in control.
The idea is simple. You have two modules: a stub module that is what Lua registers functions for, and the actual module with the function implementations. Only the latter module changes.
Your stub module is a Lua module that has two functions that it registers with Lua. The first is a stub function, which it registers multiple times. It is registered as a closure with a single upvalue.
That upvalue is a C function. The only thing this stub function does is pull the one upvalue out of the Lua state and call it with the same parameters it was given, returning exactly the values that the function being called returns. It requires a bit of stack finessing to pass-through correctly, but I’ll assume you know how to handle that. Also, this function should check the value of the upvalue; if its
nil, then do nothing and return nothing (this prevents errors).The second function is something we’ll get to in a bit.
Your stub module initialization routine will load your actual module, which is really just a DLL. This DLL needs to have a function that will provide a list of functions the actual module exports. Thus, it can query the various Lua functions that are to be forwarded.
For each exported function from the actual module, it registers the stub function with Lua, giving it that name. The upvalue for the stub is set to be the exported function, loaded without upvalues.
Obviously, the handle to the DLL will need to be preserved, likely as a userdata in a registry entry or something.
The second function your stub module exports is the one that will reload the actual module. To do this, you load the new DLL (not unloading the old one first). To make this process robust (despite your assurance that the function lists will be identical. I dislike assuming things like this), you will need to iterate over your module table.
For every registered function that isn’t in the new DLL, set it’s upvalue to
nil(and remove them from the module’s table). For every registered function that is in the new DLL, change the upvalue to be the new function. If the new DLL has unregistered functions, stick them in the table now by registering the stub function with the new upvalue.After all of this is done, then you can unload the old DLL.
There; done.
Note: in order for this to work, your module functions must be well-behaved. They can’t do things like register other C-functions and so forth. They can’t use Lua 5.2’s mechanism for yielding and resuming coroutines across C boundaries. And so forth. Otherwise, you run the risk of calling into an unloaded DLL.