Add typemap examples to handle memory management and early garbage collection

git-svn-id: https://swig.svn.sourceforge.net/svnroot/swig/trunk@7801 626c5289-ae23-0410-ae9c-e8d60b6d4f22
This commit is contained in:
William S Fulton 2005-11-04 23:52:02 +00:00
commit 5b6983d2e1
2 changed files with 508 additions and 1 deletions

View file

@ -18,6 +18,11 @@
<li><a href="#csharp_exception_example_exception_specifications">C# exception example using exception specifications</a> <li><a href="#csharp_exception_example_exception_specifications">C# exception example using exception specifications</a>
<li><a href="#csharp_custom_application_exception">Custom C# ApplicationException example</a> <li><a href="#csharp_custom_application_exception">Custom C# ApplicationException example</a>
</ul> </ul>
<li><a href="#csharp_typemap_examples">C# Typemap examples</a>
<ul>
<li><a href="#csharp_memory_management_member_variables">Memory management when returning references to member variables</a>
<li><a href="#csharp_memory_management_objects">Memory management for objects passed to the C++ layer</a>
</ul>
</ul> </ul>
</div> </div>
<!-- INDEX --> <!-- INDEX -->
@ -863,6 +868,257 @@ try {
</pre> </pre>
</div> </div>
<H2><a name="csharp_typemap_examples"></a>16.4 C# Typemap examples</H2>
This section includes a few examples of typemaps. For more examples, you
might look at the files "<tt>csharp.swg</tt>" and "<tt>typemaps.i</tt>" in
the SWIG library.
<H3><a name="csharp_memory_management_member_variables"></a>16.4.1 Memory management when returning references to member variables</H3>
<p>
This example shows how to prevent early garbage collection of objects when the underlying C++ class returns a pointer or reference to a member variable.
The example is a direct equivalent to this <a href="Java.html#java_memory_management_objects">Java equivalent</a>.
</p>
<p>
Consider the following C++ code:
</p>
<div class="code">
<pre>
struct Wheel {
int size;
Wheel(int sz) : size(sz) {}
};
class Bike {
Wheel wheel;
public:
Bike(int val) : wheel(val) {}
Wheel&amp; getWheel() { return wheel; }
};
</pre>
</div>
<p>
and the following usage from C# after running the code through SWIG:
</p>
<div class="code">
<pre>
Wheel wheel = new Bike(10).getWheel();
Console.WriteLine("wheel size: " + wheel.size);
// Simulate a garbage collection
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
Console.WriteLine("wheel size: " + wheel.size);
</pre>
</div>
<p>
Don't be surprised that if the resulting output gives strange results such as...
</p>
<div class="shell">
<pre>
wheel size: 10
wheel size: 135019664
</pre>
</div>
<p>
What has happened here is the garbage collector has collected the <tt>Bike</tt> instance as it doesn't think it is needed any more.
The proxy instance, <tt>wheel</tt>, contains a reference to memory that was deleted when the <tt>Bike</tt> instance was collected.
In order to prevent the garbage collector from collecting the <tt>Bike</tt> instance a reference to the <tt>Bike</tt> must
be added to the <tt>wheel</tt> instance. You can do this by adding the reference when the <tt>getWheel()</tt> method
is called using the following typemaps.
</p>
<div class="code">
<pre>
%typemap(cscode) Wheel %{
// Ensure that the GC doesn't collect any Bike instance set from C#
private Bike bikeReference;
internal void addReference(Bike bike) {
bikeReference = bike;
}
%}
// Add a C# reference to prevent early garbage collection and resulting use
// of dangling C++ pointer. Intended for methods that return pointers or
// references to a member variable.
%typemap(csout, excode=SWIGEXCODE) Wheel&amp; getWheel {
IntPtr cPtr = $imcall;$excode
$csclassname ret = null;
if (cPtr != IntPtr.Zero) {
ret = new $csclassname(cPtr, $owner);
ret.addReference(this);
}
return ret;
}
</pre>
</div>
<p>
The code in the first typemap gets added to the <tt>Wheel</tt> proxy class.
The code in the second typemap constitutes the bulk of the code in the generated <tt>getWheel()</tt> function:
</p>
<div class="code">
<pre>
public class Wheel : IDisposable {
...
// Ensure that the GC doesn't collect any Bike instance set from C#
private Bike bikeReference;
internal void addReference(Bike bike) {
bikeReference = bike;
}
}
public class Bike : IDisposable {
...
public Wheel getWheel() {
IntPtr cPtr = examplePINVOKE.Bike_getWheel(swigCPtr);
Wheel ret = null;
if (cPtr != IntPtr.Zero) {
ret = new Wheel(cPtr, false);
ret.addReference(this);
}
return ret;
}
}
</pre>
</div>
<p>
Note the <tt>addReference</tt> call.
</p>
<H3><a name="csharp_memory_management_objects"></a>16.4.2 Memory management for objects passed to the C++ layer</H3>
<p>
The example is a direct equivalent to this <a href="Java.html#java_memory_management_objects">Java equivalent</a>.
Managing memory can be tricky when using C++ and C# proxy classes.
The previous example shows one such case and this example looks at memory management for a class passed to a C++ method which expects the object to remain in scope
after the function has returned. Consider the following two C++ classes:
</p>
<div class="code">
<pre>
struct Element {
int value;
Element(int val) : value(val) {}
};
class Container {
Element* element;
public:
Container() : element(0) {}
void setElement(Element* e) { element = e; }
Element* getElement() { return element; }
};
</pre>
</div>
<p>
and usage from C++
</p>
<div class="code">
<pre>
Container container;
Element element(20);
container.setElement(&amp;element);
cout &lt;&lt; "element.value: " &lt;&lt; container.getElement()->value &lt;&lt; endl;
</pre>
</div>
<p>
and more or less equivalent usage from C#
</p>
<div class="code">
<pre>
Container container = new Container();
Element element = new Element(20);
container.setElement(element);
</pre>
</div>
<p>
The C++ code will always print out 20, but the value printed out may not be this in the C# equivalent code.
In order to understand why, consider a garbage collection occuring...
</p>
<div class="code">
<pre>
Container container = new Container();
Element element = new Element(20);
container.setElement(element);
Console.WriteLine("element.value: " + container.getElement().value);
// Simulate a garbage collection
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
Console.WriteLine("element.value: " + container.getElement().value);
</pre>
</div>
<p>
The temporary element created with <tt>new Element(20)</tt> could get garbage collected
which ultimately means the <tt>container</tt> variable is holding a dangling pointer, thereby printing out any old random value instead of the expected value of 20.
One solution is to add in the appropriate references in the C# layer...
</p>
<div class="code">
<pre>
public class Container : IDisposable {
...
// Ensure that the GC doesn't collect any Element set from C#
// as the underlying C++ class stores a shallow copy
private Element elementReference;
private HandleRef getCPtrAndAddReference(Element element) {
elementReference = element;
return Element.getCPtr(element);
}
public void setElement(Element e) {
examplePINVOKE.Container_setElement(swigCPtr, getCPtrAndAddReference(e));
}
}
</pre>
</div>
<p>
The following typemaps will generate the desired code.
The 'csin' typemap matches the input parameter type for the <tt>setElement</tt> method.
The 'cscode' typemap simply adds in the specified code into the C# proxy class.
</p>
<div class="code">
<pre>
%typemap(csin) Element *e "getCPtrAndAddReference($csinput)"
%typemap(cscode) Container %{
// Ensure that the GC doesn't collect any Element set from C#
// as the underlying C++ class stores a shallow copy
private Element elementReference;
private HandleRef getCPtrAndAddReference(Element element) {
elementReference = element;
return Element.getCPtr(element);
}
%}
</pre>
</div>
</body> </body>
</html> </html>

View file

@ -122,6 +122,8 @@
<li><a href="#adding_equals_method">Adding an equals method to the Java classes</a> <li><a href="#adding_equals_method">Adding an equals method to the Java classes</a>
<li><a href="#void_pointers">Void pointers and a common Java base class</a> <li><a href="#void_pointers">Void pointers and a common Java base class</a>
<li><a href="#struct_pointer_pointer">Struct pointer to pointer</a> <li><a href="#struct_pointer_pointer">Struct pointer to pointer</a>
<li><a href="#java_memory_management_member_variables">Memory management when returning references to member variables</a>
<li><a href="#java_memory_management_objects">Memory management for objects passed to the C++ layer</a>
</ul> </ul>
<li><a href="#java_directors_faq">Living with Java Directors</a> <li><a href="#java_directors_faq">Living with Java Directors</a>
<li><a href="#odds_ends">Odds and ends</a> <li><a href="#odds_ends">Odds and ends</a>
@ -2433,7 +2435,8 @@ you're lucky, you will only get a segmentation fault.
<p> <p>
To work around this, the ownership flag of <tt>o</tt> needs changing to <tt>false</tt>. To work around this, the ownership flag of <tt>o</tt> needs changing to <tt>false</tt>.
The ownership flag is a private member variable of the proxy class so this is not possible without some customization of the proxy class. The ownership flag is a private member variable of the proxy class so this is not possible without some customization of the proxy class.
This is achieved using a typemap to add pure Java code to the proxy class and is detailed later in the section on typemaps. This can be achieved by using a typemap to customise the proxy class with pure Java code as detailed later in the section on
<a href="#java_typemaps">Java typemaps</a>.
</p> </p>
<p> <p>
@ -2491,6 +2494,15 @@ Obj obj = Factory.createObj(); // obj.swigCMemOwn = true;
</pre> </pre>
</div> </div>
<p>
Some memory management issues are quite tricky to fix and may only be noticeable after using for a long time.
One such issue is early garbage collection of an object created from Java and resultant usage from C++ code.
The section on typemap examples cover two such scenarios,
<a href="#java_memory_management_objects">Memory management for objects passed to the C++ layer</a>
and
<a href="#java_memory_management_member_variables">Memory management when returning references to member variables</a>
</p>
<H4><a name="inheritance_mirroring"></a>19.4.3.2 Inheritance</H4> <H4><a name="inheritance_mirroring"></a>19.4.3.2 Inheritance</H4>
@ -6178,6 +6190,245 @@ The C functional interface has been completely morphed into an object-oriented i
the Butler class would behave much like any pure Java class and feel more natural to Java users. the Butler class would behave much like any pure Java class and feel more natural to Java users.
</p> </p>
<H3><a name="java_memory_management_member_variables"></a>19.9.11 Memory management when returning references to member variables</H3>
<p>
This example shows how to prevent early garbage collection of objects when the underlying C++ class returns a pointer or reference to a member variable.
</p>
<p>
Consider the following C++ code:
</p>
<div class="code">
<pre>
struct Wheel {
int size;
Wheel(int sz) : size(sz) {}
};
class Bike {
Wheel wheel;
public:
Bike(int val) : wheel(val) {}
Wheel&amp; getWheel() { return wheel; }
};
</pre>
</div>
<p>
and the following usage from Java after running the code through SWIG:
</p>
<div class="code">
<pre>
Wheel wheel = new Bike(10).getWheel();
System.out.println("wheel size: " + wheel.getSize());
// Simulate a garbage collection
System.gc();
System.runFinalization();
System.out.println("wheel size: " + wheel.getSize());
</pre>
</div>
<p>
Don't be surprised that if the resulting output gives strange results such as...
</p>
<div class="shell">
<pre>
wheel size: 10
wheel size: 135019664
</pre>
</div>
<p>
What has happened here is the garbage collector has collected the <tt>Bike</tt> instance as it doesn't think it is needed any more.
The proxy instance, <tt>wheel</tt>, contains a reference to memory that was deleted when the <tt>Bike</tt> instance was collected.
In order to prevent the garbage collector from collecting the <tt>Bike</tt> instance a reference to the <tt>Bike</tt> must
be added to the <tt>wheel</tt> instance. You can do this by adding the reference when the <tt>getWheel()</tt> method
is called using the following typemaps.
</p>
<div class="code">
<pre>
%typemap(javacode) Wheel %{
// Ensure that the GC doesn't collect any Bike instance set from Java
private Bike bikeReference;
protected void addReference(Bike bike) {
bikeReference = bike;
}
%}
// Add a Java reference to prevent early garbage collection and resulting use
// of dangling C++ pointer. Intended for methods that return pointers or
// references to a member variable.
%typemap(javaout) Wheel&amp; getWheel {
long cPtr = $jnicall;
$javaclassname ret = null;
if (cPtr != 0) {
ret = new $javaclassname(cPtr, $owner);
ret.addReference(this);
}
return ret;
}
</pre>
</div>
<p>
The code in the first typemap gets added to the <tt>Wheel</tt> proxy class.
The code in the second typemap constitutes the bulk of the code in the generated <tt>getWheel()</tt> function:
</p>
<div class="code">
<pre>
public class Wheel {
...
// Ensure that the GC doesn't collect any bike set from Java
private Bike bikeReference;
protected void addReference(Bike bike) {
bikeReference = bike;
}
}
public class Bike {
...
public Wheel getWheel() {
long cPtr = exampleJNI.Bike_getWheel(swigCPtr);
Wheel ret = null;
if (cPtr != 0) {
ret = new Wheel(cPtr, false);
ret.addReference(this);
}
return ret;
}
}
</pre>
</div>
<p>
Note the <tt>addReference</tt> call.
</p>
<H3><a name="java_memory_management_objects"></a>19.9.12 Memory management for objects passed to the C++ layer</H3>
<p>
Managing memory can be tricky when using C++ and Java proxy classes.
The previous example shows one such case and this example looks at memory management for a class passed to a C++ method which expects the object to remain in scope
after the function has returned. Consider the following two C++ classes:
</p>
<div class="code">
<pre>
struct Element {
int value;
Element(int val) : value(val) {}
};
class Container {
Element* element;
public:
Container() : element(0) {}
void setElement(Element* e) { element = e; }
Element* getElement() { return element; }
};
</pre>
</div>
<p>
and usage from C++
</p>
<div class="code">
<pre>
Container container;
Element element(20);
container.setElement(&amp;element);
cout &lt;&lt; "element.value: " &lt;&lt; container.getElement()->value &lt;&lt; endl;
</pre>
</div>
<p>
and more or less equivalent usage from Java
</p>
<div class="code">
<pre>
Container container = new Container();
container.setElement(new Element(20));
System.out.println("element value: " + container.getElement().getValue());
</pre>
</div>
<p>
The C++ code will always print out 20, but the value printed out may not be this in the Java equivalent code.
In order to understand why, consider a garbage collection occuring...
</p>
<div class="code">
<pre>
Container container = new Container();
container.setElement(new Element(20));
// Simulate a garbage collection
System.gc();
System.runFinalization();
System.out.println("element value: " + container.getElement().getValue());
</pre>
</div>
<p>
The temporary element created with <tt>new Element(20)</tt> could get garbage collected
which ultimately means the <tt>container</tt> variable is holding a dangling pointer, thereby printing out any old random value instead of the expected value of 20.
One solution is to add in the appropriate references in the Java layer...
</p>
<div class="code">
<pre>
public class Container {
...
// Ensure that the GC doesn't collect any Element set from Java
// as the underlying C++ class stores a shallow copy
private Element elementReference;
private long getCPtrAndAddReference(Element element) {
elementReference = element;
return Element.getCPtr(element);
}
public void setElement(Element e) {
exampleJNI.Container_setElement(swigCPtr, getCPtrAndAddReference(e));
}
}
</pre>
</div>
<p>
The following typemaps will generate the desired code.
The 'javain' typemap matches the input parameter type for the <tt>setElement</tt> method.
The 'javacode' typemap simply adds in the specified code into the Java proxy class.
</p>
<div class="code">
<pre>
%typemap(javain) Element *e "getCPtrAndAddReference($javainput)"
%typemap(javacode) Container %{
// Ensure that the GC doesn't collect any element set from Java
// as the underlying C++ class stores a shallow copy
private Element elementReference;
private long getCPtrAndAddReference(Element element) {
elementReference = element;
return Element.getCPtr(element);
}
%}
</pre>
</div>
<H2><a name="java_directors_faq"></a>19.10 Living with Java Directors</H2> <H2><a name="java_directors_faq"></a>19.10 Living with Java Directors</H2>