seq_darwin.m.support 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. // Copyright 2016 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. #include <stdio.h>
  5. #include <stdint.h>
  6. #include <string.h>
  7. #include <Foundation/Foundation.h>
  8. #include "seq.h"
  9. #include "_cgo_export.h"
  10. // * Objective-C implementation of a Go interface type
  11. //
  12. // For an interface testpkg.I, gobind defines a protocol GoSeqTestpkgI.
  13. // Reference tracker (tracker) maintains two maps:
  14. // 1) _refs: objective-C object pointer -> a refnum (starting from 42).
  15. // 2) _objs: refnum -> RefCounter.
  16. //
  17. // Whenever a user's object conforming the protocol is sent to Go (through
  18. // a function or method that takes I), _refs is consulted to find the refnum
  19. // of the object. If not found, the refnum is assigned and stored.
  20. //
  21. // _objs is also updated so that the RefCounter is incremented and the
  22. // user's object is pinned.
  23. //
  24. // When a Go side needs to call a method of the interface, the Go side
  25. // notifies the Objective-C side of the object's refnum. Upon receiving the
  26. // request, Objective-C side looks up the object from _objs map, and sends
  27. // the method to the object.
  28. //
  29. // The RefCount counts the references on objective-C objects from Go side,
  30. // and pins the objective-C objects until there is no more references from
  31. // Go side.
  32. //
  33. // * Objective-C proxy of a Go object (struct or interface type)
  34. //
  35. // For Go type object, a objective-C proxy instance is created whenever
  36. // the object reference is passed into objective-C.
  37. //
  38. // While crossing the language barrier there is a brief window where the foreign
  39. // proxy object might be finalized but the refnum is not yet translated to its object.
  40. // If the proxy object was the last reference to the foreign object, the refnum
  41. // will be invalid by the time it is looked up in the foreign reference tracker.
  42. //
  43. // To make sure the foreign object is kept live while its refnum is in transit,
  44. // increment its refererence count before crossing. The other side will decrement
  45. // it again immediately after the refnum is converted to its object.
  46. // Note that this file is copied into and compiled with the generated
  47. // bindings.
  48. // A simple thread-safe mutable dictionary.
  49. @interface goSeqDictionary : NSObject {
  50. }
  51. @property NSMutableDictionary *dict;
  52. @end
  53. @implementation goSeqDictionary
  54. - (id)init {
  55. if (self = [super init]) {
  56. _dict = [[NSMutableDictionary alloc] init];
  57. }
  58. return self;
  59. }
  60. - (id)get:(id)key {
  61. @synchronized(self) {
  62. return [_dict objectForKey:key];
  63. }
  64. }
  65. - (void)put:(id)obj withKey:(id)key {
  66. @synchronized(self) {
  67. [_dict setObject:obj forKey:key];
  68. }
  69. }
  70. @end
  71. // NULL_REFNUM is also known to bind/seq/ref.go and bind/java/Seq.java
  72. #define NULL_REFNUM 41
  73. // RefTracker encapsulates a map of objective-C objects passed to Go and
  74. // the reference number counter which is incremented whenever an objective-C
  75. // object that implements a Go interface is created.
  76. @interface RefTracker : NSObject {
  77. int32_t _next;
  78. NSMutableDictionary *_refs; // map: object ptr -> refnum
  79. NSMutableDictionary *_objs; // map: refnum -> RefCounter*
  80. }
  81. - (id)init;
  82. // decrements the counter of the objective-C object with the reference number.
  83. // This is called whenever a Go proxy to this object is finalized.
  84. // When the counter reaches 0, the object is removed from the map.
  85. - (void)dec:(int32_t)refnum;
  86. // increments the counter of the objective-C object with the reference number.
  87. // This is called whenever a Go proxy is converted to its refnum and send
  88. // across the language barrier.
  89. - (void)inc:(int32_t)refnum;
  90. // returns the object of the reference number.
  91. - (id)get:(int32_t)refnum;
  92. // returns the reference number of the object and increments the ref count.
  93. // This is called whenever an Objective-C object is sent to Go side.
  94. - (int32_t)assignRefnumAndIncRefcount:(id)obj;
  95. @end
  96. static RefTracker *tracker = NULL;
  97. #define IS_FROM_GO(refnum) ((refnum) < 0)
  98. // init_seq is called when the Go side is initialized.
  99. void init_seq() { tracker = [[RefTracker alloc] init]; }
  100. void go_seq_dec_ref(int32_t refnum) {
  101. @autoreleasepool {
  102. [tracker dec:refnum];
  103. }
  104. }
  105. void go_seq_inc_ref(int32_t refnum) {
  106. @autoreleasepool {
  107. [tracker inc:refnum];
  108. }
  109. }
  110. NSData *go_seq_to_objc_bytearray(nbyteslice s, int copy) {
  111. if (s.ptr == NULL) {
  112. return NULL;
  113. }
  114. BOOL freeWhenDone = copy ? YES : NO;
  115. return [NSData dataWithBytesNoCopy:s.ptr length:s.len freeWhenDone:freeWhenDone];
  116. }
  117. NSString *go_seq_to_objc_string(nstring str) {
  118. if (str.len == 0) { // empty string.
  119. return @"";
  120. }
  121. NSString * res = [[NSString alloc] initWithBytesNoCopy:str.ptr
  122. length:str.len
  123. encoding:NSUTF8StringEncoding
  124. freeWhenDone:YES];
  125. return res;
  126. }
  127. id go_seq_objc_from_refnum(int32_t refnum) {
  128. id obj = [tracker get:refnum];
  129. // Go called IncForeignRef just before converting its proxy to its refnum. Decrement it here.
  130. // It's very important to decrement *after* fetching the reference from the tracker, in case
  131. // there are no other proxy references to the object.
  132. [tracker dec:refnum];
  133. return obj;
  134. }
  135. GoSeqRef *go_seq_from_refnum(int32_t refnum) {
  136. if (refnum == NULL_REFNUM) {
  137. return nil;
  138. }
  139. if (IS_FROM_GO(refnum)) {
  140. return [[GoSeqRef alloc] initWithRefnum:refnum obj:NULL];
  141. }
  142. return [[GoSeqRef alloc] initWithRefnum:refnum obj:go_seq_objc_from_refnum(refnum)];
  143. }
  144. int32_t go_seq_to_refnum(id obj) {
  145. if (obj == nil) {
  146. return NULL_REFNUM;
  147. }
  148. return [tracker assignRefnumAndIncRefcount:obj];
  149. }
  150. int32_t go_seq_go_to_refnum(GoSeqRef *ref) {
  151. int32_t refnum = [ref incNum];
  152. if (!IS_FROM_GO(refnum)) {
  153. LOG_FATAL(@"go_seq_go_to_refnum on objective-c objects is not permitted");
  154. }
  155. return refnum;
  156. }
  157. nbyteslice go_seq_from_objc_bytearray(NSData *data, int copy) {
  158. struct nbyteslice res = {NULL, 0};
  159. int sz = data.length;
  160. if (sz == 0) {
  161. return res;
  162. }
  163. void *ptr;
  164. // If the argument was not a NSMutableData, copy the data so that
  165. // the NSData is not changed from Go. The corresponding free is called
  166. // by releaseByteSlice.
  167. if (copy || ![data isKindOfClass:[NSMutableData class]]) {
  168. void *arr_copy = malloc(sz);
  169. if (arr_copy == NULL) {
  170. LOG_FATAL(@"malloc failed");
  171. }
  172. memcpy(arr_copy, [data bytes], sz);
  173. ptr = arr_copy;
  174. } else {
  175. ptr = (void *)[data bytes];
  176. }
  177. res.ptr = ptr;
  178. res.len = sz;
  179. return res;
  180. }
  181. nstring go_seq_from_objc_string(NSString *s) {
  182. nstring res = {NULL, 0};
  183. int len = [s lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
  184. if (len == 0) {
  185. if (s.length > 0) {
  186. LOG_INFO(@"unable to encode an NSString into UTF-8");
  187. }
  188. return res;
  189. }
  190. char *buf = (char *)malloc(len);
  191. if (buf == NULL) {
  192. LOG_FATAL(@"malloc failed");
  193. }
  194. NSUInteger used;
  195. [s getBytes:buf
  196. maxLength:len
  197. usedLength:&used
  198. encoding:NSUTF8StringEncoding
  199. options:0
  200. range:NSMakeRange(0, [s length])
  201. remainingRange:NULL];
  202. res.ptr = buf;
  203. res.len = used;
  204. return res;
  205. }
  206. @implementation GoSeqRef {
  207. }
  208. - (id)init {
  209. LOG_FATAL(@"GoSeqRef init is disallowed");
  210. }
  211. - (int32_t)incNum {
  212. IncGoRef(_refnum);
  213. return _refnum;
  214. }
  215. // called when an object from Go is passed in.
  216. - (instancetype)initWithRefnum:(int32_t)refnum obj:(id)obj {
  217. self = [super init];
  218. if (self) {
  219. _refnum = refnum;
  220. _obj = obj;
  221. }
  222. return self;
  223. }
  224. - (void)dealloc {
  225. if (IS_FROM_GO(_refnum)) {
  226. DestroyRef(_refnum);
  227. }
  228. }
  229. @end
  230. // RefCounter is a pair of (GoSeqProxy, count). GoSeqProxy has a strong
  231. // reference to an Objective-C object. The count corresponds to
  232. // the number of Go proxy objects.
  233. //
  234. // RefTracker maintains a map of refnum to RefCounter, for every
  235. // Objective-C objects passed to Go. This map allows the transact
  236. // call to relay the method call to the right Objective-C object, and
  237. // prevents the Objective-C objects from being deallocated
  238. // while they are still referenced from Go side.
  239. @interface RefCounter : NSObject {
  240. }
  241. @property(strong, readonly) id obj;
  242. @property int cnt;
  243. - (id)initWithObject:(id)obj;
  244. @end
  245. @implementation RefCounter {
  246. }
  247. - (id)initWithObject:(id)obj {
  248. self = [super init];
  249. if (self) {
  250. _obj = obj;
  251. _cnt = 0;
  252. }
  253. return self;
  254. }
  255. @end
  256. @implementation RefTracker {
  257. }
  258. - (id)init {
  259. self = [super init];
  260. if (self) {
  261. _next = 42;
  262. _refs = [[NSMutableDictionary alloc] init];
  263. _objs = [[NSMutableDictionary alloc] init];
  264. }
  265. return self;
  266. }
  267. - (void)dec:(int32_t)refnum { // called whenever a go proxy object is finalized.
  268. if (IS_FROM_GO(refnum)) {
  269. LOG_FATAL(@"dec:invalid refnum for Objective-C objects");
  270. }
  271. @synchronized(self) {
  272. id key = @(refnum);
  273. RefCounter *counter = [_objs objectForKey:key];
  274. if (counter == NULL) {
  275. LOG_FATAL(@"unknown refnum");
  276. }
  277. int n = counter.cnt;
  278. if (n <= 0) {
  279. LOG_FATAL(@"refcount underflow");
  280. } else if (n == 1) {
  281. LOG_DEBUG(@"remove the reference %d", refnum);
  282. NSValue *ptr = [NSValue valueWithPointer:(const void *)(counter.obj)];
  283. [_refs removeObjectForKey:ptr];
  284. [_objs removeObjectForKey:key];
  285. } else {
  286. counter.cnt = n - 1;
  287. }
  288. }
  289. }
  290. // inc is called whenever a ObjC refnum crosses from Go to ObjC
  291. - (void)inc:(int32_t)refnum {
  292. if (IS_FROM_GO(refnum)) {
  293. LOG_FATAL(@"dec:invalid refnum for Objective-C objects");
  294. }
  295. @synchronized(self) {
  296. id key = @(refnum);
  297. RefCounter *counter = [_objs objectForKey:key];
  298. if (counter == NULL) {
  299. LOG_FATAL(@"unknown refnum");
  300. }
  301. counter.cnt++;
  302. }
  303. }
  304. - (id)get:(int32_t)refnum {
  305. if (IS_FROM_GO(refnum)) {
  306. LOG_FATAL(@"get:invalid refnum for Objective-C objects");
  307. }
  308. @synchronized(self) {
  309. RefCounter *counter = _objs[@(refnum)];
  310. if (counter == NULL) {
  311. LOG_FATAL(@"unidentified object refnum: %d", refnum);
  312. }
  313. return counter.obj;
  314. }
  315. }
  316. - (int32_t)assignRefnumAndIncRefcount:(id)obj {
  317. @synchronized(self) {
  318. NSValue *ptr = [NSValue valueWithPointer:(const void *)(obj)];
  319. NSNumber *refnum = [_refs objectForKey:ptr];
  320. if (refnum == NULL) {
  321. refnum = @(_next++);
  322. _refs[ptr] = refnum;
  323. }
  324. RefCounter *counter = [_objs objectForKey:refnum];
  325. if (counter == NULL) {
  326. counter = [[RefCounter alloc] initWithObject:obj];
  327. counter.cnt = 1;
  328. _objs[refnum] = counter;
  329. } else {
  330. counter.cnt++;
  331. }
  332. return (int32_t)([refnum intValue]);
  333. }
  334. }
  335. @end