Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- **Deleted** because it turns out that the "uninitialization" function is not reliably executed when using the GUI to quit the kernel (see comments by John Fultz on the other answer).
- -----
- As requested, here are two complete examples showing how to clean up temporary files based on LibraryLink uninitialization.
- Please either run the examples in a kernel that was started in command line mode (so you can see messages printed from C to the terminal), or use [this trick](http://szhorvat.net/pelican/displaying-debug-messages-coming-from-librarylink.html) to be able to see terminal messages even when running with a front end.
- ## Using library uninitialization with plain LibraryLink
- This method uses the `WolframLibrary_uninitialize`. The code is at the end of this section.
- Here's how to run it:
- << CCompilerDriver`
- lib = CreateLibrary[{"testlib.c"}, "testlib"]
- LibraryFunctionLoad[lib, "fun1", {Integer}, Integer]
- Now you will see `Library startup.` printed in the terminal, which indicated library initialization. This happens the first time a function is loaded from the library.
- What happens if we load a second function?
- LibraryFunctionLoad[lib, "fun2", {Integer}, Integer]
- The library was already initialized, so the message is not printed a second time.
- Now let us quit the kernel:
- Quit[]
- In the terminal you will see:
- Unitialize start ...
- Test message
- Uninitialize finished.
- This shows that the library cleanup code was run.
- The first message is printed using a simple `puts()` call.
- It seems to be possible to call back to the kernel from the uninitialization function. The `Test message` was a result of evaluating `Print["Test message"]` using the kernel.
- ----
- John Fultz made the following remarks in a comment on the answer above:
- > I think it's unlikely the LibraryLink proposal would work. The FE kills the kernel by sending MLTerminateMessage, which triggers a signal handler, which promptly exits. The advantage of this is that it always works promptly, no matter how hard the kernel might be hung. The disadvantage is that signal handlers are very limiting. You can't even call malloc or free in one, and definitely file system calls are right out. I would be surprised if the signal handler calls LibraryLink uninitialize functions. That having been said, I'm not an expert in the kernel functionality here.
- This would mean that this cleanup method may only work when quitting the kernel using the `Quit[]` command, but not when using the Evaluation -> Quit Kernel menu item.
- I tested this with Mathematica 11.1.1 on OS X and I found that the uninitialize function runs even when quitting the kernel using the menu item. However, callbacks to the kernel are no longer possible, making this cleanup method much less useful. Deleting some files using C or C++ code should still be feasible though.
- ----
- <!-- language: lang-c -->
- /* testlib.c */
- #include <mathlink.h>
- #include <WolframLibrary.h>
- #include <stdio.h>
- DLLEXPORT mint WolframLibrary_getVersion( ) {
- return WolframLibraryVersion;
- }
- DLLEXPORT int WolframLibrary_initialize( WolframLibraryData libData) {
- puts("Library startup.\n"); /* print to the terminal */
- return LIBRARY_NO_ERROR;
- }
- DLLEXPORT void WolframLibrary_uninitialize( WolframLibraryData libData) {
- puts("Unitialize start ...\n"); /* print to the terminal */
- /* Evaluate arbitrary Mathematica code */
- MLINK link = libData->getMathLink(libData);
- MLPutFunction(link, "EvaluatePacket", 1);
- MLPutFunction(link, "Print", 1);
- MLPutString(link, "Test message");
- libData->processMathLink(link);
- int pkt = MLNextPacket(link);
- if (pkt == RETURNPKT)
- MLNewPacket(link);
- puts("Uninitialize finished.\n"); /* print to the terminal */
- return;
- }
- /* The following are some arbitrary dummy functions that can be ignored.
- They are present only so that we can load the library using LibraryFunctionLoad[] */
- DLLEXPORT int fun1( WolframLibraryData libData, mint argc, MArgument *args, MArgument res) {
- mint x;
- x = MArgument_getInteger(args[0]);
- MArgument_setInteger(res, x);
- return LIBRARY_NO_ERROR;
- }
- DLLEXPORT int fun2( WolframLibraryData libData, mint argc, MArgument *args, MArgument res) {
- mint x;
- x = MArgument_getInteger(args[0]);
- x += 1;
- MArgument_setInteger(res, x);
- return LIBRARY_NO_ERROR;
- }
- ----
- ## Using managed library expression based cleanup with [LTemplate][1]
- An alternative method, also based on LibraryLink, is to put the cleanup code in the destructor of [managed library expressions][2]. Working with these through plain LibraryLink is a bit cumbersome, but luckily my [LTemplate package][1] makes using them significantly easier.
- Here's an example that shows how to register a number of file names for deletion. The advantage of this method is that it does not rely on kernel callbacks, thus it may not be affected by the problem John Fultz mentions. (But keep in mind that I only tested this on OS X!)
- SetDirectory@CreateDirectory[] (* we will work in a temporary directory *)
- << LTemplate`
- There will be one class, called `Cleanup`. The `registerFilename` function can register files for deletion, and the destructor will actually delete them.
- template = LClass["Cleanup",
- {LFun["registerFilename", {"UTF8String"}, "Void"]}];
- code = "
- #include <list>
- #include <string>
- #include <iostream>
- class Cleanup {
- std::list<std::string> filenames;
- public:
- ~Cleanup() {
- // in this example we will simply print file names instead of deleting anything
- for (auto s : filenames)
- std::cout << s << '\\n';
- }
- void registerFilename(const char *file) {
- filenames.push_back(file);
- mma::disownString(file);
- }
- };
- ";
- Export["Cleanup.h", code, "String"]
- I used some C++11 code above, so you may need to set the appropriate flag for your compiler when compiling. For `clang` and `gcc` it is `-std=c++11`. I believe for MSVC there is no need to enable C++11 explicitly, but you should look this up.
- CompileTemplate[template, "CompileOptions" -> {"-std=c++11"}]
- Load the library:
- LoadTemplate[template]
- Create an instance of the class:
- obj = Make[Cleanup]
- Register some file names:
- (obj@"registerFilename"[#] &) /@ {"one", "two", "three", "four"}
- The destructor will be called when the `obj` variable is cleared (so do not clear it prematurely!) or when the kernel exits.
- When evaluating `Quit[]`, you will see this printed in the terminal:
- one
- two
- three
- four
- If you use the first method, based on the uninitialize function, you can still have a registry of file names to delete, just like here.
- [1]: https://mathematica.stackexchange.com/q/96127/12
- [2]: http://reference.wolfram.com/language/ref/CreateManagedLibraryExpression.html
Advertisement
Add Comment
Please, Sign In to add comment