From 223a3b298ca7b9dbf0b06ab6c50c66320f9dfa60 Mon Sep 17 00:00:00 2001
From: Hraban Luyat <hraban@0brg.net>
Date: Mon, 22 Aug 2016 15:11:13 +0100
Subject: [PATCH] Move callbacks struct to C allocator, to be safe

I'm not sure if the Go pointer proposal has already been implemented,
but under the proposed restrictions allocating the callback struct in Go
and passing that pointer to a C function which keeps it after returning
is not allowed.

This patch allocates the struct in C and passes the pointer from Go to
C, which is fine.
---
 callbacks.c | 17 +++++++++++++++++
 stream.go   | 12 ++----------
 2 files changed, 19 insertions(+), 10 deletions(-)
 create mode 100644 callbacks.c

diff --git a/callbacks.c b/callbacks.c
new file mode 100644
index 0000000..9148557
--- /dev/null
+++ b/callbacks.c
@@ -0,0 +1,17 @@
+// Copyright © 2015, 2016 Hraban Luyat <hraban@0brg.net>
+//
+// License for use of this code is detailed in the LICENSE file
+
+// Allocate callback struct in C to ensure it's not managed by the Go GC. This
+// plays nice with the CGo rules and avoids any confusion.
+
+#include <opusfile.h>
+
+// Defined in Go. Uses the same signature as Go, no need for proxy function.
+int go_readcallback(void *p, unsigned char *buf, int nbytes);
+
+// Allocated once, never moved. Pointer to this is safe for passing around
+// between Go and C.
+struct OpusFileCallbacks callbacks = {
+    .read = go_readcallback,
+};
diff --git a/stream.go b/stream.go
index b087c33..bc1b8a6 100644
--- a/stream.go
+++ b/stream.go
@@ -15,8 +15,7 @@ import (
 #include <opusfile.h>
 #include <string.h>
 
-// Uses the same signature as Go, no need for proxy
-int go_readcallback(void *p, unsigned char *buf, int nbytes);
+extern struct OpusFileCallbacks callbacks;
 
 */
 import "C"
@@ -61,13 +60,6 @@ func go_readcallback(p unsafe.Pointer, cbuf *C.uchar, cmaxbytes C.int) C.int {
 	return C.int(n)
 }
 
-var callbacks = C.struct_OpusFileCallbacks{
-	read:  C.op_read_func(C.go_readcallback),
-	seek:  nil,
-	tell:  nil,
-	close: nil,
-}
-
 func NewStream(read io.Reader) (*Stream, error) {
 	var s Stream
 	err := s.Init(read)
@@ -104,7 +96,7 @@ func (s *Stream) Init(read io.Reader) error {
 	oggfile := C.op_open_callbacks(
 		// "C code may not keep a copy of a Go pointer after the call returns."
 		unsafe.Pointer(s.id),
-		&callbacks,
+		&C.callbacks,
 		nil,
 		0,
 		&errno)
-- 
2.51.0