github.com/c-darwin/mobile@v0.0.0-20160313183840-ff625c46f7c9/bind/java/seq_android.go (about) 1 // Copyright 2014 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package java // import "github.com/c-darwin/mobile/bind/java" 6 7 //#cgo LDFLAGS: -llog 8 //#include <android/log.h> 9 //#include <jni.h> 10 //#include <stdint.h> 11 //#include <string.h> 12 //#include "seq_android.h" 13 import "C" 14 import ( 15 "fmt" 16 "sync" 17 "unsafe" 18 19 "github.com/c-darwin/mobile/bind/seq" 20 "github.com/c-darwin/mobile/internal/mobileinit" 21 ) 22 23 const maxSliceLen = 1<<31 - 1 24 25 const debug = false 26 27 // Send is called by Java to send a request to run a Go function. 28 //export Send 29 func Send(descriptor string, code int, req *C.uint8_t, reqlen C.size_t, res **C.uint8_t, reslen *C.size_t) { 30 fn := seq.Registry[descriptor][code] 31 if fn == nil { 32 panic(fmt.Sprintf("invalid descriptor(%s) and code(0x%x)", descriptor, code)) 33 } 34 in := new(seq.Buffer) 35 if reqlen > 0 { 36 in.Data = (*[maxSliceLen]byte)(unsafe.Pointer(req))[:reqlen] 37 } 38 out := new(seq.Buffer) 39 fn(out, in) 40 // BUG(hyangah): the function returning a go byte slice (so fn writes a pointer into 'out') is unsafe. 41 // After fn is complete here, Go runtime is free to collect or move the pointed byte slice 42 // contents. (Explicitly calling runtime.GC here will surface the problem?) 43 // Without pinning support from Go side, it will be hard to fix it without extra copying. 44 45 seqToBuf(res, reslen, out) 46 } 47 48 // DestroyRef is called by Java to inform Go it is done with a reference. 49 //export DestroyRef 50 func DestroyRef(refnum C.int32_t) { 51 seq.Delete(int32(refnum)) 52 } 53 54 type request struct { 55 ref *seq.Ref 56 handle int32 57 code int 58 in *seq.Buffer 59 } 60 61 var recv struct { 62 sync.Mutex 63 cond sync.Cond // signals req is not empty 64 req []request 65 next int32 // next handle value 66 } 67 68 var res struct { 69 sync.Mutex 70 cond sync.Cond // signals a response is filled in 71 out map[int32]*seq.Buffer // handle -> output 72 } 73 74 func init() { 75 recv.cond.L = &recv.Mutex 76 recv.next = 411 // arbitrary starting point distinct from Go and Java obj ref nums 77 78 res.cond.L = &res.Mutex 79 res.out = make(map[int32]*seq.Buffer) 80 } 81 82 func seqToBuf(bufptr **C.uint8_t, lenptr *C.size_t, buf *seq.Buffer) { 83 if debug { 84 fmt.Printf("seqToBuf tag 1, len(buf.Data)=%d, *lenptr=%d\n", len(buf.Data), *lenptr) 85 } 86 if len(buf.Data) == 0 { 87 *lenptr = 0 88 return 89 } 90 if len(buf.Data) > int(*lenptr) { 91 // TODO(crawshaw): realloc 92 C.free(unsafe.Pointer(*bufptr)) 93 m := C.malloc(C.size_t(len(buf.Data))) 94 if uintptr(m) == 0 { 95 panic(fmt.Sprintf("malloc failed, size=%d", len(buf.Data))) 96 } 97 *bufptr = (*C.uint8_t)(m) 98 *lenptr = C.size_t(len(buf.Data)) 99 } 100 C.memcpy(unsafe.Pointer(*bufptr), unsafe.Pointer(&buf.Data[0]), C.size_t(len(buf.Data))) 101 } 102 103 // Recv is called by Java in a loop and blocks until Go requests a callback 104 // be executed by the JVM. Then a request object is returned, along with a 105 // handle for the host to respond via RecvRes. 106 //export Recv 107 func Recv(in **C.uint8_t, inlen *C.size_t) (ref, code, handle C.int32_t) { 108 recv.Lock() 109 for len(recv.req) == 0 { 110 recv.cond.Wait() 111 } 112 req := recv.req[0] 113 recv.req = recv.req[1:] 114 seqToBuf(in, inlen, req.in) 115 recv.Unlock() 116 117 return C.int32_t(req.ref.Num), C.int32_t(req.code), C.int32_t(req.handle) 118 } 119 120 // RecvRes is called by JNI to return the result of a requested callback. 121 //export RecvRes 122 func RecvRes(handle C.int32_t, out *C.uint8_t, outlen C.size_t) { 123 outBuf := &seq.Buffer{ 124 Data: make([]byte, outlen), 125 } 126 copy(outBuf.Data, (*[maxSliceLen]byte)(unsafe.Pointer(out))[:outlen]) 127 128 res.Lock() 129 res.out[int32(handle)] = outBuf 130 res.Unlock() 131 res.cond.Broadcast() 132 } 133 134 // transact calls a method on a Java object instance. 135 // It blocks until the call is complete. 136 func transact(ref *seq.Ref, _ string, code int, in *seq.Buffer) *seq.Buffer { 137 recv.Lock() 138 if recv.next == 1<<31-1 { 139 panic("recv handle overflow") 140 } 141 handle := recv.next 142 recv.next++ 143 recv.req = append(recv.req, request{ 144 ref: ref, 145 code: code, 146 in: in, 147 handle: handle, 148 }) 149 recv.Unlock() 150 recv.cond.Signal() 151 152 res.Lock() 153 for res.out[handle] == nil { 154 res.cond.Wait() 155 } 156 out := res.out[handle] 157 delete(res.out, handle) 158 res.Unlock() 159 160 return out 161 } 162 163 func encodeString(out *seq.Buffer, v string) { 164 out.WriteUTF16(v) 165 } 166 167 func decodeString(in *seq.Buffer) string { 168 return in.ReadUTF16() 169 } 170 171 func init() { 172 seq.FinalizeRef = func(ref *seq.Ref) { 173 if ref.Num < 0 { 174 panic(fmt.Sprintf("not a Java ref: %d", ref.Num)) 175 } 176 transact(ref, "", -1, new(seq.Buffer)) 177 } 178 179 seq.Transact = transact 180 seq.EncString = encodeString 181 seq.DecString = decodeString 182 } 183 184 //export setContext 185 func setContext(vm *C.JavaVM, ctx C.jobject) { 186 mobileinit.SetCurrentContext(unsafe.Pointer(vm), unsafe.Pointer(ctx)) 187 }