Skip to content

Commit

Permalink
workaround tightening of c-go rules
Browse files Browse the repository at this point in the history
In go1.6, cgo rules regarding the passing of Go pointers to C libraries
changed to prevent unsafe interactions with the Go garbage collector.

We need to change so that we pass an indirect reference to the Go object
to the c-runtime.

This code has been tested to the extent that required to address the
particular panic reported with #4

However, a more extensive regression test has not yet been performed.

Signed-off-by: Jon Seymour <[email protected]>
  • Loading branch information
Jon Seymour committed Mar 26, 2016
1 parent e77291c commit 03d83af
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 4 deletions.
1 change: 1 addition & 0 deletions api.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import "C"
// ask an EventLoop to quit.

type api struct {
shareable
loop EventLoop
callback NotificationCallback
eventCallback EventCallback
Expand Down
5 changes: 3 additions & 2 deletions api.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#ifndef API_H
#ifndef API_H
#define API_H
//
// api.h
Expand Down Expand Up @@ -39,14 +39,15 @@ extern "C" {
#else
#endif

#include "api/shareable.h"
typedef void API;

#include "api/manager.h"
#include "api/node.h"
#include "api/value.h"
#include "api/notification.h"
#include "api/options.h"


#ifdef __cplusplus
#include "_cgo_export.h"
}
Expand Down
5 changes: 5 additions & 0 deletions api/shareable.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
typedef struct shareable {
int sharedIndex;
} shareable;

shareable * newShareable(int i);
4 changes: 2 additions & 2 deletions run.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ func (a *api) Run() int {
//

go func() {
cSelf := unsafe.Pointer(a) // a reference to a
cSelf := marshal(&a.shareable)

C.startManager(cSelf) // start the manager
defer C.stopManager(cSelf)
Expand Down Expand Up @@ -219,7 +219,7 @@ func (a *api) Shutdown(exit int) {
//export onNotificationWrapper
func onNotificationWrapper(cNotification *C.Notification, context unsafe.Pointer) {
// marshal from C to Go
a := (*api)(context)
a := unmarshal(context).(*api)
goNotification := newGoNotification(cNotification)
if a.callback != nil {
a.callback(a, goNotification)
Expand Down
7 changes: 7 additions & 0 deletions shareable.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#include "api.h"

shareable * newShareable(int i) {
shareable * r = (shareable *) malloc(sizeof(shareable));
r->sharedIndex = i;
return r;
}
78 changes: 78 additions & 0 deletions shareable.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package openzwave

// #cgo LDFLAGS: -lopenzwave -Lgo/src/github.com/ninjasphere/go-openzwave/openzwave
// #cgo CPPFLAGS: -Iopenzwave/cpp/src/platform -Iopenzwave/cpp/src -Iopenzwave/cpp/src/value_classes
// #include "api.h"
import "C"

import (
"fmt"
"unsafe"
)

// This module works around an issue revealed when Go 1.6 tightened up the rules
// about sharing of Go pointers with C code. https://github.com/golang/go/issues/12416
//
// Now we register a reference with a map and get an integer in return. We use
// this integer to map back into the Go world.

type Shareable interface {
GetSharingIndex() int
SetSharingIndex(int)
GetShareable() unsafe.Pointer
}

type shareable struct {
cref *C.shareable
}

func (s *shareable) GetSharingIndex() int {
if s.cref == nil {
return 0
} else {
return int(s.cref.sharedIndex)
}
}

func (s *shareable) SetSharingIndex(i int) {
if i == 0 {
C.free(s.cref)
s.cref = nil
} else if s.cref == nil {
s.cref = C.newShareable(C.int(i))
}
s.cref.sharedIndex = C.int(i)
}

func (s *shareable) GetShareable() unsafe.Pointer {
return unsafe.Pointer(s.cref)
}

var shared = map[int]Shareable{}
var sharedCount = 0

func marshal(p Shareable) unsafe.Pointer {
if p.GetSharingIndex() == 0 {
sharedCount++
p.SetSharingIndex(sharedCount)
shared[sharedCount] = p
}
return p.GetShareable()
}

func unmarshal(c unsafe.Pointer) Shareable {
i := (*C.shareable)(c).sharedIndex
if i == 0 {
return nil
} else {
if s, ok := shared[int(i)]; !ok {
panic(fmt.Errorf("failure to unmarshal index %d", int(i)))
} else {
return s
}
}
}

func release(i int) {
delete(shared, i)
}

0 comments on commit 03d83af

Please sign in to comment.