unload

unload ?switches? fileName ?packageName? ?interp?

http://www.purl.org/tcl/home/man/tcl8.5/TclCmd/unload.htm

New command in Tcl 8.5 that can remove functionality imported via load (in some cases). See TIP 100 [L1 ] from George Petasis for where it came from.


Hints for extension authors who want to have an unloadable extension:

  • Be prepared for extra work when using a custom Tcl_Object type with non-NULL freeProc
  • Be extremly careful in your use of TSD (Thread Specific Data)
  • Be prepared for trouble if your extension involves the VFS layer
  • ...

JH: Note that TIP 239 [L2 ] will likely change the unload command before 8.5 is final.


RS 2007-10-19: Here's a simple working example of building a DLL with tcltcc, loading, calling, unloading it:

 #!/usr/bin/env tclsh85

 package require tcc 0.2
 set d [tcc::dll]
 $d ccode {static int fib(int n) {return n <= 2? 1 : fib(n-1) + fib(n-2);}}
 $d cproc fiboy {int n} int {return fib(n);}
 $d cproc hello {} char* {return "world";}
 $d ccode {
    DLL_EXPORT int Fiboy_Unload(Tcl_Interp* interp, int flags) {
        Tcl_DeleteCommand(interp,"fiboy");
        Tcl_DeleteCommand(interp,"hello");
        printf("bye, world! flags: %d\n", flags);
        return TCL_OK;
    }
 }
 $d write -name fiboy -code {Tcl_Eval(interp,"puts {hello Fiboy!}");}

 load fiboy[info sharedlibextension]
 puts "[fiboy 20] [hello]"

 unload fiboy[info sharedlibextension]
 catch {puts "[fiboy 20] [hello]"} res
 puts $res

which produces

 hello Fiboy!
 6765 world
 bye, world! flags: 2
 invalid command name "fiboy"

Only if a _Unload function is provided (_SafeUnload for safe interps) will unload succeed. Be careful to delete all commands the DLL created, otherwise get a seg fault.

Another observation: on Win XP at least (ActiveTcl 8.5b1), the load/unload order matters. Given foo.dll and bar.dll, the sequence

   load foo; load bar; unload foo; unload bar

segfaults, while

   load foo; load bar; unload bar; unload foo

succeeds. Is that so? The man page does not mention such a condition. Example:

 $ tclsh85
 % load test.dll
 DLL loaded
 % load fiboy.dll
 hello Fiboy!
 % unload test.dll
 DLL unloaded
 % unload fiboy.dll
 ---------------------------------------- seg fault popup --------------------------
 /etc/dk $

Then again, on Windows 95, 8.5b1 built myself with mingw, I can unload a pair of DLLs in any of the two orders... Funny.


kbk would like to revert TIP #100 because it doesn't really work. There are too many calls that retain pointers into the loaded library, e.g., Tcl_RegisterObjType, which can't be undone. (1)


AMG: How does one safely write an unloadable extension having internal data? Said data should be deallocated or otherwise cleaned up when the extension is unloaded, but there is no obvious way for the extension load function to reliably leave behind breadcrumbs for the extension unload function to follow back to the internal data.

Functions implementing commands are given nice client data, and the command deletion function gets a pointer to that client data, but there isn't anything analogous for extensions.

If each part of the internal data can cleanly be associated with exactly one command, then the command deletion function can be made responsible for the cleanup, and there's no problem. However, it's more likely that multiple commands will need to share data, in which case it's dangerous for any one command deletion function to do the cleanup, for example if one command is deleted before calling the other.

The only thing I can think to do is for the extension to maintain a hash table mapping from interpreter pointers to internal data. This hash table needs to be thread-local or have access protected by mutexes. However, I feel it's wrong to force extensions to manage this degree of hassle, and I feel this capability should have been part of the original specification for loading and unloading extensions. (Yes, I am aware the latter came much later.)

Well, I guess another solution would be to go ahead and associate the data with every command, then make every command's deletion function delete all the other commands too, at least those not already deleted. Keeping track of this can get tricky. The internal data would need to contain all the Tcl_Command tokens returned by each invocation of Tcl_CreateObjCommand(). This is probably the simplest and safest solution with the current architecture.

Perhaps the situation can be remedied by adding new functions that can be called within the extension load and unload functions to store and retrieve opaque client data for the specific instance of the loaded extension. Though I am leery of functions that behave differently depending on when they are called, since they must rely on global variables to provide a communication path jumping from one stack frame to another several layers down.

I'd much rather the extension load function return NULL in place of TCL_ERROR and non-NULL in place of TCL_OK, then that non-NULL value would later be passed to the extension unload function. While it might seem too late to take this approach, the new-style API could be selected by having the load function be named something other than PACKAGE_Init().

References

1
kbk, Tcl Chatroom, 2014-11-24