github.com/c-darwin/mobile@v0.0.0-20160313183840-ff625c46f7c9/bind/objc/seq_darwin.go (about) 1 // Copyright 2015 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 objc 6 7 /* 8 #cgo CFLAGS: -x objective-c -fobjc-arc 9 #cgo LDFLAGS: -framework Foundation 10 11 #include <stdint.h> 12 #include <stdlib.h> 13 #include <string.h> 14 15 void init_seq(); 16 void go_seq_recv(int32_t, const char*, int, uint8_t*, size_t, uint8_t**, size_t*); 17 */ 18 import "C" 19 20 import ( 21 "fmt" 22 "sync" 23 "unsafe" 24 25 "github.com/c-darwin/mobile/bind/seq" 26 ) 27 28 const debug = false 29 30 const maxSliceLen = 1<<31 - 1 31 32 // Send is called by Objective-C to send a request to run a Go function. 33 //export Send 34 func Send(descriptor string, code int, req *C.uint8_t, reqlen C.size_t, res **C.uint8_t, reslen *C.size_t) { 35 fn := seq.Registry[descriptor][code] 36 if fn == nil { 37 panic(fmt.Sprintf("invalid descriptor(%s) and code(0x%x)", descriptor, code)) 38 } 39 var in, out *seq.Buffer 40 if reqlen > 0 { 41 in = &seq.Buffer{Data: (*[maxSliceLen]byte)(unsafe.Pointer(req))[:reqlen]} 42 } 43 if reslen != nil { 44 out = new(seq.Buffer) 45 } 46 47 fn(out, in) 48 if out != nil { 49 // sender expects results. 50 seqToBuf(res, reslen, out) 51 } 52 } 53 54 // DestroyRef is called by Objective-C to inform Go it is done with a reference. 55 //export DestroyRef 56 func DestroyRef(refnum C.int32_t) { 57 seq.Delete(int32(refnum)) 58 } 59 60 type request struct { 61 ref *seq.Ref 62 handle int32 63 code int 64 in *seq.Buffer 65 } 66 67 var recv struct { 68 sync.Mutex 69 cond sync.Cond // signals req is not empty 70 req []request 71 next int32 // next handle value 72 } 73 74 var res struct { 75 sync.Mutex 76 cond sync.Cond // signals a response is filled in 77 out map[int32]*seq.Buffer // handle -> output 78 } 79 80 func init() { 81 recv.cond.L = &recv.Mutex 82 recv.next = 411 // arbitrary starting point distrinct from Go and Objective-C object ref nums. 83 res.cond.L = &res.Mutex 84 res.out = make(map[int32]*seq.Buffer) 85 } 86 87 func seqToBuf(bufptr **C.uint8_t, lenptr *C.size_t, buf *seq.Buffer) { 88 if debug { 89 fmt.Printf("seqToBuf tag 1, len(buf.Data)=%d, *lenptr=%d\n", len(buf.Data), *lenptr) 90 } 91 if len(buf.Data) == 0 { 92 *lenptr = 0 93 return 94 } 95 if len(buf.Data) > int(*lenptr) { 96 // TODO(crawshaw): realloc 97 C.free(unsafe.Pointer(*bufptr)) 98 m := C.malloc(C.size_t(len(buf.Data))) 99 if uintptr(m) == 0 { 100 panic(fmt.Sprintf("malloc failed, size=%d", len(buf.Data))) 101 } 102 *bufptr = (*C.uint8_t)(m) 103 *lenptr = C.size_t(len(buf.Data)) 104 } 105 C.memcpy(unsafe.Pointer(*bufptr), unsafe.Pointer(&buf.Data[0]), C.size_t(len(buf.Data))) 106 } 107 108 type cStringMap struct { 109 sync.Mutex 110 m map[string]*C.char 111 } 112 113 var cstrings = &cStringMap{ 114 m: make(map[string]*C.char), 115 } 116 117 func (s *cStringMap) get(k string) *C.char { 118 s.Lock() 119 c, ok := s.m[k] 120 if !ok { 121 c = C.CString(k) 122 s.m[k] = c 123 } 124 s.Unlock() 125 return c 126 } 127 128 // transact calls a method on an Objective-C object instance. 129 // It blocks until the call is complete. 130 // 131 // Code (>0) is the method id assigned by gobind. 132 // Code -1 is used to instruct Objective-C to decrement the ref count of 133 // the Objective-Co object. 134 func transact(ref *seq.Ref, descriptor string, code int, in *seq.Buffer) *seq.Buffer { 135 var ( 136 res *C.uint8_t = nil 137 resLen C.size_t = 0 138 req *C.uint8_t = nil 139 reqLen C.size_t = 0 140 ) 141 142 if len(in.Data) > 0 { 143 req = (*C.uint8_t)(unsafe.Pointer(&in.Data[0])) 144 reqLen = C.size_t(len(in.Data)) 145 } 146 147 if debug { 148 fmt.Printf("transact: ref.Num = %d code = %d\n", ref.Num, code) 149 } 150 151 desc := cstrings.get(descriptor) 152 C.go_seq_recv(C.int32_t(ref.Num), desc, C.int(code), req, reqLen, &res, &resLen) 153 154 if resLen > 0 { 155 goSlice := (*[maxSliceLen]byte)(unsafe.Pointer(res))[:resLen] 156 out := new(seq.Buffer) 157 out.Data = make([]byte, int(resLen)) 158 copy(out.Data, goSlice) 159 C.free(unsafe.Pointer(res)) 160 // TODO: own or copy []bytes whose addresses were passed in. 161 return out 162 } 163 return nil 164 } 165 166 // finalizeRef notifies Objective-C side of GC of a proxy object from Go side. 167 func finalizeRef(ref *seq.Ref) { 168 if ref.Num < 0 { 169 panic(fmt.Sprintf("not an Objective-C ref: %d", ref.Num)) 170 } 171 transact(ref, "", -1, new(seq.Buffer)) 172 } 173 174 func init() { 175 seq.EncString = func(out *seq.Buffer, v string) { 176 out.WriteUTF8(v) 177 } 178 seq.DecString = func(in *seq.Buffer) string { 179 return in.ReadUTF8() 180 } 181 seq.Transact = transact 182 seq.FinalizeRef = finalizeRef 183 184 C.init_seq() 185 }