Add typemap examples to handle memory management and early garbage collection

git-svn-id: https://swig.svn.sourceforge.net/svnroot/swig/trunk/SWIG@7801 626c5289-ae23-0410-ae9c-e8d60b6d4f22
This commit is contained in:
William S Fulton 2005-11-04 23:52:02 +00:00
commit 455dbf6a6d
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_custom_application_exception">Custom C# ApplicationException example</a>
</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>
</div>
<!-- INDEX -->
@ -863,6 +868,257 @@ try {
</pre>
</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>
</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="#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="#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>
<li><a href="#java_directors_faq">Living with Java Directors</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>
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.
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>
@ -2491,6 +2494,15 @@ Obj obj = Factory.createObj(); // obj.swigCMemOwn = true;
</pre>
</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>
@ -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.
</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>