vpx_once.patch 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. commit 64f73cc1f7f57ca6643b027eae63041fec408ea8
  2. Author: Ralph Giles <giles@mozilla.com>
  3. Date: Fri Nov 6 16:42:49 2015 -0800
  4. Bug 1218124 - Use InterlockCompare in win32 vpx_once(). r=gerald
  5. diff --git a/media/libvpx/vpx_ports/vpx_once.h b/media/libvpx/vpx_ports/vpx_once.h
  6. index f1df394..da04db4 100644
  7. --- a/media/libvpx/vpx_ports/vpx_once.h
  8. +++ b/media/libvpx/vpx_ports/vpx_once.h
  9. @@ -1,5 +1,5 @@
  10. /*
  11. - * Copyright (c) 2011 The WebM project authors. All Rights Reserved.
  12. + * Copyright (c) 2015 The WebM project authors. All Rights Reserved.
  13. *
  14. * Use of this source code is governed by a BSD-style license
  15. * that can be found in the LICENSE file in the root of the source
  16. @@ -13,63 +13,83 @@
  17. #include "vpx_config.h"
  18. +/* Implement a function wrapper to guarantee initialization
  19. + * thread-safety for library singletons.
  20. + *
  21. + * NOTE: These functions use static locks, and can only be
  22. + * used with one common argument per compilation unit. So
  23. + *
  24. + * file1.c:
  25. + * vpx_once(foo);
  26. + * ...
  27. + * vpx_once(foo);
  28. + *
  29. + * file2.c:
  30. + * vpx_once(bar);
  31. + *
  32. + * will ensure foo() and bar() are each called only once, but in
  33. + *
  34. + * file1.c:
  35. + * vpx_once(foo);
  36. + * vpx_once(bar):
  37. + *
  38. + * bar() will never be called because the lock is used up
  39. + * by the call to foo().
  40. + */
  41. +
  42. #if CONFIG_MULTITHREAD && defined(_WIN32)
  43. #include <windows.h>
  44. #include <stdlib.h>
  45. +/* Declare a per-compilation-unit state variable to track the progress
  46. + * of calling func() only once. This must be at global scope because
  47. + * local initializers are not thread-safe in MSVC prior to Visual
  48. + * Studio 2015.
  49. + *
  50. + * As a static, once_state will be zero-initialized as program start.
  51. + */
  52. +static LONG once_state;
  53. static void once(void (*func)(void))
  54. {
  55. - static CRITICAL_SECTION *lock;
  56. - static LONG waiters;
  57. - static int done;
  58. - void *lock_ptr = &lock;
  59. -
  60. - /* If the initialization is complete, return early. This isn't just an
  61. - * optimization, it prevents races on the destruction of the global
  62. - * lock.
  63. + /* Try to advance once_state from its initial value of 0 to 1.
  64. + * Only one thread can succeed in doing so.
  65. */
  66. - if(done)
  67. + if (InterlockedCompareExchange(&once_state, 1, 0) == 0) {
  68. + /* We're the winning thread, having set once_state to 1.
  69. + * Call our function. */
  70. + func();
  71. + /* Now advance once_state to 2, unblocking any other threads. */
  72. + InterlockedIncrement(&once_state);
  73. return;
  74. -
  75. - InterlockedIncrement(&waiters);
  76. -
  77. - /* Get a lock. We create one and try to make it the one-true-lock,
  78. - * throwing it away if we lost the race.
  79. - */
  80. -
  81. - {
  82. - /* Scope to protect access to new_lock */
  83. - CRITICAL_SECTION *new_lock = malloc(sizeof(CRITICAL_SECTION));
  84. - InitializeCriticalSection(new_lock);
  85. - if (InterlockedCompareExchangePointer(lock_ptr, new_lock, NULL) != NULL)
  86. - {
  87. - DeleteCriticalSection(new_lock);
  88. - free(new_lock);
  89. - }
  90. }
  91. - /* At this point, we have a lock that can be synchronized on. We don't
  92. - * care which thread actually performed the allocation.
  93. + /* We weren't the winning thread, but we want to block on
  94. + * the state variable so we don't return before func()
  95. + * has finished executing elsewhere.
  96. + *
  97. + * Try to advance once_state from 2 to 2, which is only possible
  98. + * after the winning thead advances it from 1 to 2.
  99. */
  100. -
  101. - EnterCriticalSection(lock);
  102. -
  103. - if (!done)
  104. - {
  105. - func();
  106. - done = 1;
  107. + while (InterlockedCompareExchange(&once_state, 2, 2) != 2) {
  108. + /* State isn't yet 2. Try again.
  109. + *
  110. + * We are used for singleton initialization functions,
  111. + * which should complete quickly. Contention will likewise
  112. + * be rare, so it's worthwhile to use a simple but cpu-
  113. + * intensive busy-wait instead of successive backoff,
  114. + * waiting on a kernel object, or another heavier-weight scheme.
  115. + *
  116. + * We can at least yield our timeslice.
  117. + */
  118. + Sleep(0);
  119. }
  120. - LeaveCriticalSection(lock);
  121. -
  122. - /* Last one out should free resources. The destructed objects are
  123. - * protected by checking if(done) above.
  124. + /* We've seen once_state advance to 2, so we know func()
  125. + * has been called. And we've left once_state as we found it,
  126. + * so other threads will have the same experience.
  127. + *
  128. + * It's safe to return now.
  129. */
  130. - if(!InterlockedDecrement(&waiters))
  131. - {
  132. - DeleteCriticalSection(lock);
  133. - free(lock);
  134. - lock = NULL;
  135. - }
  136. + return;
  137. }