Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/driver_vive.c
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,9 @@ struct SurviveUSBInfo {
struct survive_config_packet *cfg_user;

bool request_close, request_reopen;
/* Raised from the libusb transfer-completion callback, consumed by the
poll loop off-callback. See survive_disconnect_device(). */
bool request_disconnect;
};

struct SurviveViveData {
Expand Down Expand Up @@ -1031,6 +1034,13 @@ int survive_vive_usb_poll(SurviveContext *ctx, void *v) {

survive_config_poll(usbInfo);

/* Close requested from a transfer callback: do it HERE, on the poll
thread, where no libusb lock is held. */
if (usbInfo->request_disconnect) {
usbInfo->request_disconnect = false;
survive_close_usb_device(usbInfo);
}

if (survive_handle_close_request_flag(usbInfo)) {
i--;
}
Expand Down
28 changes: 26 additions & 2 deletions src/driver_vive.libusb.h
Original file line number Diff line number Diff line change
Expand Up @@ -204,8 +204,25 @@ static int survive_open_usb_device(SurviveViveData *sv, survive_usb_device_t d,
static inline void survive_close_usb_device(struct SurviveUSBInfo *usbInfo);

static void survive_disconnect_device(SurviveUSBInterface *iface) {
struct SurviveUSBInfo *usbInfo = iface->usbInfo;
iface->ctx = 0;
survive_close_usb_device(iface->usbInfo);

/* We are inside handle_transfer(), i.e. inside libusb's
transfer-completion callback: the event thread holds the current
transfer's locks. survive_close_usb_device() calls
libusb_cancel_transfer() on every interface -- including the one being
handled -- and survive_config_cancel() reaches libusb the same way.
Re-entering libusb from here makes pthread_mutex_lock() fail, and
usbi_mutex_lock() (os/threads_posix.h) asserts: abort().

Only mark here. The poll loop calls survive_close_usb_device()
off-callback, where no lock is held. The transfer being handled is
still cleaned up as before by the shutdown: label below. */
for (size_t j = 0; j < usbInfo->interface_cnt; j++) {
usbInfo->interfaces[j].shutdown = 1;
usbInfo->interfaces[j].assoc_obj = 0;
}
usbInfo->request_disconnect = true;
}
static void handle_transfer(struct libusb_transfer *transfer) {
uint64_t time = OGGetAbsoluteTimeUS();
Expand All @@ -227,11 +244,18 @@ static void handle_transfer(struct libusb_transfer *transfer) {
if (!iface->shutdown && transfer->status != LIBUSB_TRANSFER_COMPLETED) {
SV_WARN("%f %s Device disconnect: %d", survive_run_time(ctx), survive_colorize_codename(iface->assoc_obj),
transfer->status);
iface->error_count++;
if (iface->error_count++ < 10) {
if (libusb_submit_transfer(transfer)) {
goto shutdown;
}

/* Resubmit succeeded: the transfer is IN FLIGHT again. Falling
through to shutdown: would call libusb_free_transfer() on it,
which destroys its mutex while libusb still holds it in the
flying-transfers list. The next event-loop pass then locks a
destroyed mutex and usbi_mutex_lock() asserts. Return and let
the retry run its course. */
return;
}

goto disconnect;
Expand Down