aboutsummaryrefslogtreecommitdiffhomepage
path: root/tensorflow/go/operation_test.go
diff options
context:
space:
mode:
authorGravatar Asim Shankar <ashankar@google.com>2016-09-23 09:10:07 -0800
committerGravatar TensorFlower Gardener <gardener@tensorflow.org>2016-09-23 10:18:23 -0700
commitbb2aa864b335d18d8468d66cb3dd67203484e2d3 (patch)
tree3ae8cfa40bb94860916cfecffceb69f435f0c6cc /tensorflow/go/operation_test.go
parent06324c7e094543da7a1726dc7724925e136b8453 (diff)
go: Prevent a Graph from being GCed while an Operation is alive.
And also add trivial accessors for Name() and Type() to the Operation type. The lifetime of TF_Operation* objects is tied to the lifetime of the TF_Graph* that they come from in the C API. Prior to this commit, it was possible for the Go Graph object to be garbage collected even while the Operation object is still alive. The added test demonstrates this problem and fails with the following if the Operation type doesn't hold on to a reference to the Graph: === RUN TestOperationLifetime fatal error: unexpected signal during runtime execution [signal SIGSEGV: segmentation violation code=0x80 addr=0x0 pc=0x886ebc4] runtime stack: runtime.throw(0xc73afc7, 0x2a) go/gc/src/runtime/panic.go:569 +0x9e runtime.sigpanic() go/gc/src/runtime/sigpanic_unix.go:12 +0x2cc goroutine 34 [syscall, locked to thread]: runtime.cgocall(0x7733410, 0xc420055e28, 0x0) go/gc/src/runtime/cgocall.go:131 +0x110 fp=0xc420055df8 sp=0xc420055db8 pc=0x75e64a0 tensorflow/go/tensorflow._Cfunc_TF_OperationName(0x7f4c9f59b790, 0x0) ??:0 +0x4a fp=0xc420055e28 sp=0xc420055df8 pc=0x764884a tensorflow/go/tensorflow.(*Operation).Name(0xc4201760c0, 0x0, 0x0) /build/work/c9dcfe7aa482d82c72c0b2b6c0351d7a/tensorflow/go/operation.go:32 +0x78 fp=0xc420055e70 sp=0xc420055e28 pc=0x7649248 tensorflow/go/tensorflow.TestOperationLifetime(0xc4201a00c0) tensorflow/go/operation_test.go:45 +0x7e fp=0xc420055f68 sp=0xc420055e70 pc=0x764409e testing.tRunner(0xc4201a00c0, 0xc748ed0) go/gc/src/testing/testing.go:610 +0x81 fp=0xc420055f90 sp=0xc420055f68 pc=0x76589a1 runtime.goexit() go/gc/src/runtime/asm_amd64.s:2086 +0x1 fp=0xc420055f98 sp=0xc420055f90 pc=0x7642c91 created by testing.(*T).Run go/gc/src/testing/testing.go:646 +0x2ec This is yet another step towards #10 Change: 134086736
Diffstat (limited to 'tensorflow/go/operation_test.go')
-rw-r--r--tensorflow/go/operation_test.go68
1 files changed, 68 insertions, 0 deletions
diff --git a/tensorflow/go/operation_test.go b/tensorflow/go/operation_test.go
new file mode 100644
index 0000000000..7cba0a5c27
--- /dev/null
+++ b/tensorflow/go/operation_test.go
@@ -0,0 +1,68 @@
+// Copyright 2016 The TensorFlow Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package tensorflow
+
+import (
+ "runtime"
+ "runtime/debug"
+ "testing"
+)
+
+// createGraphAndOp creates an Operation but loses the reference to the Graph.
+func createGraphAndOp() (*Operation, error) {
+ t, err := NewTensor(int64(1))
+ if err != nil {
+ return nil, err
+ }
+ g := NewGraph()
+ output, err := Placeholder(g, "my_placeholder", t.DataType())
+ if err != nil {
+ return nil, err
+ }
+ return output.Op, nil
+}
+
+func TestOperationLifetime(t *testing.T) {
+ // Ensure that the Graph is not garbage collected while the program
+ // still has access to the Operation.
+ op, err := createGraphAndOp()
+ if err != nil {
+ t.Fatal(err)
+ }
+ forceGC()
+ if got, want := op.Name(), "my_placeholder"; got != want {
+ t.Errorf("Got '%s', want '%s'", got, want)
+ }
+ if got, want := op.Type(), "Placeholder"; got != want {
+ t.Errorf("Got '%s', want '%s'", got, want)
+ }
+}
+
+func forceGC() {
+ var mem runtime.MemStats
+ runtime.ReadMemStats(&mem)
+ // It was empirically observed that without this extra allocation
+ // TestOperationLifetime would fail only 50% of the time if
+ // Operation did not hold on to a reference to Graph. With this
+ // additional allocation, and with the bug where Operation does
+ // not hold onto a Graph, the test failed 90+% of the time.
+ //
+ // The author is aware that this technique is potentially fragile
+ // and fishy. Suggestions for alternatives are welcome.
+ bytesTillGC := mem.NextGC - mem.HeapAlloc + 1
+ _ = make([]byte, bytesTillGC)
+ runtime.GC()
+ debug.FreeOSMemory()
+}