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  }