objc-sync.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. /* GNU Objective C Runtime @synchronized implementation
  2. Copyright (C) 2010-2022 Free Software Foundation, Inc.
  3. Contributed by Nicola Pero <nicola.pero@meta-innovation.com>
  4. This file is part of GCC.
  5. GCC is free software; you can redistribute it and/or modify it under the
  6. terms of the GNU General Public License as published by the Free Software
  7. Foundation; either version 3, or (at your option) any later version.
  8. GCC is distributed in the hope that it will be useful, but WITHOUT ANY
  9. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  10. FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  11. details.
  12. Under Section 7 of GPL version 3, you are granted additional
  13. permissions described in the GCC Runtime Library Exception, version
  14. 3.1, as published by the Free Software Foundation.
  15. You should have received a copy of the GNU General Public License and
  16. a copy of the GCC Runtime Library Exception along with this program;
  17. see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
  18. <http://www.gnu.org/licenses/>. */
  19. /* This file implements objc_sync_enter() and objc_sync_exit(), the
  20. two functions required to support @synchronized().
  21. objc_sync_enter(object) needs to get a recursive lock associated
  22. with 'object', and lock it.
  23. objc_sync_exit(object) needs to get the recursive lock associated
  24. with 'object', and unlock it. */
  25. /* To avoid the overhead of continuously allocating and deallocating
  26. locks, we implement a pool of locks. When a lock is needed for an
  27. object, we get a lock from the pool and associate it with the
  28. object.
  29. The lock pool need to be protected by its own lock (the
  30. "protection" lock), which has to be locked then unlocked each time
  31. objc_sync_enter() and objc_sync_exit() are called. To reduce the
  32. contention on the protection lock, instead of a single pool with a
  33. single (global) protection lock we use a number of smaller pools,
  34. each with its own pool protection lock. To decide which lock pool
  35. to use for each object, we compute a hash from the object pointer.
  36. The implementation of each lock pool uses a linked list of all the
  37. locks in the pool (both unlocked, and locked); this works in the
  38. assumption that the number of locks concurrently required is very
  39. low. In practice, it seems that you rarely see more than a few
  40. locks ever concurrently required.
  41. A standard case is a thread acquiring a lock recursively, over and
  42. over again: for example when most methods of a class are protected
  43. by @synchronized(self) but they also call each other. We use
  44. thread-local storage to implement a cache and optimize this case.
  45. The cache stores locks that the thread successfully acquired,
  46. allowing objc_sync_enter() and objc_sync_exit() to locate a lock
  47. which is already held by the current thread without having to use
  48. any protection lock or synchronization mechanism. It can so detect
  49. recursive locks/unlocks, and transform them into no-ops that
  50. require no actual locking or synchronization mechanisms at all. */
  51. /* You can disable the thread-local cache (most likely to benchmark
  52. the code with and without it) by compiling with
  53. -DSYNC_CACHE_DISABLE, or commenting out the following line. */
  54. /* #define SYNC_CACHE_DISABLE */
  55. /* If thread-local storage is not available, automatically disable the
  56. cache. */
  57. #ifndef HAVE_TLS
  58. # define SYNC_CACHE_DISABLE
  59. #endif
  60. #include "objc-private/common.h"
  61. #include "objc/objc-sync.h" /* For objc_sync_enter(), objc_sync_exit() */
  62. #include "objc/runtime.h" /* For objc_malloc() */
  63. #include "objc/thr.h" /* For objc_mutex_loc() and similar */
  64. #include "objc-private/objc-sync.h" /* For __objc_sync_init() */
  65. /* We have 32 pools of locks, each of them protected by its own
  66. protection lock. It's tempting to increase this number to reduce
  67. contention; but in our tests it is high enough. */
  68. #define SYNC_NUMBER_OF_POOLS 32
  69. /* Given an object, it determines which pool contains the associated
  70. lock. */
  71. #define SYNC_OBJECT_HASH(OBJECT) ((((size_t)OBJECT >> 8) ^ (size_t)OBJECT) & (SYNC_NUMBER_OF_POOLS - 1))
  72. /* The locks protecting each pool. */
  73. static objc_mutex_t sync_pool_protection_locks[SYNC_NUMBER_OF_POOLS];
  74. /* The data structure (linked list) holding the locks. */
  75. typedef struct lock_node
  76. {
  77. /* Pointer to next entry on the list. NULL indicates end of list.
  78. You need to hold the appropriate sync_pool_protection_locks[N] to
  79. read or write this variable. */
  80. struct lock_node *next;
  81. /* The (recursive) lock. Allocated when the node is created, and
  82. always not-NULL, and unchangeable, after that. */
  83. objc_mutex_t lock;
  84. /* This is how many times the objc_mutex_lock() has been called on
  85. the lock (it is 0 when the lock is unused). Used to track when
  86. the lock is no longer associated with an object and can be reused
  87. for another object. It records "real" locks, potentially (but
  88. not necessarily) by multiple threads. You need to hold the
  89. appropriate sync_pool_protection_locks[N] to read or write this
  90. variable. */
  91. unsigned int usage_count;
  92. /* The object that the lock is associated with. This variable can
  93. only be written when holding the sync_pool_protection_locks[N]
  94. and when node->usage_count == 0, ie, the lock is not being used.
  95. You can read this variable either when you hold the
  96. sync_pool_protection_locks[N] or when you hold node->lock,
  97. because in that case you know that node->usage_count can't get to
  98. zero until you release the lock. It is valid to have usage_count
  99. == 0 and object != nil; in that case, the lock is not currently
  100. being used, but is still currently associated with the
  101. object. */
  102. id object;
  103. /* This is a counter reserved for use by the thread currently
  104. holding the lock. So, you need to hold node->lock to read or
  105. write this variable. It is normally 0, and if the cache is not
  106. being used, it is kept at 0 (even if recursive locks are being
  107. done; in that case, no difference is made between recursive and
  108. non-recursive locks: they all increase usage_count, and call
  109. objc_mutex_lock()). When the cache is being used, a thread may
  110. be able to find a lock that it already holds using the cache; in
  111. that case, to perform additional locks/unlocks it can
  112. increase/decrease the recursive_usage_count (which does not
  113. require any synchronization with other threads, since it's
  114. protected by the node->lock itself) instead of the usage_count
  115. (which requires locking the pool protection lock). And it can
  116. skip the call to objc_mutex_lock/unlock too. */
  117. unsigned int recursive_usage_count;
  118. } *lock_node_ptr;
  119. /* The pools of locks. Each of them is a linked list of lock_nodes.
  120. In the list we keep both unlocked and locked nodes. */
  121. static lock_node_ptr sync_pool_array[SYNC_NUMBER_OF_POOLS];
  122. #ifndef SYNC_CACHE_DISABLE
  123. /* We store a cache of locks acquired by each thread in thread-local
  124. storage. */
  125. static __thread lock_node_ptr *lock_cache = NULL;
  126. /* This is a conservative implementation that uses a static array of
  127. fixed size as cache. Because the cache is an array that we scan
  128. linearly, the bigger it is, the slower it gets. This does not
  129. matter much at small sizes (eg, the overhead of checking 8 cache
  130. slots instead of 4 is very small compared to the other overheads
  131. involved such as function calls and lock/unlock operations), but at
  132. large sizes it becomes important as obviously there is a size over
  133. which using the cache backfires: the lookup is so slow that the
  134. cache slows down the software instead of speeding it up. In
  135. practice, it seems that most threads use a small number of
  136. concurrent locks, so we have a conservative implementation with a
  137. fixed-size cache of 8 locks which gives a very predictable
  138. behaviour. If a thread locks lots of different locks, only the
  139. first 8 get the speed benefits of the cache, but the cache remains
  140. always small, fast and predictable.
  141. SYNC_CACHE_SIZE is the size of the lock cache for each thread. */
  142. #define SYNC_CACHE_SIZE 8
  143. #endif /* SYNC_CACHE_DISABLE */
  144. /* Called at startup by init.c. */
  145. void
  146. __objc_sync_init (void)
  147. {
  148. int i;
  149. for (i = 0; i < SYNC_NUMBER_OF_POOLS; i++)
  150. {
  151. lock_node_ptr new_node;
  152. /* Create a protection lock for each pool. */
  153. sync_pool_protection_locks[i] = objc_mutex_allocate ();
  154. /* Preallocate a lock per pool. */
  155. new_node = objc_malloc (sizeof (struct lock_node));
  156. new_node->lock = objc_mutex_allocate ();
  157. new_node->object = nil;
  158. new_node->usage_count = 0;
  159. new_node->recursive_usage_count = 0;
  160. new_node->next = NULL;
  161. sync_pool_array[i] = new_node;
  162. }
  163. }
  164. int
  165. objc_sync_enter (id object)
  166. {
  167. #ifndef SYNC_CACHE_DISABLE
  168. int free_cache_slot;
  169. #endif
  170. int hash;
  171. lock_node_ptr node;
  172. lock_node_ptr unused_node;
  173. if (object == nil)
  174. return OBJC_SYNC_SUCCESS;
  175. #ifndef SYNC_CACHE_DISABLE
  176. if (lock_cache == NULL)
  177. {
  178. /* Note that this calloc only happen only once per thread, the
  179. very first time a thread does a objc_sync_enter(). */
  180. lock_cache = objc_calloc (SYNC_CACHE_SIZE, sizeof (lock_node_ptr));
  181. }
  182. /* Check the cache to see if we have a record of having already
  183. locked the lock corresponding to this object. While doing so,
  184. keep track of the first free cache node in case we need it
  185. later. */
  186. node = NULL;
  187. free_cache_slot = -1;
  188. {
  189. int i;
  190. for (i = 0; i < SYNC_CACHE_SIZE; i++)
  191. {
  192. lock_node_ptr locked_node = lock_cache[i];
  193. if (locked_node == NULL)
  194. {
  195. if (free_cache_slot == -1)
  196. free_cache_slot = i;
  197. }
  198. else if (locked_node->object == object)
  199. {
  200. node = locked_node;
  201. break;
  202. }
  203. }
  204. }
  205. if (node != NULL)
  206. {
  207. /* We found the lock. Increase recursive_usage_count, which is
  208. protected by node->lock, which we already hold. */
  209. node->recursive_usage_count++;
  210. /* There is no need to actually lock anything, since we already
  211. hold the lock. Correspondingly, objc_sync_exit() will just
  212. decrease recursive_usage_count and do nothing to unlock. */
  213. return OBJC_SYNC_SUCCESS;
  214. }
  215. #endif /* SYNC_CACHE_DISABLE */
  216. /* The following is the standard lookup for the lock in the standard
  217. pool lock. It requires a pool protection lock. */
  218. hash = SYNC_OBJECT_HASH(object);
  219. /* Search for an existing lock for 'object'. While searching, make
  220. note of any unused lock if we find any. */
  221. unused_node = NULL;
  222. objc_mutex_lock (sync_pool_protection_locks[hash]);
  223. node = sync_pool_array[hash];
  224. while (node != NULL)
  225. {
  226. if (node->object == object)
  227. {
  228. /* We found the lock. */
  229. node->usage_count++;
  230. objc_mutex_unlock (sync_pool_protection_locks[hash]);
  231. #ifndef SYNC_CACHE_DISABLE
  232. /* Put it in the cache. */
  233. if (free_cache_slot != -1)
  234. lock_cache[free_cache_slot] = node;
  235. #endif
  236. /* Lock it. */
  237. objc_mutex_lock (node->lock);
  238. return OBJC_SYNC_SUCCESS;
  239. }
  240. if (unused_node == NULL && node->usage_count == 0)
  241. {
  242. /* We found the first unused node. Record it. */
  243. unused_node = node;
  244. }
  245. node = node->next;
  246. }
  247. /* An existing lock for 'object' could not be found. */
  248. if (unused_node != NULL)
  249. {
  250. /* But we found a unused lock; use it. */
  251. unused_node->object = object;
  252. unused_node->usage_count = 1;
  253. unused_node->recursive_usage_count = 0;
  254. objc_mutex_unlock (sync_pool_protection_locks[hash]);
  255. #ifndef SYNC_CACHE_DISABLE
  256. if (free_cache_slot != -1)
  257. lock_cache[free_cache_slot] = unused_node;
  258. #endif
  259. objc_mutex_lock (unused_node->lock);
  260. return OBJC_SYNC_SUCCESS;
  261. }
  262. else
  263. {
  264. /* There are no unused nodes; allocate a new node. */
  265. lock_node_ptr new_node;
  266. /* Create the node. */
  267. new_node = objc_malloc (sizeof (struct lock_node));
  268. new_node->lock = objc_mutex_allocate ();
  269. new_node->object = object;
  270. new_node->usage_count = 1;
  271. new_node->recursive_usage_count = 0;
  272. /* Attach it at the beginning of the pool. */
  273. new_node->next = sync_pool_array[hash];
  274. sync_pool_array[hash] = new_node;
  275. objc_mutex_unlock (sync_pool_protection_locks[hash]);
  276. #ifndef SYNC_CACHE_DISABLE
  277. if (free_cache_slot != -1)
  278. lock_cache[free_cache_slot] = new_node;
  279. #endif
  280. objc_mutex_lock (new_node->lock);
  281. return OBJC_SYNC_SUCCESS;
  282. }
  283. }
  284. int
  285. objc_sync_exit (id object)
  286. {
  287. int hash;
  288. lock_node_ptr node;
  289. if (object == nil)
  290. return OBJC_SYNC_SUCCESS;
  291. #ifndef SYNC_CACHE_DISABLE
  292. if (lock_cache != NULL)
  293. {
  294. int i;
  295. /* Find the lock in the cache. */
  296. node = NULL;
  297. for (i = 0; i < SYNC_CACHE_SIZE; i++)
  298. {
  299. lock_node_ptr locked_node = lock_cache[i];
  300. if (locked_node != NULL && locked_node->object == object)
  301. {
  302. node = locked_node;
  303. break;
  304. }
  305. }
  306. /* Note that, if a node was found in the cache, the variable i
  307. now holds the index where it was found, which will be used to
  308. remove it from the cache. */
  309. if (node != NULL)
  310. {
  311. if (node->recursive_usage_count > 0)
  312. {
  313. node->recursive_usage_count--;
  314. return OBJC_SYNC_SUCCESS;
  315. }
  316. else
  317. {
  318. /* We need to do a real unlock. */
  319. hash = SYNC_OBJECT_HASH(object);
  320. /* TODO: If we had atomic increase/decrease operations
  321. with memory barriers, we could avoid the lock
  322. here! */
  323. objc_mutex_lock (sync_pool_protection_locks[hash]);
  324. node->usage_count--;
  325. /* Normally, we do not reset object to nil here. We'll
  326. leave the lock associated with that object, at zero
  327. usage count. This makes it slightly more efficient to
  328. provide a lock for that object if (as likely)
  329. requested again. If the object is deallocated, we
  330. don't care. It will never match a new lock that is
  331. requested, and the node will be reused at some point.
  332. But, if garbage collection is enabled, leaving a
  333. pointer to the object in memory might prevent the
  334. object from being released. In that case, we remove
  335. it (TODO: maybe we should avoid using the garbage
  336. collector at all ? Nothing is ever deallocated in
  337. this file). */
  338. #if OBJC_WITH_GC
  339. node->object = nil;
  340. #endif
  341. objc_mutex_unlock (sync_pool_protection_locks[hash]);
  342. /* PS: Between objc_mutex_unlock
  343. (sync_pool_protection_locks[hash]) and
  344. objc_mutex_unlock (node->lock), the pool is unlocked
  345. so other threads may allocate this same lock to
  346. another object (!). This is not a problem, but it is
  347. curious. */
  348. objc_mutex_unlock (node->lock);
  349. /* Remove the node from the cache. */
  350. lock_cache[i] = NULL;
  351. return OBJC_SYNC_SUCCESS;
  352. }
  353. }
  354. }
  355. #endif
  356. /* The cache either wasn't there, or didn't work (eg, we overflowed
  357. it at some point and stopped recording new locks in the cache).
  358. Proceed with a full search of the lock pool. */
  359. hash = SYNC_OBJECT_HASH(object);
  360. objc_mutex_lock (sync_pool_protection_locks[hash]);
  361. /* Search for an existing lock for 'object'. */
  362. node = sync_pool_array[hash];
  363. while (node != NULL)
  364. {
  365. if (node->object == object)
  366. {
  367. /* We found the lock. */
  368. node->usage_count--;
  369. objc_mutex_unlock (sync_pool_protection_locks[hash]);
  370. objc_mutex_unlock (node->lock);
  371. /* No need to remove the node from the cache, since it
  372. wasn't found in the cache when we looked for it! */
  373. return OBJC_SYNC_SUCCESS;
  374. }
  375. node = node->next;
  376. }
  377. objc_mutex_unlock (sync_pool_protection_locks[hash]);
  378. /* A lock for 'object' to unlock could not be found (!!). */
  379. return OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
  380. }