• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * libusbx synchronization on Microsoft Windows
3  *
4  * Copyright © 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 
21 #include <config.h>
22 #include <objbase.h>
23 #include <errno.h>
24 #include <stdarg.h>
25 
26 #include "libusbi.h"
27 
28 extern const uint64_t epoch_time;
29 
usbi_mutex_init(usbi_mutex_t * mutex,const usbi_mutexattr_t * attr)30 int usbi_mutex_init(usbi_mutex_t *mutex,
31 					const usbi_mutexattr_t *attr) {
32 	UNUSED(attr);
33 	if(! mutex) return ((errno=EINVAL));
34 	*mutex = CreateMutex(NULL, FALSE, NULL);
35 	if(!*mutex) return ((errno=ENOMEM));
36 	return 0;
37 }
usbi_mutex_destroy(usbi_mutex_t * mutex)38 int usbi_mutex_destroy(usbi_mutex_t *mutex) {
39 	// It is not clear if CloseHandle failure is due to failure to unlock.
40 	//   If so, this should be errno=EBUSY.
41 	if(!mutex || !CloseHandle(*mutex)) return ((errno=EINVAL));
42 	*mutex = NULL;
43 	return 0;
44 }
usbi_mutex_trylock(usbi_mutex_t * mutex)45 int usbi_mutex_trylock(usbi_mutex_t *mutex) {
46 	DWORD result;
47 	if(!mutex) return ((errno=EINVAL));
48 	result = WaitForSingleObject(*mutex, 0);
49 	if(result == WAIT_OBJECT_0 || result == WAIT_ABANDONED)
50 		return 0; // acquired (ToDo: check that abandoned is ok)
51 	if(result == WAIT_TIMEOUT)
52 		return ((errno=EBUSY));
53 	return ((errno=EINVAL)); // don't know how this would happen
54 							 //   so don't know proper errno
55 }
usbi_mutex_lock(usbi_mutex_t * mutex)56 int usbi_mutex_lock(usbi_mutex_t *mutex) {
57 	DWORD result;
58 	if(!mutex) return ((errno=EINVAL));
59 	result = WaitForSingleObject(*mutex, INFINITE);
60 	if(result == WAIT_OBJECT_0 || result == WAIT_ABANDONED)
61 		return 0; // acquired (ToDo: check that abandoned is ok)
62 	return ((errno=EINVAL)); // don't know how this would happen
63 							 //   so don't know proper errno
64 }
usbi_mutex_unlock(usbi_mutex_t * mutex)65 int usbi_mutex_unlock(usbi_mutex_t *mutex) {
66 	if(!mutex)                return ((errno=EINVAL));
67 	if(!ReleaseMutex(*mutex)) return ((errno=EPERM ));
68 	return 0;
69 }
70 
usbi_mutex_static_lock(usbi_mutex_static_t * mutex)71 int usbi_mutex_static_lock(usbi_mutex_static_t *mutex) {
72 	if(!mutex)               return ((errno=EINVAL));
73 	while (InterlockedExchange((LONG *)mutex, 1) == 1) {
74 		SleepEx(0, TRUE);
75 	}
76 	return 0;
77 }
usbi_mutex_static_unlock(usbi_mutex_static_t * mutex)78 int usbi_mutex_static_unlock(usbi_mutex_static_t *mutex) {
79 	if(!mutex)               return ((errno=EINVAL));
80 	*mutex = 0;
81 	return 0;
82 }
83 
usbi_cond_init(usbi_cond_t * cond,const usbi_condattr_t * attr)84 int usbi_cond_init(usbi_cond_t *cond,
85 				   const usbi_condattr_t *attr) {
86 	UNUSED(attr);
87 	if(!cond)           return ((errno=EINVAL));
88 	list_init(&cond->waiters    );
89 	list_init(&cond->not_waiting);
90 	return 0;
91 }
usbi_cond_destroy(usbi_cond_t * cond)92 int usbi_cond_destroy(usbi_cond_t *cond) {
93 	// This assumes no one is using this anymore.  The check MAY NOT BE safe.
94 	struct usbi_cond_perthread *pos, *next_pos = NULL;
95 	if(!cond) return ((errno=EINVAL));
96 	if(!list_empty(&cond->waiters)) return ((errno=EBUSY )); // (!see above!)
97 	list_for_each_entry_safe(pos, next_pos, &cond->not_waiting, list, struct usbi_cond_perthread) {
98 		CloseHandle(pos->event);
99 		list_del(&pos->list);
100 		free(pos);
101 	}
102 
103 	return 0;
104 }
105 
usbi_cond_broadcast(usbi_cond_t * cond)106 int usbi_cond_broadcast(usbi_cond_t *cond) {
107 	// Assumes mutex is locked; this is not in keeping with POSIX spec, but
108 	//   libusb does this anyway, so we simplify by not adding more sync
109 	//   primitives to the CV definition!
110 	int fail = 0;
111 	struct usbi_cond_perthread *pos;
112 	if(!cond)                      return ((errno=EINVAL));
113 	list_for_each_entry(pos, &cond->waiters, list, struct usbi_cond_perthread) {
114 		if(!SetEvent(pos->event))
115 			fail = 1;
116 	}
117 	// The wait function will remove its respective item from the list.
118 	return fail ? ((errno=EINVAL)) : 0;
119 }
usbi_cond_signal(usbi_cond_t * cond)120 int usbi_cond_signal(usbi_cond_t *cond) {
121 	// Assumes mutex is locked; this is not in keeping with POSIX spec, but
122 	//   libusb does this anyway, so we simplify by not adding more sync
123 	//   primitives to the CV definition!
124 	struct usbi_cond_perthread *pos;
125 	if(!cond)                      return ((errno=EINVAL));
126 	if(list_empty(&cond->waiters)) return 0; // no one to wakeup.
127 	pos = list_entry(&cond->waiters.next, struct usbi_cond_perthread, list);
128 	// The wait function will remove its respective item from the list.
129 	return SetEvent(pos->event) ? 0 : ((errno=EINVAL));
130 }
usbi_cond_intwait(usbi_cond_t * cond,usbi_mutex_t * mutex,DWORD timeout_ms)131 __inline static int usbi_cond_intwait(usbi_cond_t *cond,
132 									  usbi_mutex_t *mutex,
133 									  DWORD timeout_ms) {
134 	struct usbi_cond_perthread *pos;
135 	int found = 0, r;
136 	DWORD r2,tid = GetCurrentThreadId();
137 	if(!cond || !mutex) return ((errno=EINVAL));
138 	list_for_each_entry(pos, &cond->not_waiting, list, struct usbi_cond_perthread) {
139 		if(tid == pos->tid) {
140 			found = 1;
141 			break;
142 		}
143 	}
144 	if(!found) {
145 		pos      = (struct usbi_cond_perthread*) calloc(1, sizeof(struct usbi_cond_perthread));
146 		if(!pos) return ((errno=ENOMEM)); // This errno is not POSIX-allowed.
147 		pos->tid = tid;
148 		pos->event = CreateEvent(NULL, FALSE, FALSE, NULL); // auto-reset.
149 		if(!pos->event) {
150 			free(pos);
151 			return      ((errno=ENOMEM));
152 		}
153 		list_add(&pos->list, &cond->not_waiting);
154 	}
155 
156 	list_del(&pos->list); // remove from not_waiting list.
157 	list_add(&pos->list, &cond->waiters);
158 
159 	r  = usbi_mutex_unlock(mutex);
160 	if(r) return r;
161 	r2 = WaitForSingleObject(pos->event, timeout_ms);
162 	r  = usbi_mutex_lock(mutex);
163 	if(r) return r;
164 
165 	list_del(&pos->list);
166 	list_add(&pos->list, &cond->not_waiting);
167 
168 	if(r2 == WAIT_TIMEOUT) return ((errno=ETIMEDOUT));
169 
170 	return 0;
171 }
172 // N.B.: usbi_cond_*wait() can also return ENOMEM, even though pthread_cond_*wait cannot!
usbi_cond_wait(usbi_cond_t * cond,usbi_mutex_t * mutex)173 int usbi_cond_wait(usbi_cond_t *cond, usbi_mutex_t *mutex) {
174 	return usbi_cond_intwait(cond, mutex, INFINITE);
175 }
usbi_cond_timedwait(usbi_cond_t * cond,usbi_mutex_t * mutex,const struct timespec * abstime)176 int usbi_cond_timedwait(usbi_cond_t *cond,
177 						usbi_mutex_t *mutex,
178 						const struct timespec *abstime) {
179 	FILETIME filetime;
180 	ULARGE_INTEGER rtime;
181 	struct timeval targ_time, cur_time, delta_time;
182 	struct timespec cur_time_ns;
183 	DWORD millis;
184 
185 	// GetSystemTimeAsFileTime() is not available on CE
186 	SYSTEMTIME st;
187 	GetSystemTime(&st);
188 	SystemTimeToFileTime(&st, &filetime);
189 	rtime.LowPart   = filetime.dwLowDateTime;
190 	rtime.HighPart  = filetime.dwHighDateTime;
191 	rtime.QuadPart -= epoch_time;
192 	cur_time_ns.tv_sec = (long)(rtime.QuadPart / 10000000);
193 	cur_time_ns.tv_nsec = (long)((rtime.QuadPart % 10000000)*100);
194 	TIMESPEC_TO_TIMEVAL(&cur_time, &cur_time_ns);
195 
196 	TIMESPEC_TO_TIMEVAL(&targ_time, abstime);
197 	timersub(&targ_time, &cur_time, &delta_time);
198 	if(delta_time.tv_sec < 0) // abstime already passed?
199 		millis = 0;
200 	else {
201 		millis  = delta_time.tv_usec/1000;
202 		millis += delta_time.tv_sec *1000;
203 		if (delta_time.tv_usec % 1000) // round up to next millisecond
204 			millis++;
205 	}
206 
207 	return usbi_cond_intwait(cond, mutex, millis);
208 }
209 
usbi_get_tid(void)210 int usbi_get_tid(void) {
211 	return GetCurrentThreadId();
212 }
213