Home | History | Annotate | Download | only in os
      1 /*
      2  * libusb synchronization on Microsoft Windows
      3  *
      4  * Copyright (C) 2010 Michael Plante <michael.plante (at) 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 
     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 }
     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 }
     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 }
     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 }
     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 
     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 }
     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 
     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 }
     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 
    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 }
    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 }
    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!
    173 int usbi_cond_wait(usbi_cond_t *cond, usbi_mutex_t *mutex) {
    174 	return usbi_cond_intwait(cond, mutex, INFINITE);
    175 }
    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