• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
21 #include <config.h>
22 #include <objbase.h>
23 #include <errno.h>
24 #include <stdarg.h>
25 
26 #include "libusbi.h"
27 
28 
usbi_mutex_init(usbi_mutex_t * mutex,const usbi_mutexattr_t * attr)29 int usbi_mutex_init(usbi_mutex_t *mutex,
30 					const usbi_mutexattr_t *attr) {
31 	if(! mutex) return ((errno=EINVAL));
32 	*mutex = CreateMutex(NULL, FALSE, NULL);
33 	if(!*mutex) return ((errno=ENOMEM));
34 	return 0;
35 }
usbi_mutex_destroy(usbi_mutex_t * mutex)36 int usbi_mutex_destroy(usbi_mutex_t *mutex) {
37 	// It is not clear if CloseHandle failure is due to failure to unlock.
38 	//   If so, this should be errno=EBUSY.
39 	if(!mutex || !CloseHandle(*mutex)) return ((errno=EINVAL));
40 	*mutex = NULL;
41 	return 0;
42 }
usbi_mutex_trylock(usbi_mutex_t * mutex)43 int usbi_mutex_trylock(usbi_mutex_t *mutex) {
44 	DWORD result;
45 	if(!mutex) return ((errno=EINVAL));
46 	result = WaitForSingleObject(*mutex, 0);
47 	if(result == WAIT_OBJECT_0 || result == WAIT_ABANDONED)
48 		return 0; // acquired (ToDo: check that abandoned is ok)
49 	if(result == WAIT_TIMEOUT)
50 		return ((errno=EBUSY));
51 	return ((errno=EINVAL)); // don't know how this would happen
52 							 //   so don't know proper errno
53 }
usbi_mutex_lock(usbi_mutex_t * mutex)54 int usbi_mutex_lock(usbi_mutex_t *mutex) {
55 	DWORD result;
56 	if(!mutex) return ((errno=EINVAL));
57 	result = WaitForSingleObject(*mutex, INFINITE);
58 	if(result == WAIT_OBJECT_0 || result == WAIT_ABANDONED)
59 		return 0; // acquired (ToDo: check that abandoned is ok)
60 	return ((errno=EINVAL)); // don't know how this would happen
61 							 //   so don't know proper errno
62 }
usbi_mutex_unlock(usbi_mutex_t * mutex)63 int usbi_mutex_unlock(usbi_mutex_t *mutex) {
64 	if(!mutex)                return ((errno=EINVAL));
65 	if(!ReleaseMutex(*mutex)) return ((errno=EPERM ));
66 	return 0;
67 }
68 
usbi_mutex_static_lock(usbi_mutex_static_t * mutex)69 int usbi_mutex_static_lock(usbi_mutex_static_t *mutex) {
70 	if(!mutex)               return ((errno=EINVAL));
71 	while (InterlockedExchange((LONG *)mutex, 1) == 1) {
72 		SleepEx(0, TRUE);
73 	}
74 	return 0;
75 }
usbi_mutex_static_unlock(usbi_mutex_static_t * mutex)76 int usbi_mutex_static_unlock(usbi_mutex_static_t *mutex) {
77 	if(!mutex)               return ((errno=EINVAL));
78 	*mutex = 0;
79 	return 0;
80 }
81 
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 	if(!cond)           return ((errno=EINVAL));
87 	list_init(&cond->waiters    );
88 	list_init(&cond->not_waiting);
89 	return 0;
90 }
usbi_cond_destroy(usbi_cond_t * cond)91 int usbi_cond_destroy(usbi_cond_t *cond) {
92 	// This assumes no one is using this anymore.  The check MAY NOT BE safe.
93 	struct usbi_cond_perthread *pos, *prev_pos = NULL;
94 	if(!cond) return ((errno=EINVAL));
95 	if(!list_empty(&cond->waiters)) return ((errno=EBUSY )); // (!see above!)
96 	list_for_each_entry(pos, &cond->not_waiting, list, struct usbi_cond_perthread) {
97 		free(prev_pos);
98 		list_del(&pos->list);
99 		prev_pos = pos;
100 	}
101 	free(prev_pos);
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 static int __inline 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 	extern const uint64_t epoch_time;
185 
186 	GetSystemTimeAsFileTime(&filetime);
187 	rtime.LowPart   = filetime.dwLowDateTime;
188 	rtime.HighPart  = filetime.dwHighDateTime;
189 	rtime.QuadPart -= epoch_time;
190 	cur_time_ns.tv_sec = (long)(rtime.QuadPart / 10000000);
191 	cur_time_ns.tv_nsec = (long)((rtime.QuadPart % 10000000)*100);
192 	TIMESPEC_TO_TIMEVAL(&cur_time, &cur_time_ns);
193 
194 	TIMESPEC_TO_TIMEVAL(&targ_time, abstime);
195 	timersub(&targ_time, &cur_time, &delta_time);
196 	if(delta_time.tv_sec < 0) // abstime already passed?
197 		millis = 0;
198 	else {
199 		millis  = delta_time.tv_usec/1000;
200 		millis += delta_time.tv_sec *1000;
201 		if (delta_time.tv_usec % 1000) // round up to next millisecond
202 			millis++;
203 	}
204 
205 	return usbi_cond_intwait(cond, mutex, millis);
206 }
207 
208