* are invalid and will remain so even if the device comes back.
*
* When handling a LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED event it is considered
- * safe to call any libusb function that takes a libusb_device. On the other hand,
- * when handling a LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT event the only safe function
+ * safe to call any libusb function that takes a libusb_device. It also safe to
+ * open a device and submit asynchronous transfers. However, most other functions
+ * that take a libusb_device_handle are <b>not</b> safe to call. Examples of such
+ * functions are any of the \ref syncio "synchronous API" functions or the blocking
+ * functions that retrieve various \ref desc "USB descriptors". These functions must
+ * be used outside of the context of the hotplug callback.
+ *
+ * When handling a LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT event the only safe function
* is libusb_get_device_descriptor().
*
* The following code provides an example of the usage of the hotplug interface:
* success or failure reason, number of bytes of data transferred, etc. See
* the libusb_transfer structure documentation for more information.
*
+ * <b>Important Note</b>: The user-specified callback is called from an event
+ * handling context. It is therefore important that no calls are made into
+ * libusb that will attempt to perform any event handling. Examples of such
+ * functions are any listed in the \ref syncio "synchronous API" and any of
+ * the blocking functions that retrieve \ref desc "USB descriptors".
+ *
* \subsection Deallocation
*
* When a transfer has completed (i.e. the callback function has been invoked),
usbi_mutex_init(&ctx->event_waiters_lock, NULL);
usbi_cond_init(&ctx->event_waiters_cond, NULL);
usbi_mutex_init(&ctx->event_data_lock, NULL);
+ usbi_tls_key_create(&ctx->event_handling_key, NULL);
list_init(&ctx->flying_transfers);
list_init(&ctx->ipollfds);
list_init(&ctx->hotplug_msgs);
usbi_mutex_destroy(&ctx->event_waiters_lock);
usbi_cond_destroy(&ctx->event_waiters_cond);
usbi_mutex_destroy(&ctx->event_data_lock);
+ usbi_tls_key_delete(ctx->event_handling_key);
return r;
}
usbi_mutex_destroy(&ctx->event_waiters_lock);
usbi_cond_destroy(&ctx->event_waiters_cond);
usbi_mutex_destroy(&ctx->event_data_lock);
+ usbi_tls_key_delete(ctx->event_handling_key);
if (ctx->pollfds)
free(ctx->pollfds);
}
int timeout_ms;
int special_event;
+ /* prevent attempts to recursively handle events (e.g. calling into
+ * libusb_handle_events() from within a hotplug or transfer callback) */
+ if (usbi_handling_events(ctx))
+ return LIBUSB_ERROR_BUSY;
+ usbi_start_event_handling(ctx);
+
/* there are certain fds that libusb uses internally, currently:
*
* 1) event pipe
ctx->pollfds = calloc(ctx->pollfds_cnt, sizeof(*ctx->pollfds));
if (!ctx->pollfds) {
usbi_mutex_unlock(&ctx->event_data_lock);
- return LIBUSB_ERROR_NO_MEM;
+ r = LIBUSB_ERROR_NO_MEM;
+ goto done;
}
list_for_each_entry(ipollfd, &ctx->ipollfds, list, struct usbi_pollfd) {
usbi_dbg("poll() %d fds with timeout in %dms", nfds, timeout_ms);
r = usbi_poll(fds, nfds, timeout_ms);
usbi_dbg("poll() returned %d", r);
- if (r == 0)
- return handle_timeouts(ctx);
- else if (r == -1 && errno == EINTR)
- return LIBUSB_ERROR_INTERRUPTED;
+ if (r == 0) {
+ r = handle_timeouts(ctx);
+ goto done;
+ }
+ else if (r == -1 && errno == EINTR) {
+ r = LIBUSB_ERROR_INTERRUPTED;
+ goto done;
+ }
else if (r < 0) {
usbi_err(ctx, "poll failed %d err=%d", r, errno);
- return LIBUSB_ERROR_IO;
+ r = LIBUSB_ERROR_IO;
+ goto done;
}
special_event = 0;
if (ret) {
/* return error code */
r = ret;
- goto handled;
+ goto done;
}
if (0 == --r)
if (ret < 0) {
/* return error code */
r = ret;
- goto handled;
+ goto done;
}
if (0 == --r)
goto redo_poll;
}
+done:
+ usbi_end_event_handling(ctx);
return r;
}
/* used to see if there is an active thread doing event handling */
int event_handler_active;
+ /* A thread-local storage key to track which thread is performing event
+ * handling */
+ usbi_tls_key_t event_handling_key;
+
/* used to wait for event completion in threads other than the one that is
* event handling */
usbi_mutex_t event_waiters_lock;
struct list_head list;
};
+/* Macros for managing event handling state */
+#define usbi_handling_events(ctx) \
+ (usbi_tls_key_get((ctx)->event_handling_key) != NULL)
+
+#define usbi_start_event_handling(ctx) \
+ usbi_tls_key_set((ctx)->event_handling_key, ctx)
+
+#define usbi_end_event_handling(ctx) \
+ usbi_tls_key_set((ctx)->event_handling_key, NULL)
+
/* Update the following macro if new event sources are added */
#define usbi_pending_events(ctx) \
((ctx)->device_close || (ctx)->pollfds_modified \
#define usbi_cond_destroy pthread_cond_destroy
#define usbi_cond_signal pthread_cond_signal
+#define usbi_tls_key_t pthread_key_t
+#define usbi_tls_key_create pthread_key_create
+#define usbi_tls_key_get pthread_getspecific
+#define usbi_tls_key_set pthread_setspecific
+#define usbi_tls_key_delete pthread_key_delete
+
extern int usbi_mutex_init_recursive(pthread_mutex_t *mutex, pthread_mutexattr_t *attr);
int usbi_get_tid(void);
return usbi_cond_intwait(cond, mutex, millis);
}
+int usbi_tls_key_create(usbi_tls_key_t *key, void (*destructor)(void *)) {
+ UNUSED(destructor);
+ if(!key) return ((errno=EINVAL));
+ *key = TlsAlloc();
+ if (*key == TLS_OUT_OF_INDEXES) return ((errno=ENOMEM));
+ return 0;
+}
+
+void *usbi_tls_key_get(usbi_tls_key_t key) {
+ return TlsGetValue(key);
+}
+
+int usbi_tls_key_set(usbi_tls_key_t key, const void *value) {
+ if (!TlsSetValue(key, (LPVOID)value)) return ((errno=EINVAL));
+ return 0;
+}
+
+int usbi_tls_key_delete(usbi_tls_key_t key) {
+ if (!TlsFree(key)) return ((errno=EINVAL));
+ return 0;
+}
+
int usbi_get_tid(void) {
return GetCurrentThreadId();
}
#define usbi_mutexattr_t void
#define usbi_condattr_t void
+#define usbi_tls_key_t DWORD
// all Windows mutexes are recursive
#define usbi_mutex_init_recursive(mutex, attr) usbi_mutex_init((mutex), (attr))
int usbi_cond_broadcast(usbi_cond_t *cond);
int usbi_cond_signal(usbi_cond_t *cond);
+int usbi_tls_key_create(usbi_tls_key_t *key, void (*destructor)(void *));
+void *usbi_tls_key_get(usbi_tls_key_t key);
+int usbi_tls_key_set(usbi_tls_key_t key, const void *value);
+int usbi_tls_key_delete(usbi_tls_key_t key);
+
int usbi_get_tid(void);
#endif /* LIBUSB_THREADS_WINDOWS_H */
* \returns LIBUSB_ERROR_PIPE if the control request was not supported by the
* device
* \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns LIBUSB_ERROR_BUSY if called from event handling context
* \returns another LIBUSB_ERROR code on other failures
*/
int API_EXPORTED libusb_control_transfer(libusb_device_handle *dev_handle,
uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex,
unsigned char *data, uint16_t wLength, unsigned int timeout)
{
- struct libusb_transfer *transfer = libusb_alloc_transfer(0);
+ struct libusb_transfer *transfer;
unsigned char *buffer;
int completed = 0;
int r;
+ if (usbi_handling_events(HANDLE_CTX(dev_handle)))
+ return LIBUSB_ERROR_BUSY;
+
+ transfer = libusb_alloc_transfer(0);
if (!transfer)
return LIBUSB_ERROR_NO_MEM;
unsigned char endpoint, unsigned char *buffer, int length,
int *transferred, unsigned int timeout, unsigned char type)
{
- struct libusb_transfer *transfer = libusb_alloc_transfer(0);
+ struct libusb_transfer *transfer;
int completed = 0;
int r;
+ if (usbi_handling_events(HANDLE_CTX(dev_handle)))
+ return LIBUSB_ERROR_BUSY;
+
+ transfer = libusb_alloc_transfer(0);
if (!transfer)
return LIBUSB_ERROR_NO_MEM;
* \returns LIBUSB_ERROR_OVERFLOW if the device offered more data, see
* \ref packetoverflow
* \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns LIBUSB_ERROR_BUSY if called from event handling context
* \returns another LIBUSB_ERROR code on other failures
*/
int API_EXPORTED libusb_bulk_transfer(struct libusb_device_handle *dev_handle,
* \returns LIBUSB_ERROR_OVERFLOW if the device offered more data, see
* \ref packetoverflow
* \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns LIBUSB_ERROR_BUSY if called from event handling context
* \returns another LIBUSB_ERROR code on other error
*/
int API_EXPORTED libusb_interrupt_transfer(
-#define LIBUSB_NANO 11012
+#define LIBUSB_NANO 11013