threads_windows.c 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. /*
  2. * libusb synchronization on Microsoft Windows
  3. *
  4. * Copyright (C) 2010 Michael Plante <michael.plante@gmail.com>
  5. *
  6. * This library is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 2.1 of the License, or (at your option) any later version.
  10. *
  11. * This library is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public
  17. * License along with this library; if not, write to the Free Software
  18. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  19. */
  20. #include <config.h>
  21. #include <objbase.h>
  22. #include <errno.h>
  23. #include <stdarg.h>
  24. #include "libusbi.h"
  25. int usbi_mutex_init(usbi_mutex_t *mutex,
  26. const usbi_mutexattr_t *attr) {
  27. if(! mutex) return ((errno=EINVAL));
  28. *mutex = CreateMutex(NULL, FALSE, NULL);
  29. if(!*mutex) return ((errno=ENOMEM));
  30. return 0;
  31. }
  32. int usbi_mutex_destroy(usbi_mutex_t *mutex) {
  33. // It is not clear if CloseHandle failure is due to failure to unlock.
  34. // If so, this should be errno=EBUSY.
  35. if(!mutex || !CloseHandle(*mutex)) return ((errno=EINVAL));
  36. *mutex = NULL;
  37. return 0;
  38. }
  39. int usbi_mutex_trylock(usbi_mutex_t *mutex) {
  40. DWORD result;
  41. if(!mutex) return ((errno=EINVAL));
  42. result = WaitForSingleObject(*mutex, 0);
  43. if(result == WAIT_OBJECT_0 || result == WAIT_ABANDONED)
  44. return 0; // acquired (ToDo: check that abandoned is ok)
  45. if(result == WAIT_TIMEOUT)
  46. return ((errno=EBUSY));
  47. return ((errno=EINVAL)); // don't know how this would happen
  48. // so don't know proper errno
  49. }
  50. int usbi_mutex_lock(usbi_mutex_t *mutex) {
  51. DWORD result;
  52. if(!mutex) return ((errno=EINVAL));
  53. result = WaitForSingleObject(*mutex, INFINITE);
  54. if(result == WAIT_OBJECT_0 || result == WAIT_ABANDONED)
  55. return 0; // acquired (ToDo: check that abandoned is ok)
  56. return ((errno=EINVAL)); // don't know how this would happen
  57. // so don't know proper errno
  58. }
  59. int usbi_mutex_unlock(usbi_mutex_t *mutex) {
  60. if(!mutex) return ((errno=EINVAL));
  61. if(!ReleaseMutex(*mutex)) return ((errno=EPERM ));
  62. return 0;
  63. }
  64. int usbi_mutex_static_lock(usbi_mutex_static_t *mutex) {
  65. if(!mutex) return ((errno=EINVAL));
  66. while (InterlockedExchange((LONG *)mutex, 1) == 1) {
  67. SleepEx(0, TRUE);
  68. }
  69. return 0;
  70. }
  71. int usbi_mutex_static_unlock(usbi_mutex_static_t *mutex) {
  72. if(!mutex) return ((errno=EINVAL));
  73. *mutex = 0;
  74. return 0;
  75. }
  76. int usbi_cond_init(usbi_cond_t *cond,
  77. const usbi_condattr_t *attr) {
  78. if(!cond) return ((errno=EINVAL));
  79. list_init(&cond->waiters );
  80. list_init(&cond->not_waiting);
  81. return 0;
  82. }
  83. int usbi_cond_destroy(usbi_cond_t *cond) {
  84. // This assumes no one is using this anymore. The check MAY NOT BE safe.
  85. struct usbi_cond_perthread *pos, *prev_pos = NULL;
  86. if(!cond) return ((errno=EINVAL));
  87. if(!list_empty(&cond->waiters)) return ((errno=EBUSY )); // (!see above!)
  88. list_for_each_entry(pos, &cond->not_waiting, list, struct usbi_cond_perthread) {
  89. free(prev_pos);
  90. CloseHandle(pos->event);
  91. list_del(&pos->list);
  92. prev_pos = pos;
  93. }
  94. free(prev_pos);
  95. return 0;
  96. }
  97. int usbi_cond_broadcast(usbi_cond_t *cond) {
  98. // Assumes mutex is locked; this is not in keeping with POSIX spec, but
  99. // libusb does this anyway, so we simplify by not adding more sync
  100. // primitives to the CV definition!
  101. int fail = 0;
  102. struct usbi_cond_perthread *pos;
  103. if(!cond) return ((errno=EINVAL));
  104. list_for_each_entry(pos, &cond->waiters, list, struct usbi_cond_perthread) {
  105. if(!SetEvent(pos->event))
  106. fail = 1;
  107. }
  108. // The wait function will remove its respective item from the list.
  109. return fail ? ((errno=EINVAL)) : 0;
  110. }
  111. int usbi_cond_signal(usbi_cond_t *cond) {
  112. // Assumes mutex is locked; this is not in keeping with POSIX spec, but
  113. // libusb does this anyway, so we simplify by not adding more sync
  114. // primitives to the CV definition!
  115. struct usbi_cond_perthread *pos;
  116. if(!cond) return ((errno=EINVAL));
  117. if(list_empty(&cond->waiters)) return 0; // no one to wakeup.
  118. pos = list_entry(&cond->waiters.next, struct usbi_cond_perthread, list);
  119. // The wait function will remove its respective item from the list.
  120. return SetEvent(pos->event) ? 0 : ((errno=EINVAL));
  121. }
  122. static int __inline usbi_cond_intwait(usbi_cond_t *cond,
  123. usbi_mutex_t *mutex,
  124. DWORD timeout_ms) {
  125. struct usbi_cond_perthread *pos;
  126. int found = 0, r;
  127. DWORD r2,tid = GetCurrentThreadId();
  128. if(!cond || !mutex) return ((errno=EINVAL));
  129. list_for_each_entry(pos, &cond->not_waiting, list, struct usbi_cond_perthread) {
  130. if(tid == pos->tid) {
  131. found = 1;
  132. break;
  133. }
  134. }
  135. if(!found) {
  136. pos = (struct usbi_cond_perthread*) calloc(1, sizeof(struct usbi_cond_perthread));
  137. if(!pos) return ((errno=ENOMEM)); // This errno is not POSIX-allowed.
  138. pos->tid = tid;
  139. pos->event = CreateEvent(NULL, FALSE, FALSE, NULL); // auto-reset.
  140. if(!pos->event) {
  141. free(pos);
  142. return ((errno=ENOMEM));
  143. }
  144. list_add(&pos->list, &cond->not_waiting);
  145. }
  146. list_del(&pos->list); // remove from not_waiting list.
  147. list_add(&pos->list, &cond->waiters);
  148. r = usbi_mutex_unlock(mutex);
  149. if(r) return r;
  150. r2 = WaitForSingleObject(pos->event, timeout_ms);
  151. r = usbi_mutex_lock(mutex);
  152. if(r) return r;
  153. list_del(&pos->list);
  154. list_add(&pos->list, &cond->not_waiting);
  155. if(r2 == WAIT_TIMEOUT) return ((errno=ETIMEDOUT));
  156. return 0;
  157. }
  158. // N.B.: usbi_cond_*wait() can also return ENOMEM, even though pthread_cond_*wait cannot!
  159. int usbi_cond_wait(usbi_cond_t *cond, usbi_mutex_t *mutex) {
  160. return usbi_cond_intwait(cond, mutex, INFINITE);
  161. }
  162. int usbi_cond_timedwait(usbi_cond_t *cond,
  163. usbi_mutex_t *mutex,
  164. const struct timespec *abstime) {
  165. FILETIME filetime;
  166. ULARGE_INTEGER rtime;
  167. struct timeval targ_time, cur_time, delta_time;
  168. struct timespec cur_time_ns;
  169. DWORD millis;
  170. extern const uint64_t epoch_time;
  171. GetSystemTimeAsFileTime(&filetime);
  172. rtime.LowPart = filetime.dwLowDateTime;
  173. rtime.HighPart = filetime.dwHighDateTime;
  174. rtime.QuadPart -= epoch_time;
  175. cur_time_ns.tv_sec = (long)(rtime.QuadPart / 10000000);
  176. cur_time_ns.tv_nsec = (long)((rtime.QuadPart % 10000000)*100);
  177. TIMESPEC_TO_TIMEVAL(&cur_time, &cur_time_ns);
  178. TIMESPEC_TO_TIMEVAL(&targ_time, abstime);
  179. timersub(&targ_time, &cur_time, &delta_time);
  180. if(delta_time.tv_sec < 0) // abstime already passed?
  181. millis = 0;
  182. else {
  183. millis = delta_time.tv_usec/1000;
  184. millis += delta_time.tv_sec *1000;
  185. if (delta_time.tv_usec % 1000) // round up to next millisecond
  186. millis++;
  187. }
  188. return usbi_cond_intwait(cond, mutex, millis);
  189. }