diff options
author | Asim Shankar <ashankar@google.com> | 2016-09-23 09:10:07 -0800 |
---|---|---|
committer | TensorFlower Gardener <gardener@tensorflow.org> | 2016-09-23 10:18:23 -0700 |
commit | bb2aa864b335d18d8468d66cb3dd67203484e2d3 (patch) | |
tree | 3ae8cfa40bb94860916cfecffceb69f435f0c6cc /tensorflow/go/operation.go | |
parent | 06324c7e094543da7a1726dc7724925e136b8453 (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.go')
-rw-r--r-- | tensorflow/go/operation.go | 20 |
1 files changed, 18 insertions, 2 deletions
diff --git a/tensorflow/go/operation.go b/tensorflow/go/operation.go index 7d9a9655dc..0f14ea1bef 100644 --- a/tensorflow/go/operation.go +++ b/tensorflow/go/operation.go @@ -22,6 +22,19 @@ import "unsafe" // Operation that has been added to the graph. type Operation struct { c *C.TF_Operation + // A reference to the Graph to prevent it from + // being GCed while the Operation is still alive. + g *Graph +} + +// Name returns the name of the operation. +func (op *Operation) Name() string { + return C.GoString(C.TF_OperationName(op.c)) +} + +// Type returns the name of the operator used by this operation. +func (op *Operation) Type() string { + return C.GoString(C.TF_OperationOpType(op.c)) } // Output represents one of the outputs of an operation in the graph. Has a @@ -44,12 +57,15 @@ func (p *Output) c() C.TF_Port { // Build() must be called for any in-progress Operation, or else we leak. type opBuilder struct { c *C.TF_OperationDescription + // A reference to the Graph to prevent it from being GCed while + // the opBuilder is still alive. + g *Graph } func newOpBuilder(g *Graph, typ string, name string) *opBuilder { opType := C.CString(typ) opName := C.CString(name) - b := &opBuilder{c: C.TF_NewOperation(g.c, opType, opName)} + b := &opBuilder{c: C.TF_NewOperation(g.c, opType, opName), g: g} C.free(unsafe.Pointer(opType)) C.free(unsafe.Pointer(opName)) return b @@ -75,7 +91,7 @@ func (b *opBuilder) AddInput(port Output) { func (b *opBuilder) Build() (*Operation, error) { status := newStatus() - op := &Operation{c: C.TF_FinishOperation(b.c, status.c)} + op := &Operation{c: C.TF_FinishOperation(b.c, status.c), g: b.g} if err := status.Err(); err != nil { return nil, err } |