SDL_syssem.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. /*
  2. Simple DirectMedia Layer
  3. Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
  4. This software is provided 'as-is', without any express or implied
  5. warranty. In no event will the authors be held liable for any damages
  6. arising from the use of this software.
  7. Permission is granted to anyone to use this software for any purpose,
  8. including commercial applications, and to alter it and redistribute it
  9. freely, subject to the following restrictions:
  10. 1. The origin of this software must not be misrepresented; you must not
  11. claim that you wrote the original software. If you use this software
  12. in a product, an acknowledgment in the product documentation would be
  13. appreciated but is not required.
  14. 2. Altered source versions must be plainly marked as such, and must not be
  15. misrepresented as being the original software.
  16. 3. This notice may not be removed or altered from any source distribution.
  17. */
  18. #include "../../SDL_internal.h"
  19. #if SDL_THREAD_WINDOWS
  20. /**
  21. * Semaphore functions using the Win32 API
  22. * There are two implementations available based on:
  23. * - Kernel Semaphores. Available on all OS versions. (kern)
  24. * Heavy-weight inter-process kernel objects.
  25. * - Atomics and WaitOnAddress API. (atom)
  26. * Faster due to significantly less context switches.
  27. * Requires Windows 8 or newer.
  28. * which are chosen at runtime.
  29. */
  30. #include "../../core/windows/SDL_windows.h"
  31. typedef SDL_sem * (*pfnSDL_CreateSemaphore)(Uint32);
  32. typedef void (*pfnSDL_DestroySemaphore)(SDL_sem *);
  33. typedef int (*pfnSDL_SemWaitTimeout)(SDL_sem *, Uint32);
  34. typedef int (*pfnSDL_SemTryWait)(SDL_sem *);
  35. typedef int (*pfnSDL_SemWait)(SDL_sem *);
  36. typedef Uint32 (*pfnSDL_SemValue)(SDL_sem *);
  37. typedef int (*pfnSDL_SemPost)(SDL_sem *);
  38. typedef struct SDL_semaphore_impl_t
  39. {
  40. pfnSDL_CreateSemaphore Create;
  41. pfnSDL_DestroySemaphore Destroy;
  42. pfnSDL_SemWaitTimeout WaitTimeout;
  43. pfnSDL_SemTryWait TryWait;
  44. pfnSDL_SemWait Wait;
  45. pfnSDL_SemValue Value;
  46. pfnSDL_SemPost Post;
  47. } SDL_sem_impl_t;
  48. /* Implementation will be chosen at runtime based on available Kernel features */
  49. static SDL_sem_impl_t SDL_sem_impl_active = {0};
  50. /**
  51. * Atomic + WaitOnAddress implementation
  52. */
  53. /* APIs not available on WinPhone 8.1 */
  54. /* https://www.microsoft.com/en-us/download/details.aspx?id=47328 */
  55. #if (HAVE_WINAPIFAMILY_H) && defined(WINAPI_FAMILY_PHONE_APP)
  56. #define SDL_WINAPI_FAMILY_PHONE (WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP)
  57. #else
  58. #define SDL_WINAPI_FAMILY_PHONE 0
  59. #endif
  60. #if !SDL_WINAPI_FAMILY_PHONE
  61. #if __WINRT__
  62. /* Functions are guaranteed to be available */
  63. #define pWaitOnAddress WaitOnAddress
  64. #define pWakeByAddressSingle WakeByAddressSingle
  65. #else
  66. typedef BOOL(WINAPI *pfnWaitOnAddress)(volatile VOID*, PVOID, SIZE_T, DWORD);
  67. typedef VOID(WINAPI *pfnWakeByAddressSingle)(PVOID);
  68. static pfnWaitOnAddress pWaitOnAddress = NULL;
  69. static pfnWakeByAddressSingle pWakeByAddressSingle = NULL;
  70. #endif
  71. typedef struct SDL_semaphore_atom
  72. {
  73. LONG count;
  74. } SDL_sem_atom;
  75. static SDL_sem *
  76. SDL_CreateSemaphore_atom(Uint32 initial_value)
  77. {
  78. SDL_sem_atom *sem;
  79. sem = (SDL_sem_atom *) SDL_malloc(sizeof(*sem));
  80. if (sem != NULL) {
  81. sem->count = initial_value;
  82. } else {
  83. SDL_OutOfMemory();
  84. }
  85. return (SDL_sem *)sem;
  86. }
  87. static void
  88. SDL_DestroySemaphore_atom(SDL_sem * sem)
  89. {
  90. if (sem != NULL) {
  91. SDL_free(sem);
  92. }
  93. }
  94. static int
  95. SDL_SemTryWait_atom(SDL_sem * _sem)
  96. {
  97. SDL_sem_atom *sem = (SDL_sem_atom *)_sem;
  98. LONG count;
  99. if (sem == NULL) {
  100. return SDL_InvalidParamError("sem");
  101. }
  102. count = sem->count;
  103. if (count == 0) {
  104. return SDL_MUTEX_TIMEDOUT;
  105. }
  106. if (InterlockedCompareExchange(&sem->count, count - 1, count) == count) {
  107. return 0;
  108. }
  109. return SDL_MUTEX_TIMEDOUT;
  110. }
  111. static int
  112. SDL_SemWait_atom(SDL_sem * _sem)
  113. {
  114. SDL_sem_atom *sem = (SDL_sem_atom *)_sem;
  115. LONG count;
  116. if (sem == NULL) {
  117. return SDL_InvalidParamError("sem");
  118. }
  119. for (;;) {
  120. count = sem->count;
  121. while (count == 0) {
  122. if (pWaitOnAddress(&sem->count, &count, sizeof(sem->count), INFINITE) == FALSE) {
  123. return SDL_SetError("WaitOnAddress() failed");
  124. }
  125. count = sem->count;
  126. }
  127. if (InterlockedCompareExchange(&sem->count, count - 1, count) == count) {
  128. return 0;
  129. }
  130. }
  131. }
  132. static int
  133. SDL_SemWaitTimeout_atom(SDL_sem * _sem, Uint32 timeout)
  134. {
  135. SDL_sem_atom *sem = (SDL_sem_atom *)_sem;
  136. LONG count;
  137. Uint32 now;
  138. Uint32 deadline;
  139. DWORD timeout_eff;
  140. if (timeout == SDL_MUTEX_MAXWAIT) {
  141. return SDL_SemWait_atom(_sem);
  142. }
  143. if (sem == NULL) {
  144. return SDL_InvalidParamError("sem");
  145. }
  146. /**
  147. * WaitOnAddress is subject to spurious and stolen wakeups so we
  148. * need to recalculate the effective timeout before every wait
  149. */
  150. now = SDL_GetTicks();
  151. deadline = now + (DWORD) timeout;
  152. for (;;) {
  153. count = sem->count;
  154. /* If no semaphore is available we need to wait */
  155. while (count == 0) {
  156. now = SDL_GetTicks();
  157. if (deadline > now) {
  158. timeout_eff = deadline - now;
  159. } else {
  160. return SDL_MUTEX_TIMEDOUT;
  161. }
  162. if (pWaitOnAddress(&sem->count, &count, sizeof(count), timeout_eff) == FALSE) {
  163. if (GetLastError() == ERROR_TIMEOUT) {
  164. return SDL_MUTEX_TIMEDOUT;
  165. }
  166. return SDL_SetError("WaitOnAddress() failed");
  167. }
  168. count = sem->count;
  169. }
  170. /* Actually the semaphore is only consumed if this succeeds */
  171. /* If it doesn't we need to do everything again */
  172. if (InterlockedCompareExchange(&sem->count, count - 1, count) == count) {
  173. return 0;
  174. }
  175. }
  176. }
  177. static Uint32
  178. SDL_SemValue_atom(SDL_sem * _sem)
  179. {
  180. SDL_sem_atom *sem = (SDL_sem_atom *)_sem;
  181. if (sem == NULL) {
  182. SDL_InvalidParamError("sem");
  183. return 0;
  184. }
  185. return (Uint32)sem->count;
  186. }
  187. static int
  188. SDL_SemPost_atom(SDL_sem * _sem)
  189. {
  190. SDL_sem_atom *sem = (SDL_sem_atom *)_sem;
  191. if (sem == NULL) {
  192. return SDL_InvalidParamError("sem");
  193. }
  194. InterlockedIncrement(&sem->count);
  195. pWakeByAddressSingle(&sem->count);
  196. return 0;
  197. }
  198. static const SDL_sem_impl_t SDL_sem_impl_atom =
  199. {
  200. &SDL_CreateSemaphore_atom,
  201. &SDL_DestroySemaphore_atom,
  202. &SDL_SemWaitTimeout_atom,
  203. &SDL_SemTryWait_atom,
  204. &SDL_SemWait_atom,
  205. &SDL_SemValue_atom,
  206. &SDL_SemPost_atom,
  207. };
  208. #endif /* !SDL_WINAPI_FAMILY_PHONE */
  209. /**
  210. * Fallback Semaphore implementation using Kernel Semaphores
  211. */
  212. typedef struct SDL_semaphore_kern
  213. {
  214. HANDLE id;
  215. LONG count;
  216. } SDL_sem_kern;
  217. /* Create a semaphore */
  218. static SDL_sem *
  219. SDL_CreateSemaphore_kern(Uint32 initial_value)
  220. {
  221. SDL_sem_kern *sem;
  222. /* Allocate sem memory */
  223. sem = (SDL_sem_kern *) SDL_malloc(sizeof(*sem));
  224. if (sem != NULL) {
  225. /* Create the semaphore, with max value 32K */
  226. #if __WINRT__
  227. sem->id = CreateSemaphoreEx(NULL, initial_value, 32 * 1024, NULL, 0, SEMAPHORE_ALL_ACCESS);
  228. #else
  229. sem->id = CreateSemaphore(NULL, initial_value, 32 * 1024, NULL);
  230. #endif
  231. sem->count = initial_value;
  232. if (!sem->id) {
  233. SDL_SetError("Couldn't create semaphore");
  234. SDL_free(sem);
  235. sem = NULL;
  236. }
  237. } else {
  238. SDL_OutOfMemory();
  239. }
  240. return (SDL_sem *)sem;
  241. }
  242. /* Free the semaphore */
  243. static void
  244. SDL_DestroySemaphore_kern(SDL_sem * _sem)
  245. {
  246. SDL_sem_kern *sem = (SDL_sem_kern *)_sem;
  247. if (sem != NULL) {
  248. if (sem->id) {
  249. CloseHandle(sem->id);
  250. sem->id = 0;
  251. }
  252. SDL_free(sem);
  253. }
  254. }
  255. static int
  256. SDL_SemWaitTimeout_kern(SDL_sem * _sem, Uint32 timeout)
  257. {
  258. SDL_sem_kern *sem = (SDL_sem_kern *)_sem;
  259. int retval;
  260. DWORD dwMilliseconds;
  261. if (sem == NULL) {
  262. return SDL_InvalidParamError("sem");
  263. }
  264. if (timeout == SDL_MUTEX_MAXWAIT) {
  265. dwMilliseconds = INFINITE;
  266. } else {
  267. dwMilliseconds = (DWORD) timeout;
  268. }
  269. switch (WaitForSingleObjectEx(sem->id, dwMilliseconds, FALSE)) {
  270. case WAIT_OBJECT_0:
  271. InterlockedDecrement(&sem->count);
  272. retval = 0;
  273. break;
  274. case WAIT_TIMEOUT:
  275. retval = SDL_MUTEX_TIMEDOUT;
  276. break;
  277. default:
  278. retval = SDL_SetError("WaitForSingleObject() failed");
  279. break;
  280. }
  281. return retval;
  282. }
  283. static int
  284. SDL_SemTryWait_kern(SDL_sem * sem)
  285. {
  286. return SDL_SemWaitTimeout_kern(sem, 0);
  287. }
  288. static int
  289. SDL_SemWait_kern(SDL_sem * sem)
  290. {
  291. return SDL_SemWaitTimeout_kern(sem, SDL_MUTEX_MAXWAIT);
  292. }
  293. /* Returns the current count of the semaphore */
  294. static Uint32
  295. SDL_SemValue_kern(SDL_sem * _sem)
  296. {
  297. SDL_sem_kern *sem = (SDL_sem_kern *)_sem;
  298. if (sem == NULL) {
  299. SDL_InvalidParamError("sem");
  300. return 0;
  301. }
  302. return (Uint32)sem->count;
  303. }
  304. static int
  305. SDL_SemPost_kern(SDL_sem * _sem)
  306. {
  307. SDL_sem_kern *sem = (SDL_sem_kern *)_sem;
  308. if (sem == NULL) {
  309. return SDL_InvalidParamError("sem");
  310. }
  311. /* Increase the counter in the first place, because
  312. * after a successful release the semaphore may
  313. * immediately get destroyed by another thread which
  314. * is waiting for this semaphore.
  315. */
  316. InterlockedIncrement(&sem->count);
  317. if (ReleaseSemaphore(sem->id, 1, NULL) == FALSE) {
  318. InterlockedDecrement(&sem->count); /* restore */
  319. return SDL_SetError("ReleaseSemaphore() failed");
  320. }
  321. return 0;
  322. }
  323. static const SDL_sem_impl_t SDL_sem_impl_kern =
  324. {
  325. &SDL_CreateSemaphore_kern,
  326. &SDL_DestroySemaphore_kern,
  327. &SDL_SemWaitTimeout_kern,
  328. &SDL_SemTryWait_kern,
  329. &SDL_SemWait_kern,
  330. &SDL_SemValue_kern,
  331. &SDL_SemPost_kern,
  332. };
  333. /**
  334. * Runtime selection and redirection
  335. */
  336. SDL_sem *
  337. SDL_CreateSemaphore(Uint32 initial_value)
  338. {
  339. if (SDL_sem_impl_active.Create == NULL) {
  340. /* Default to fallback implementation */
  341. const SDL_sem_impl_t * impl = &SDL_sem_impl_kern;
  342. #if !SDL_WINAPI_FAMILY_PHONE
  343. if (!SDL_GetHintBoolean(SDL_HINT_WINDOWS_FORCE_SEMAPHORE_KERNEL, SDL_FALSE)) {
  344. #if __WINRT__
  345. /* Link statically on this platform */
  346. impl = &SDL_sem_impl_atom;
  347. #else
  348. /* We already statically link to features from this Api
  349. * Set (e.g. WaitForSingleObject). Dynamically loading
  350. * API Sets is not explicitly documented but according to
  351. * Microsoft our specific use case is legal and correct:
  352. * https://github.com/microsoft/STL/pull/593#issuecomment-655799859
  353. */
  354. HMODULE synch120 = GetModuleHandle(TEXT("api-ms-win-core-synch-l1-2-0.dll"));
  355. if (synch120) {
  356. /* Try to load required functions provided by Win 8 or newer */
  357. pWaitOnAddress = (pfnWaitOnAddress) GetProcAddress(synch120, "WaitOnAddress");
  358. pWakeByAddressSingle = (pfnWakeByAddressSingle) GetProcAddress(synch120, "WakeByAddressSingle");
  359. if (pWaitOnAddress && pWakeByAddressSingle) {
  360. impl = &SDL_sem_impl_atom;
  361. }
  362. }
  363. #endif
  364. }
  365. #endif
  366. /* Copy instead of using pointer to save one level of indirection */
  367. SDL_copyp(&SDL_sem_impl_active, impl);
  368. }
  369. return SDL_sem_impl_active.Create(initial_value);
  370. }
  371. void
  372. SDL_DestroySemaphore(SDL_sem * sem)
  373. {
  374. SDL_sem_impl_active.Destroy(sem);
  375. }
  376. int
  377. SDL_SemWaitTimeout(SDL_sem * sem, Uint32 timeout)
  378. {
  379. return SDL_sem_impl_active.WaitTimeout(sem, timeout);
  380. }
  381. int
  382. SDL_SemTryWait(SDL_sem * sem)
  383. {
  384. return SDL_sem_impl_active.TryWait(sem);
  385. }
  386. int
  387. SDL_SemWait(SDL_sem * sem)
  388. {
  389. return SDL_sem_impl_active.Wait(sem);
  390. }
  391. Uint32
  392. SDL_SemValue(SDL_sem * sem)
  393. {
  394. return SDL_sem_impl_active.Value(sem);
  395. }
  396. int
  397. SDL_SemPost(SDL_sem * sem)
  398. {
  399. return SDL_sem_impl_active.Post(sem);
  400. }
  401. #endif /* SDL_THREAD_WINDOWS */
  402. /* vi: set ts=4 sw=4 expandtab: */