// Copyright 2014 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 java // import "golang.org/x/mobile/bind/java" //#cgo LDFLAGS: -llog //#include //#include //#include //#include "seq_android.h" import "C" import ( "fmt" "sync" "unsafe" "golang.org/x/mobile/bind/seq" ) const maxSliceLen = 1<<31 - 1 const debug = false // Send is called by Java to send a request to run a Go function. //export Send func Send(descriptor string, code int, req *C.uint8_t, reqlen C.size_t, res **C.uint8_t, reslen *C.size_t) { <-running fn := seq.Registry[descriptor][code] if fn == nil { panic(fmt.Sprintf("invalid descriptor(%s) and code(0x%x)", descriptor, code)) } in := new(seq.Buffer) if reqlen > 0 { in.Data = (*[maxSliceLen]byte)(unsafe.Pointer(req))[:reqlen] } out := new(seq.Buffer) fn(out, in) // BUG(hyangah): the function returning a go byte slice (so fn writes a pointer into 'out') is unsafe. // After fn is complete here, Go runtime is free to collect or move the pointed byte slice // contents. (Explicitly calling runtime.GC here will surface the problem?) // Without pinning support from Go side, it will be hard to fix it without extra copying. seqToBuf(res, reslen, out) } // DestroyRef is called by Java to inform Go it is done with a reference. //export DestroyRef func DestroyRef(refnum C.int32_t) { seq.Delete(int32(refnum)) } type request struct { ref *seq.Ref handle int32 code int in *seq.Buffer } var recv struct { sync.Mutex cond sync.Cond // signals req is not empty req []request next int32 // next handle value } var res struct { sync.Mutex cond sync.Cond // signals a response is filled in out map[int32]*seq.Buffer // handle -> output } func init() { recv.cond.L = &recv.Mutex recv.next = 411 // arbitrary starting point distinct from Go and Java obj ref nums res.cond.L = &res.Mutex res.out = make(map[int32]*seq.Buffer) } func seqToBuf(bufptr **C.uint8_t, lenptr *C.size_t, buf *seq.Buffer) { if debug { fmt.Printf("seqToBuf tag 1, len(buf.Data)=%d, *lenptr=%d\n", len(buf.Data), *lenptr) } if len(buf.Data) == 0 { *lenptr = 0 return } if len(buf.Data) > int(*lenptr) { // TODO(crawshaw): realloc C.free(unsafe.Pointer(*bufptr)) m := C.malloc(C.size_t(len(buf.Data))) if uintptr(m) == 0 { panic(fmt.Sprintf("malloc failed, size=%d", len(buf.Data))) } *bufptr = (*C.uint8_t)(m) *lenptr = C.size_t(len(buf.Data)) } C.memcpy(unsafe.Pointer(*bufptr), unsafe.Pointer(&buf.Data[0]), C.size_t(len(buf.Data))) } // Recv is called by Java in a loop and blocks until Go requests a callback // be executed by the JVM. Then a request object is returned, along with a // handle for the host to respond via RecvRes. //export Recv func Recv(in **C.uint8_t, inlen *C.size_t) (ref, code, handle C.int32_t) { recv.Lock() for len(recv.req) == 0 { recv.cond.Wait() } req := recv.req[0] recv.req = recv.req[1:] seqToBuf(in, inlen, req.in) recv.Unlock() return C.int32_t(req.ref.Num), C.int32_t(req.code), C.int32_t(req.handle) } // RecvRes is called by JNI to return the result of a requested callback. //export RecvRes func RecvRes(handle C.int32_t, out *C.uint8_t, outlen C.size_t) { outBuf := &seq.Buffer{ Data: make([]byte, outlen), } copy(outBuf.Data, (*[maxSliceLen]byte)(unsafe.Pointer(out))[:outlen]) res.Lock() res.out[int32(handle)] = outBuf res.Unlock() res.cond.Broadcast() } // transact calls a method on a Java object instance. // It blocks until the call is complete. func transact(ref *seq.Ref, code int, in *seq.Buffer) *seq.Buffer { recv.Lock() if recv.next == 1<<31-1 { panic("recv handle overflow") } handle := recv.next recv.next++ recv.req = append(recv.req, request{ ref: ref, code: code, in: in, handle: handle, }) recv.Unlock() recv.cond.Signal() res.Lock() for res.out[handle] == nil { res.cond.Wait() } out := res.out[handle] delete(res.out, handle) res.Unlock() return out } func encodeString(out *seq.Buffer, v string) { out.WriteUTF16(v) } func decodeString(in *seq.Buffer) string { return in.ReadUTF16() } func init() { seq.FinalizeRef = func(ref *seq.Ref) { if ref.Num < 0 { panic(fmt.Sprintf("not a Java ref: %d", ref.Num)) } transact(ref, -1, new(seq.Buffer)) } seq.Transact = transact seq.EncString = encodeString seq.DecString = decodeString }