summaryrefslogtreecommitdiff
path: root/Doc
diff options
context:
space:
mode:
authorMichael Schaller <michael@5challer.de>2015-05-08 14:32:22 +0200
committerMichael Schaller <michael@5challer.de>2015-05-08 14:32:22 +0200
commit255715d9ad38d9d1faf265f890712a99b540f9f8 (patch)
tree34abaacc0689ba2b7f6cf2a3f917ce2f816fdeb0 /Doc
parent9086eb351ce1d158d9b66d707d3fcb48e447b382 (diff)
downloadswig-255715d9ad38d9d1faf265f890712a99b540f9f8.tar.gz
[Go] Improved Go Class Memory Management section of the Go documentation.
Fixes #289.
Diffstat (limited to 'Doc')
-rw-r--r--Doc/Manual/Go.html121
1 files changed, 103 insertions, 18 deletions
diff --git a/Doc/Manual/Go.html b/Doc/Manual/Go.html
index 175f22f26..0a413b25a 100644
--- a/Doc/Manual/Go.html
+++ b/Doc/Manual/Go.html
@@ -436,34 +436,119 @@ for this by calling the Swigcptr() method.
<p>
-Calling <tt>NewClassName</tt> for some C++ class <tt>ClassName</tt>
-will allocate memory using the C++ memory allocator. This memory will
-not be automatically freed by Go's garbage collector as the object ownership is
-not tracked. When you are done with the C++ object you must free it manually
-using <tt>DeleteClassName</tt>.
+Calling <tt>NewClassName</tt> for a C++ class <tt>ClassName</tt> will allocate
+memory using the C++ memory allocator. This memory will not be automatically
+freed by Go's garbage collector as the object ownership is not tracked. When
+you are done with the C++ object you must free it using
+<tt>DeleteClassName</tt>.<br>
+<br>
+The most Go idiomatic way to manage the memory for some C++ class is to call
+<tt>NewClassName</tt> followed by a
+<tt><a href="https://golang.org/doc/effective_go.html#defer">defer</a></tt> of
+the <tt>DeleteClassName</tt> call. Using <tt>defer</tt> ensures that the memory
+of the C++ object is freed as soon as the function containing the <tt>defer</tt>
+statement returns. Furthemore <tt>defer</tt> works great for short-lived
+objects and fits nicely C++'s RAII idiom. Example:
</p>
+<div class="code">
+<pre>
+func UseClassName(...) ... {
+ o := NewClassName(...)
+ defer DeleteClassName(o)
+ // Use the ClassName object
+ return ...
+}
+</pre>
+</div>
+
+<p>
+With increasing complexity, especially complex C++ object hierarchies, the
+correct placement of <tt>defer</tt> statements becomes harder and harder as C++
+objects need to be freed in the correct order. This problem can be eased by
+keeping a C++ object function local so that it is only available to the function
+that creates a C++ object and functions called by this function. Example:
+</p>
+<div class="code">
+<pre>
+func WithClassName(constructor args, f func(ClassName, ...interface{}) error, data ...interface{}) error {
+ o := NewClassName(constructor args)
+ defer DeleteClassName(o)
+ return f(o, data...)
+}
+
+func UseClassName(o ClassName, data ...interface{}) (err error) {
+ // Use the ClassName object and additional data and return error.
+}
+
+func main() {
+ WithClassName(constructor args, UseClassName, additional data)
+}
+</pre>
+</div>
<p>
-A common technique is to store the C++ object into a Go object, and
-use the Go function <tt>runtime.SetFinalizer</tt> to free the C++ object when
-the Go object is freed. It is strongly recommended to read the
-<a href="https://golang.org/pkg/runtime/#SetFinalizer">runtime.SetFinalizer</a>
-documentation before using this technique to understand its limitations.
-For example, if the SWIG package is imported as "wrap":
+Using <tt>defer</tt> has limitations though, especially when it comes to
+long-lived C++ objects whichs lifetimes are hard to predict. For such C++
+objects a common technique is to store the C++ object into a Go object, and to
+use the Go function <tt>runtime.SetFinalizer</tt> to add a finalizer which frees
+the C++ object when the Go object is freed. It is strongly recommended to read
+the <a href="https://golang.org/pkg/runtime/#SetFinalizer">runtime.SetFinalizer
+</a> documentation before using this technique to understand the
+<tt>runtime.SetFinalizer</tt> limitations.<br>
+<br>
+Common pitfalls with <tt>runtime.SetFinalizer</tt> are:
+<ul>
+<li>
+If a hierarchy of C++ objects will be automatically freed by Go finalizers then
+the Go objects that store the C++ objects need to replicate the hierarchy of the
+C++ objects to prevent that C++ objects are freed prematurely while other C++
+objects still rely on them.
+</li>
+<li>
+The usage of Go finalizers is problematic with C++'s RAII idiom as it isn't
+predictable when the finalizer will run and this might require a Close or Delete
+method to be added the Go object that stores a C++ object to mitigate.
+</li>
+<li>
+The Go finalizer function typically runs in a different OS thread which can be
+problematic with C++ code that uses thread-local storage.
+</li>
+</ul>
+</p>
+
+<p>
+<tt>runtime.SetFinalizer</tt> Example:
</p>
<div class="code">
<pre>
+import (
+ "runtime"
+ "wrap" // SWIG generated wrapper code
+)
+
type GoClassName struct {
- w wrap.ClassName
+ wcn wrap.ClassName
}
func NewGoClassName() *GoClassName {
- r := &amp;GoClassName{wrap.NewClassName()}
- runtime.SetFinalizer(r,
- func(r *GoClassName) {
- wrap.DeleteClassName(r.w)
- })
- return r
+ o := &amp;GoClassName{wcn: wrap.NewClassName()}
+ runtime.SetFinalizer(o, deleteGoClassName)
+ return o
+}
+
+func deleteGoClassName(o *GoClassName) {
+ // Runs typically in a different OS thread!
+ wrap.DeleteClassName(o.wcn)
+ o.wcn = nil
+}
+
+func (o *GoClassName) Close() {
+ // If the C++ object has a Close method.
+ o.wcn.Close()
+
+ // If the GoClassName object is no longer in an usable state.
+ runtime.SetFinalizer(o, nil) // Remove finalizer.
+ deleteGoClassName() // Free the C++ object.
}
</pre>
</div>