Each side of the language barrier maintains a map of reference numbers to objects. Each entry has a reference count that exactly matches the number of active proxy objects on the other side. When a reference crosses the barrier, the count is incremented and when a proxy finalizer is run, the count is decremented. If the count reaches 0, the reference number and its object are removed from the map. There is a possibility that a reference number is passed to the other side, and the last proxy is then immediately garbage collected and finalized. The reference counter then reaches 0 before the other side has converted the reference number to its object, crashing the program. This is possible in both Go/Java/ObjC but is most likely to happen in ObjC because its own automatic reference count runtime frees objects as soon as they are statically never referenced again. Fix the race by always incrementing the reference count before sending a reference across the barrier. When converting the reference back into an object on the other side, decrement the counter again. Only the new ObjC test fails without this fix, but I left the Java counterpart in for good measure. Change-Id: I92743aabec275b4a5b82b952052e7e284872ce02 Reviewed-on: https://go-review.googlesource.com/21311 Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
51 lines
1.1 KiB
Plaintext
51 lines
1.1 KiB
Plaintext
// Copyright 2016 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package gomobile_bind
|
|
|
|
// Go support functions for generated Go bindings. This file is
|
|
// copied into the generated package, gomobile_bind, and compiled
|
|
// along with the bindings.
|
|
|
|
// #include <stdlib.h>
|
|
// #include "seq.h"
|
|
import "C"
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
|
|
_seq "golang.org/x/mobile/bind/seq"
|
|
)
|
|
|
|
func toError(s string) error {
|
|
if s == "" {
|
|
return nil
|
|
}
|
|
return errors.New(s)
|
|
}
|
|
|
|
func init() {
|
|
_seq.FinalizeRef = func(ref *_seq.Ref) {
|
|
refnum := ref.Bind_Num
|
|
if refnum < 0 {
|
|
panic(fmt.Sprintf("not a foreign ref: %d", refnum))
|
|
}
|
|
C.go_seq_dec_ref(C.int32_t(refnum))
|
|
}
|
|
_seq.IncForeignRef = func(refnum int32) {
|
|
if refnum < 0 {
|
|
panic(fmt.Sprintf("not a foreign ref: %d", refnum))
|
|
}
|
|
C.go_seq_inc_ref(C.int32_t(refnum))
|
|
}
|
|
}
|
|
|
|
// IncGoRef is called by foreign code to pin a Go object while its refnum is crossing
|
|
// the language barrier
|
|
//export IncGoRef
|
|
func IncGoRef(refnum C.int32_t) {
|
|
_seq.Inc(int32(refnum))
|
|
}
|