libglib2-0003-implement-GMutex-natively-on-Linux.patch 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. From 49b59e5ac4428a6a99a85d699c3662f96efc4e9d Mon Sep 17 00:00:00 2001
  2. From: Ryan Lortie <desrt@desrt.ca>
  3. Date: Tue, 10 Jun 2014 08:28:32 -0400
  4. Subject: [PATCH] GLib: implement GMutex natively on Linux
  5. If we have futex(2) then we can implement GMutex natively and gain a
  6. substantial performance increase (vs. using pthreads).
  7. This also avoids the need to allocate an extra structure in memory when
  8. using GMutex or GCond: we can use the structure directly.
  9. The main reason for the increase in performance is that our
  10. implementation can be made more simple: we don't need to support the
  11. array of options on pthread_mutex_t (which includes the possibility, for
  12. example, of being recursive).
  13. The result is a ~30% improvement in uncontended cases and a much larger
  14. increase (3 to 4 times) in contended cases for a simple testcase.
  15. https://bugzilla.gnome.org/show_bug.cgi?id=731986
  16. ---
  17. glib/gthread-posix.c | 208 ++++++++++++++++++++++++++++++++++++++++++++++++++-
  18. 1 file changed, 207 insertions(+), 1 deletion(-)
  19. diff --git a/glib/gthread-posix.c b/glib/gthread-posix.c
  20. index 6f5a606..f7d5d8a 100644
  21. --- a/glib/gthread-posix.c
  22. +++ b/glib/gthread-posix.c
  23. @@ -66,6 +66,11 @@
  24. #include <windows.h>
  25. #endif
  26. +/* clang defines __ATOMIC_SEQ_CST but doesn't support the GCC extension */
  27. +#if defined(HAVE_FUTEX) && defined(__ATOMIC_SEQ_CST) && !defined(__clang__)
  28. +#define USE_NATIVE_MUTEX
  29. +#endif
  30. +
  31. static void
  32. g_thread_abort (gint status,
  33. const gchar *function)
  34. @@ -77,6 +82,8 @@ g_thread_abort (gint status,
  35. /* {{{1 GMutex */
  36. +#if !defined(USE_NATIVE_MUTEX)
  37. +
  38. static pthread_mutex_t *
  39. g_mutex_impl_new (void)
  40. {
  41. @@ -258,6 +265,8 @@ g_mutex_trylock (GMutex *mutex)
  42. return FALSE;
  43. }
  44. +#endif /* !defined(USE_NATIVE_MUTEX) */
  45. +
  46. /* {{{1 GRecMutex */
  47. static pthread_mutex_t *
  48. @@ -631,6 +640,8 @@ g_rw_lock_reader_unlock (GRWLock *rw_lock)
  49. /* {{{1 GCond */
  50. +#if !defined(USE_NATIVE_MUTEX)
  51. +
  52. static pthread_cond_t *
  53. g_cond_impl_new (void)
  54. {
  55. @@ -902,6 +913,8 @@ g_cond_wait_until (GCond *cond,
  56. return FALSE;
  57. }
  58. +#endif /* defined(USE_NATIVE_MUTEX) */
  59. +
  60. /* {{{1 GPrivate */
  61. /**
  62. @@ -1219,5 +1232,198 @@ g_system_thread_set_name (const gchar *name)
  63. #endif
  64. }
  65. -/* {{{1 Epilogue */
  66. +/* {{{1 GMutex and GCond futex implementation */
  67. +
  68. +#if defined(USE_NATIVE_MUTEX)
  69. +
  70. +#include <linux/futex.h>
  71. +#include <sys/syscall.h>
  72. +
  73. +/* We should expand the set of operations available in gatomic once we
  74. + * have better C11 support in GCC in common distributions (ie: 4.9).
  75. + *
  76. + * Before then, let's define a couple of useful things for our own
  77. + * purposes...
  78. + */
  79. +
  80. +#define exchange_acquire(ptr, new) \
  81. + __atomic_exchange_4((ptr), (new), __ATOMIC_ACQUIRE)
  82. +#define compare_exchange_acquire(ptr, old, new) \
  83. + __atomic_compare_exchange_4((ptr), (old), (new), 0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)
  84. +
  85. +#define exchange_release(ptr, new) \
  86. + __atomic_exchange_4((ptr), (new), __ATOMIC_RELEASE)
  87. +#define store_release(ptr, new) \
  88. + __atomic_store_4((ptr), (new), __ATOMIC_RELEASE)
  89. +
  90. +/* Our strategy for the mutex is pretty simple:
  91. + *
  92. + * 0: not in use
  93. + *
  94. + * 1: acquired by one thread only, no contention
  95. + *
  96. + * > 1: contended
  97. + *
  98. + *
  99. + * As such, attempting to acquire the lock should involve an increment.
  100. + * If we find that the previous value was 0 then we can return
  101. + * immediately.
  102. + *
  103. + * On unlock, we always store 0 to indicate that the lock is available.
  104. + * If the value there was 1 before then we didn't have contention and
  105. + * can return immediately. If the value was something other than 1 then
  106. + * we have the contended case and need to wake a waiter.
  107. + *
  108. + * If it was not 0 then there is another thread holding it and we must
  109. + * wait. We must always ensure that we mark a value >1 while we are
  110. + * waiting in order to instruct the holder to do a wake operation on
  111. + * unlock.
  112. + */
  113. +
  114. +void
  115. +g_mutex_init (GMutex *mutex)
  116. +{
  117. + mutex->i[0] = 0;
  118. +}
  119. +
  120. +void
  121. +g_mutex_clear (GMutex *mutex)
  122. +{
  123. +}
  124. +
  125. +static void __attribute__((noinline))
  126. +g_mutex_lock_slowpath (GMutex *mutex)
  127. +{
  128. + /* Set to 2 to indicate contention. If it was zero before then we
  129. + * just acquired the lock.
  130. + *
  131. + * Otherwise, sleep for as long as the 2 remains...
  132. + */
  133. + while (exchange_acquire (&mutex->i[0], 2) != 0)
  134. + syscall (__NR_futex, &mutex->i[0], (gsize) FUTEX_WAIT, (gsize) 2, NULL);
  135. +}
  136. +
  137. +static void __attribute__((noinline))
  138. +g_mutex_unlock_slowpath (GMutex *mutex)
  139. +{
  140. + /* We seem to get better code for the uncontended case by splitting
  141. + * out this call...
  142. + */
  143. + syscall (__NR_futex, &mutex->i[0], (gsize) FUTEX_WAKE, (gsize) 1, NULL);
  144. +}
  145. +
  146. +void
  147. +g_mutex_lock (GMutex *mutex)
  148. +{
  149. + /* 0 -> 1 and we're done. Anything else, and we need to wait... */
  150. + if G_UNLIKELY (g_atomic_int_add (&mutex->i[0], 1) != 0)
  151. + g_mutex_lock_slowpath (mutex);
  152. +}
  153. +
  154. +void
  155. +g_mutex_unlock (GMutex *mutex)
  156. +{
  157. + /* 1-> 0 and we're done. Anything else and we need to signal... */
  158. + if G_UNLIKELY (exchange_release (&mutex->i[0], 0) != 1)
  159. + g_mutex_unlock_slowpath (mutex);
  160. +}
  161. +
  162. +gboolean
  163. +g_mutex_trylock (GMutex *mutex)
  164. +{
  165. + guint zero = 0;
  166. +
  167. + /* We don't want to touch the value at all unless we can move it from
  168. + * exactly 0 to 1.
  169. + */
  170. + return compare_exchange_acquire (&mutex->i[0], &zero, 1);
  171. +}
  172. +
  173. +/* Condition variables are implemented in a rather simple way as well.
  174. + * In many ways, futex() as an abstraction is even more ideally suited
  175. + * to condition variables than it is to mutexes.
  176. + *
  177. + * We store a generation counter. We sample it with the lock held and
  178. + * unlock before sleeping on the futex.
  179. + *
  180. + * Signalling simply involves increasing the counter and making the
  181. + * appropriate futex call.
  182. + *
  183. + * The only thing that is the slightest bit complicated is timed waits
  184. + * because we must convert our absolute time to relative.
  185. + */
  186. +
  187. +void
  188. +g_cond_init (GCond *cond)
  189. +{
  190. + cond->i[0] = 0;
  191. +}
  192. +
  193. +void
  194. +g_cond_clear (GCond *cond)
  195. +{
  196. +}
  197. +
  198. +void
  199. +g_cond_wait (GCond *cond,
  200. + GMutex *mutex)
  201. +{
  202. + guint sampled = g_atomic_int_get (&cond->i[0]);
  203. +
  204. + g_mutex_unlock (mutex);
  205. + syscall (__NR_futex, &cond->i[0], (gsize) FUTEX_WAIT, (gsize) sampled, NULL);
  206. + g_mutex_lock (mutex);
  207. +}
  208. +
  209. +void
  210. +g_cond_signal (GCond *cond)
  211. +{
  212. + g_atomic_int_inc (&cond->i[0]);
  213. +
  214. + syscall (__NR_futex, &cond->i[0], (gsize) FUTEX_WAKE, (gsize) 1, NULL);
  215. +}
  216. +
  217. +void
  218. +g_cond_broadcast (GCond *cond)
  219. +{
  220. + g_atomic_int_inc (&cond->i[0]);
  221. +
  222. + syscall (__NR_futex, &cond->i[0], (gsize) FUTEX_WAKE, (gsize) INT_MAX, NULL);
  223. +}
  224. +
  225. +gboolean
  226. +g_cond_wait_until (GCond *cond,
  227. + GMutex *mutex,
  228. + gint64 end_time)
  229. +{
  230. + struct timespec now;
  231. + struct timespec span;
  232. + guint sampled;
  233. +
  234. + if (end_time < 0)
  235. + return FALSE;
  236. +
  237. + clock_gettime (CLOCK_MONOTONIC, &now);
  238. + span.tv_sec = (end_time / 1000000) - now.tv_sec;
  239. + span.tv_nsec = ((end_time % 1000000) * 1000) - now.tv_nsec;
  240. + if (span.tv_nsec < 0)
  241. + {
  242. + span.tv_nsec += 1000000000;
  243. + span.tv_sec--;
  244. + }
  245. +
  246. + if (span.tv_sec < 0)
  247. + return FALSE;
  248. +
  249. + sampled = cond->i[0];
  250. + g_mutex_unlock (mutex);
  251. + syscall (__NR_futex, &cond->i[0], (gsize) FUTEX_WAIT, (gsize) sampled, &span);
  252. + g_mutex_lock (mutex);
  253. +
  254. + return TRUE;
  255. +}
  256. +
  257. +#endif
  258. +
  259. + /* {{{1 Epilogue */
  260. /* vim:set foldmethod=marker: */
  261. --
  262. 1.8.1.4