2 * libusbx synchronization on Microsoft Windows
4 * Copyright © 2010 Michael Plante <michael.plante@gmail.com>
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.
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.
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
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
));
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
));
43 int usbi_mutex_trylock(usbi_mutex_t
*mutex
) {
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
54 int usbi_mutex_lock(usbi_mutex_t
*mutex
) {
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
63 int usbi_mutex_unlock(usbi_mutex_t
*mutex
) {
64 if(!mutex
) return ((errno
=EINVAL
));
65 if(!ReleaseMutex(*mutex
)) return ((errno
=EPERM
));
69 int usbi_mutex_static_lock(usbi_mutex_static_t
*mutex
) {
70 if(!mutex
) return ((errno
=EINVAL
));
71 while (InterlockedExchange((LONG
*)mutex
, 1) == 1) {
76 int usbi_mutex_static_unlock(usbi_mutex_static_t
*mutex
) {
77 if(!mutex
) return ((errno
=EINVAL
));
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
);
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
) {
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!
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
))
117 // The wait function will remove its respective item from the list.
118 return fail
? ((errno
=EINVAL
)) : 0;
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
));
131 static int __inline
usbi_cond_intwait(usbi_cond_t
*cond
,
134 struct usbi_cond_perthread
*pos
;
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
) {
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.
148 pos
->event
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
); // auto-reset.
151 return ((errno
=ENOMEM
));
153 list_add(&pos
->list
, &cond
->not_waiting
);
156 list_del(&pos
->list
); // remove from not_waiting list.
157 list_add(&pos
->list
, &cond
->waiters
);
159 r
= usbi_mutex_unlock(mutex
);
161 r2
= WaitForSingleObject(pos
->event
, timeout_ms
);
162 r
= usbi_mutex_lock(mutex
);
165 list_del(&pos
->list
);
166 list_add(&pos
->list
, &cond
->not_waiting
);
168 if(r2
== WAIT_TIMEOUT
) return ((errno
=ETIMEDOUT
));
172 // N.B.: usbi_cond_*wait() can also return ENOMEM, even though pthread_cond_*wait cannot!
173 int usbi_cond_wait(usbi_cond_t
*cond
, usbi_mutex_t
*mutex
) {
174 return usbi_cond_intwait(cond
, mutex
, INFINITE
);
176 int usbi_cond_timedwait(usbi_cond_t
*cond
,
178 const struct timespec
*abstime
) {
180 ULARGE_INTEGER rtime
;
181 struct timeval targ_time
, cur_time
, delta_time
;
182 struct timespec cur_time_ns
;
184 extern const uint64_t epoch_time
;
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
);
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?
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
205 return usbi_cond_intwait(cond
, mutex
, millis
);