evthread_win32.c 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. /*
  2. * Copyright 2009-2012 Niels Provos and Nick Mathewson
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions
  6. * are met:
  7. * 1. Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * 2. Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * 3. The name of the author may not be used to endorse or promote products
  13. * derived from this software without specific prior written permission.
  14. *
  15. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  16. * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  17. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  18. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  19. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  20. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  21. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  22. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  23. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  24. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. */
  26. #include "event2/event-config.h"
  27. #include "evconfig-private.h"
  28. #ifdef _WIN32
  29. #ifndef _WIN32_WINNT
  30. /* Minimum required for InitializeCriticalSectionAndSpinCount */
  31. #define _WIN32_WINNT 0x0403
  32. #endif
  33. #include <winsock2.h>
  34. #define WIN32_LEAN_AND_MEAN
  35. #include <windows.h>
  36. #undef WIN32_LEAN_AND_MEAN
  37. #include <sys/locking.h>
  38. #endif
  39. struct event_base;
  40. #include "event2/thread.h"
  41. #include "mm-internal.h"
  42. #include "evthread-internal.h"
  43. #include "time-internal.h"
  44. #define SPIN_COUNT 2000
  45. static void *
  46. evthread_win32_lock_create(unsigned locktype)
  47. {
  48. CRITICAL_SECTION *lock = mm_malloc(sizeof(CRITICAL_SECTION));
  49. if (!lock)
  50. return NULL;
  51. if (InitializeCriticalSectionAndSpinCount(lock, SPIN_COUNT) == 0) {
  52. mm_free(lock);
  53. return NULL;
  54. }
  55. return lock;
  56. }
  57. static void
  58. evthread_win32_lock_free(void *lock_, unsigned locktype)
  59. {
  60. CRITICAL_SECTION *lock = lock_;
  61. DeleteCriticalSection(lock);
  62. mm_free(lock);
  63. }
  64. static int
  65. evthread_win32_lock(unsigned mode, void *lock_)
  66. {
  67. CRITICAL_SECTION *lock = lock_;
  68. if ((mode & EVTHREAD_TRY)) {
  69. return ! TryEnterCriticalSection(lock);
  70. } else {
  71. EnterCriticalSection(lock);
  72. return 0;
  73. }
  74. }
  75. static int
  76. evthread_win32_unlock(unsigned mode, void *lock_)
  77. {
  78. CRITICAL_SECTION *lock = lock_;
  79. LeaveCriticalSection(lock);
  80. return 0;
  81. }
  82. static unsigned long
  83. evthread_win32_get_id(void)
  84. {
  85. return (unsigned long) GetCurrentThreadId();
  86. }
  87. #ifdef WIN32_HAVE_CONDITION_VARIABLES
  88. static void WINAPI (*InitializeConditionVariable_fn)(PCONDITION_VARIABLE)
  89. = NULL;
  90. static BOOL WINAPI (*SleepConditionVariableCS_fn)(
  91. PCONDITION_VARIABLE, PCRITICAL_SECTION, DWORD) = NULL;
  92. static void WINAPI (*WakeAllConditionVariable_fn)(PCONDITION_VARIABLE) = NULL;
  93. static void WINAPI (*WakeConditionVariable_fn)(PCONDITION_VARIABLE) = NULL;
  94. static int
  95. evthread_win32_condvar_init(void)
  96. {
  97. HANDLE lib;
  98. lib = GetModuleHandle(TEXT("kernel32.dll"));
  99. if (lib == NULL)
  100. return 0;
  101. #define LOAD(name) \
  102. name##_fn = GetProcAddress(lib, #name)
  103. LOAD(InitializeConditionVariable);
  104. LOAD(SleepConditionVariableCS);
  105. LOAD(WakeAllConditionVariable);
  106. LOAD(WakeConditionVariable);
  107. return InitializeConditionVariable_fn && SleepConditionVariableCS_fn &&
  108. WakeAllConditionVariable_fn && WakeConditionVariable_fn;
  109. }
  110. /* XXXX Even if we can build this, we don't necessarily want to: the functions
  111. * in question didn't exist before Vista, so we'd better LoadProc them. */
  112. static void *
  113. evthread_win32_condvar_alloc(unsigned condflags)
  114. {
  115. CONDITION_VARIABLE *cond = mm_malloc(sizeof(CONDITION_VARIABLE));
  116. if (!cond)
  117. return NULL;
  118. InitializeConditionVariable_fn(cond);
  119. return cond;
  120. }
  121. static void
  122. evthread_win32_condvar_free(void *cond_)
  123. {
  124. CONDITION_VARIABLE *cond = cond_;
  125. /* There doesn't _seem_ to be a cleaup fn here... */
  126. mm_free(cond);
  127. }
  128. static int
  129. evthread_win32_condvar_signal(void *cond, int broadcast)
  130. {
  131. CONDITION_VARIABLE *cond = cond_;
  132. if (broadcast)
  133. WakeAllConditionVariable_fn(cond);
  134. else
  135. WakeConditionVariable_fn(cond);
  136. return 0;
  137. }
  138. static int
  139. evthread_win32_condvar_wait(void *cond_, void *lock_, const struct timeval *tv)
  140. {
  141. CONDITION_VARIABLE *cond = cond_;
  142. CRITICAL_SECTION *lock = lock_;
  143. DWORD ms, err;
  144. BOOL result;
  145. if (tv)
  146. ms = evutil_tv_to_msec_(tv);
  147. else
  148. ms = INFINITE;
  149. result = SleepConditionVariableCS_fn(cond, lock, ms);
  150. if (result) {
  151. if (GetLastError() == WAIT_TIMEOUT)
  152. return 1;
  153. else
  154. return -1;
  155. } else {
  156. return 0;
  157. }
  158. }
  159. #endif
  160. struct evthread_win32_cond {
  161. HANDLE event;
  162. CRITICAL_SECTION lock;
  163. int n_waiting;
  164. int n_to_wake;
  165. int generation;
  166. };
  167. static void *
  168. evthread_win32_cond_alloc(unsigned flags)
  169. {
  170. struct evthread_win32_cond *cond;
  171. if (!(cond = mm_malloc(sizeof(struct evthread_win32_cond))))
  172. return NULL;
  173. if (InitializeCriticalSectionAndSpinCount(&cond->lock, SPIN_COUNT)==0) {
  174. mm_free(cond);
  175. return NULL;
  176. }
  177. if ((cond->event = CreateEvent(NULL,TRUE,FALSE,NULL)) == NULL) {
  178. DeleteCriticalSection(&cond->lock);
  179. mm_free(cond);
  180. return NULL;
  181. }
  182. cond->n_waiting = cond->n_to_wake = cond->generation = 0;
  183. return cond;
  184. }
  185. static void
  186. evthread_win32_cond_free(void *cond_)
  187. {
  188. struct evthread_win32_cond *cond = cond_;
  189. DeleteCriticalSection(&cond->lock);
  190. CloseHandle(cond->event);
  191. mm_free(cond);
  192. }
  193. static int
  194. evthread_win32_cond_signal(void *cond_, int broadcast)
  195. {
  196. struct evthread_win32_cond *cond = cond_;
  197. EnterCriticalSection(&cond->lock);
  198. if (broadcast)
  199. cond->n_to_wake = cond->n_waiting;
  200. else
  201. ++cond->n_to_wake;
  202. cond->generation++;
  203. SetEvent(cond->event);
  204. LeaveCriticalSection(&cond->lock);
  205. return 0;
  206. }
  207. static int
  208. evthread_win32_cond_wait(void *cond_, void *lock_, const struct timeval *tv)
  209. {
  210. struct evthread_win32_cond *cond = cond_;
  211. CRITICAL_SECTION *lock = lock_;
  212. int generation_at_start;
  213. int waiting = 1;
  214. int result = -1;
  215. DWORD ms = INFINITE, ms_orig = INFINITE, startTime, endTime;
  216. if (tv)
  217. ms_orig = ms = evutil_tv_to_msec_(tv);
  218. EnterCriticalSection(&cond->lock);
  219. ++cond->n_waiting;
  220. generation_at_start = cond->generation;
  221. LeaveCriticalSection(&cond->lock);
  222. LeaveCriticalSection(lock);
  223. startTime = GetTickCount();
  224. do {
  225. DWORD res;
  226. res = WaitForSingleObject(cond->event, ms);
  227. EnterCriticalSection(&cond->lock);
  228. if (cond->n_to_wake &&
  229. cond->generation != generation_at_start) {
  230. --cond->n_to_wake;
  231. --cond->n_waiting;
  232. result = 0;
  233. waiting = 0;
  234. goto out;
  235. } else if (res != WAIT_OBJECT_0) {
  236. result = (res==WAIT_TIMEOUT) ? 1 : -1;
  237. --cond->n_waiting;
  238. waiting = 0;
  239. goto out;
  240. } else if (ms != INFINITE) {
  241. endTime = GetTickCount();
  242. if (startTime + ms_orig <= endTime) {
  243. result = 1; /* Timeout */
  244. --cond->n_waiting;
  245. waiting = 0;
  246. goto out;
  247. } else {
  248. ms = startTime + ms_orig - endTime;
  249. }
  250. }
  251. /* If we make it here, we are still waiting. */
  252. if (cond->n_to_wake == 0) {
  253. /* There is nobody else who should wake up; reset
  254. * the event. */
  255. ResetEvent(cond->event);
  256. }
  257. out:
  258. LeaveCriticalSection(&cond->lock);
  259. } while (waiting);
  260. EnterCriticalSection(lock);
  261. EnterCriticalSection(&cond->lock);
  262. if (!cond->n_waiting)
  263. ResetEvent(cond->event);
  264. LeaveCriticalSection(&cond->lock);
  265. return result;
  266. }
  267. int
  268. evthread_use_windows_threads(void)
  269. {
  270. struct evthread_lock_callbacks cbs = {
  271. EVTHREAD_LOCK_API_VERSION,
  272. EVTHREAD_LOCKTYPE_RECURSIVE,
  273. evthread_win32_lock_create,
  274. evthread_win32_lock_free,
  275. evthread_win32_lock,
  276. evthread_win32_unlock
  277. };
  278. struct evthread_condition_callbacks cond_cbs = {
  279. EVTHREAD_CONDITION_API_VERSION,
  280. evthread_win32_cond_alloc,
  281. evthread_win32_cond_free,
  282. evthread_win32_cond_signal,
  283. evthread_win32_cond_wait
  284. };
  285. #ifdef WIN32_HAVE_CONDITION_VARIABLES
  286. struct evthread_condition_callbacks condvar_cbs = {
  287. EVTHREAD_CONDITION_API_VERSION,
  288. evthread_win32_condvar_alloc,
  289. evthread_win32_condvar_free,
  290. evthread_win32_condvar_signal,
  291. evthread_win32_condvar_wait
  292. };
  293. #endif
  294. evthread_set_lock_callbacks(&cbs);
  295. evthread_set_id_callback(evthread_win32_get_id);
  296. #ifdef WIN32_HAVE_CONDITION_VARIABLES
  297. if (evthread_win32_condvar_init()) {
  298. evthread_set_condition_callbacks(&condvar_cbs);
  299. return 0;
  300. }
  301. #endif
  302. evthread_set_condition_callbacks(&cond_cbs);
  303. return 0;
  304. }