From: William S F. <ws...@fu...> - 2013-01-28 21:52:22
|
On 25/01/13 23:41, Eric Wing wrote: > On 1/25/13, William S Fulton <ws...@fu...> wrote: >> On 23/01/13 08:29, Eric Wing wrote: >>> Sorry if some of you have already seen this, but I'm struggling with >>> %newobject and %delobject. I can't tell if the problem is if I'm >>> misusing and misunderstanding them or if there is an actual bug. My >>> expectation is in garbage collected languages, the %delobject function >>> should automatically be invoked when all references to an object are >>> removed and the garbage collector kicks in. >>> >>> >>> In my reduced test case, I have 2 C functions that load sound handles >>> (malloc) and a function that deletes them. In my .i file, I declare >>> them like so: >>> >>> %newobject ALmixer_LoadAll; >>> %newobject ALmixer_LoadStream; >>> %delobject ALmixer_FreeData; >>> extern ALmixer_Data* ALmixer_LoadAll(const char* file_name); >>> extern ALmixer_Data* ALmixer_LoadStream(const char* file_name); >>> extern void ALmixer_FreeData(ALmixer_Data* almixer_data); >>> >>> I started with the JavaScriptCore generator but never saw anything get >>> collected. So with the Lua generator, I repeated the same test and >>> know I can force garbage collection by calling >>> collectgarbage("collect"). >>> >>> Basically my test script is: >>> sound = ALmixer.LoadAll("test.wav") >>> sound = nil >>> collectgarbage("collect") >>> >>> I expect my ALmixer.FreeData function to be called when the object is >>> collected. My FreeData function prints a message to let me know if it >>> was called. It is not called. >>> >> I don't know about the Javascript module as it isn't in the mainline >> SWIG distribution, however, this works in languages like Python. I think >> that given the Javascript module is not fully ready for release, you're >> best to compare behaviour with a known working language module to >> determine if the problem is in your SWIG directives or in the >> implementation of the language module. Did you check to see if the >> generated code changes with or without the %newobject/%delobject? It >> should. >> >> The implementation for each language module is slightly different, but a >> call to >> >> GetFlag(n, "feature:new") >> >> would indicate it has been implemented in the Source/Modules/xxx.cxx >> file, where xxx is the language module implementation file, something >> like javascript.cxx. >> >> William >> > > So based on some private replies I got, I now believe I misunderstood > what %delobject actually does. %delobject marks the SWIG proxy object > so if you manually free it using exposed API functions, and the object > later gets garbage collected by the system, SWIG will avoid double > freeing it. > > But returning to the question, "Why isn't SWIG invoking my free > function when the object is collected?", it seems that > %newobject/%delobject do not actually set a mapping between the Create > and Free functions. All they seem to do is inform SWIG about the > current reference count of objects to avoid the double-free problem > mentioned above. > > > So I didn't try Python, but I did try Lua (because I understand the > Lua-C API a bit better and can analyze that generated code output and > there is an explicit function I can call in Lua to force garbage > collection immediately for easy testing/debugging). My test code in > Lua showed this exact same problem as I described with the > JavaScriptCore generator. > > Using %newobject/%delobject does in fact generate slightly different > code in the Lua generator so it is in fact implemented. > > So as a newcomer, this seems very odd/strange/deficient that SWIG > would have all this infrastructure for memory management and yet > doesn't clean up after itself. So I looked at the generated code in a > little more detail. > > In Lua, you have the ability to register a metamethod called "__gc" > which lets you define a C function to be invoked to handle any clean > up. If you set this up, when your object gets collected, that callback > fires. Typically, this is where you would call your Free function for > the stuff that the SWIG proxy is wrapping. > > SWIG does in fact seem to have code for __gc. (The code is a little > complicated and there is some indirection/nesting in the metatable > which confuses me so I don't fully understand how it works yet.) But > for my case, this code is not reached for my case so my objects are > leaking. I think what is going on is that SWIG only sets this code up > if the objects being wrapped are C++ classes with destructors that can > be invoked. In my case, I am dealing with a plain old C struct, not a > C++ class so there is no destructor. So SWIG is leaking my object and > I don't seem to have a way to tell SWIG explicitly that the > "destructor" in this case is my ALmixer_Free function. I thought > %delobject would set up this mapping, but it doesn't seem to be the > case (unless this is different for other language generators, but > neither Lua nor JSCore worked so far). > Ah right I see, yes, %delobject isn't right for what you want. What you want are constructor and destructor type functionality for C structs. This is covered in the Basics chapter: file:///home/william/swig/github/swig/Doc/Manual/SWIG.html#SWIG_adding_member_functions So for example you use %extend like this to add in a 'destructor' (I havn't put in a constructor wrapper for you, assuming you want your users to just call ALmixer_LoadAll, but you can design this however you want): %module example %newobject ALmixer_LoadAll; %extend ALmixer_Data { ~ALmixer_Data() { ALmixer_FreeData($self); } }; %inline %{ struct ALmixer_Data { int x; }; typedef struct ALmixer_Data ALmixer_Data; ALmixer_Data* ALmixer_LoadAll(const char* file_name) { printf("ALmixer_LoadAll\n"); return (ALmixer_Data *)malloc(sizeof(ALmixer_Data)); } void ALmixer_FreeData(ALmixer_Data* almixer_data) { printf("ALmixer_FreeData\n"); free(almixer_data); } %} The following lua script: sound = example.ALmixer_LoadAll("test.wav") sound = nil collectgarbage() print("Finished") when run then shows it working as you want: ALmixer_LoadAll ALmixer_FreeData Finished If you omit %newobject, then the ALmixer_FreeData is not called. > > Anyway, I feel like this is the "hello world" case of memory > management. Dealing with Create/Free functions for C is the most > basic/fundamental case of memory management. SWIG seems to have a lot > of advanced handling for memory management for esoteric cases. It > seems odd that this one isn't handled. I'm hoping that maybe there is > a typemap I don't know about. Or perhaps at worst, both the JSCore and > Lua generator have a bug and this should in fact automatically be > automatically handled by %newobject/%delobject. > > Does any of this make sense/ring a bell? Is there an actual solution > for this that already exists? Maybe the docs are not clear enough, but this is the way it is meant to work. Patches to the docs to improvement are welcome. William |