1
0
Fork 0
mirror of https://github.com/DarkMatterCore/nxdumptool.git synced 2024-11-29 21:52:22 +00:00

Proper USB background thread exit.

This commit is contained in:
Pablo Curiel 2020-07-12 12:37:03 -04:00
parent 400cab42a0
commit 8baa5800a1

View file

@ -101,9 +101,9 @@ static bool g_usbDeviceInterfaceInitialized = false;
static Event *g_usbStateChangeEvent = NULL; static Event *g_usbStateChangeEvent = NULL;
static thrd_t g_usbDetectionThread; static thrd_t g_usbDetectionThread;
static UEvent g_usbTimeoutEvent = {0}; static UEvent g_usbDetectionThreadExitEvent = {0}, g_usbTimeoutEvent = {0};
static bool g_usbDetectionThreadCreated = false, g_usbHostAvailable = false, g_usbSessionStarted = false; static bool g_usbHostAvailable = false, g_usbSessionStarted = false, g_usbDetectionThreadExitFlag = false;
static atomic_bool g_usbDetectionThreadExit = false; static atomic_bool g_usbDetectionThreadCreated = false;
static u8 *g_usbTransferBuffer = NULL; static u8 *g_usbTransferBuffer = NULL;
static u64 g_usbTransferRemainingSize = 0; static u64 g_usbTransferRemainingSize = 0;
@ -167,11 +167,15 @@ bool usbInitialize(void)
goto exit; goto exit;
} }
/* Create usermode exit event. */
ueventCreate(&g_usbDetectionThreadExitEvent, true);
/* Create usermode USB timeout event. */ /* Create usermode USB timeout event. */
ueventCreate(&g_usbTimeoutEvent, true); ueventCreate(&g_usbTimeoutEvent, true);
/* Create USB detection thread. */ /* Create USB detection thread. */
if (!(g_usbDetectionThreadCreated = usbCreateDetectionThread())) goto exit; atomic_store(&g_usbDetectionThreadCreated, usbCreateDetectionThread());
if (!atomic_load(&g_usbDetectionThreadCreated)) goto exit;
ret = true; ret = true;
@ -183,15 +187,14 @@ exit:
void usbExit(void) void usbExit(void)
{ {
/* Destroy USB detection thread. */ /* Destroy USB detection thread before attempting to lock. */
if (g_usbDetectionThreadCreated && !atomic_load(&g_usbDetectionThreadExit)) if (atomic_load(&g_usbDetectionThreadCreated))
{ {
atomic_store(&g_usbDetectionThreadExit, true);
usbDestroyDetectionThread(); usbDestroyDetectionThread();
g_usbDetectionThreadCreated = false; atomic_store(&g_usbDetectionThreadCreated, false);
atomic_store(&g_usbDetectionThreadExit, false);
} }
/* Now we can safely lock. */
rwlockWriteLock(&g_usbDeviceLock); rwlockWriteLock(&g_usbDeviceLock);
/* Clear USB state change kernel event. */ /* Clear USB state change kernel event. */
@ -346,6 +349,9 @@ static bool usbCreateDetectionThread(void)
static void usbDestroyDetectionThread(void) static void usbDestroyDetectionThread(void)
{ {
/* Signal the exit event to terminate the USB detection thread */
ueventSignal(&g_usbDetectionThreadExitEvent);
/* Wait for the USB detection thread to exit. */ /* Wait for the USB detection thread to exit. */
thrd_join(g_usbDetectionThread, NULL); thrd_join(g_usbDetectionThread, NULL);
} }
@ -359,28 +365,39 @@ static int usbDetectionThreadFunc(void *arg)
Waiter usb_change_event_waiter = waiterForEvent(g_usbStateChangeEvent); Waiter usb_change_event_waiter = waiterForEvent(g_usbStateChangeEvent);
Waiter usb_timeout_event_waiter = waiterForUEvent(&g_usbTimeoutEvent); Waiter usb_timeout_event_waiter = waiterForUEvent(&g_usbTimeoutEvent);
Waiter exit_event_waiter = waiterForUEvent(&g_usbDetectionThreadExitEvent);
while(true) while(true)
{ {
/* Check if the thread exit flag has been enabled. */ /* Wait until an event is triggered. */
if (atomic_load(&g_usbDetectionThreadExit)) break; rc = waitMulti(&idx, -1, usb_change_event_waiter, usb_timeout_event_waiter, exit_event_waiter);
/* Wait until an event is triggered (100 ms). */
rc = waitMulti(&idx, (u64)100000000, usb_change_event_waiter, usb_timeout_event_waiter);
if (R_FAILED(rc)) continue; if (R_FAILED(rc)) continue;
rwlockWriteLock(&g_usbDeviceLock); rwlockWriteLock(&g_usbDeviceLock);
rwlockWriteLock(&(g_usbDeviceInterface.lock)); rwlockWriteLock(&(g_usbDeviceInterface.lock));
/* Exit event triggered. */
if (idx == 2) break;
/* Retrieve current USB connection status. */ /* Retrieve current USB connection status. */
/* Only proceed if we're dealing with a status change. */ /* Only proceed if we're dealing with a status change. */
g_usbHostAvailable = usbIsHostAvailable(); g_usbHostAvailable = usbIsHostAvailable();
g_usbSessionStarted = false; g_usbSessionStarted = false;
g_usbTransferRemainingSize = 0; g_usbTransferRemainingSize = 0;
/* Start a USB session if we're connected to a host device and if the status change event was triggered. */ /* Start an USB session if we're connected to a host device. */
/* This will essentially hang this thread and all other threads that call USB-related functions until a session is established. */ /* This will essentially hang this thread and all other threads that call USB-related functions until: */
if (g_usbHostAvailable) g_usbSessionStarted = usbStartSession(); /* a) A session is established. */
/* b) The console is disconnected. */
/* c) The thread exit event is triggered. */
if (g_usbHostAvailable)
{
/* Wait until a session is established. */
g_usbSessionStarted = usbStartSession();
/* Check if the exit event was triggered while waiting for a session to be established. */
if (!g_usbSessionStarted && g_usbDetectionThreadExitFlag) break;
}
rwlockWriteUnlock(&(g_usbDeviceInterface.lock)); rwlockWriteUnlock(&(g_usbDeviceInterface.lock));
rwlockWriteUnlock(&g_usbDeviceLock); rwlockWriteUnlock(&g_usbDeviceLock);
@ -388,9 +405,12 @@ static int usbDetectionThreadFunc(void *arg)
/* Close USB session if needed. */ /* Close USB session if needed. */
if (g_usbHostAvailable && g_usbSessionStarted) usbEndSession(); if (g_usbHostAvailable && g_usbSessionStarted) usbEndSession();
g_usbHostAvailable = g_usbSessionStarted = false; g_usbHostAvailable = g_usbSessionStarted = g_usbDetectionThreadExitFlag = false;
g_usbTransferRemainingSize = 0; g_usbTransferRemainingSize = 0;
rwlockWriteUnlock(&(g_usbDeviceInterface.lock));
rwlockWriteUnlock(&g_usbDeviceLock);
return 0; return 0;
} }
@ -1014,20 +1034,16 @@ static bool usbTransferData(void *buf, u64 size, UsbDsEndpoint *endpoint)
rc = eventWait(&(endpoint->CompletionEvent), USB_TRANSFER_TIMEOUT * (u64)1000000000); rc = eventWait(&(endpoint->CompletionEvent), USB_TRANSFER_TIMEOUT * (u64)1000000000);
} else { } else {
/* If we're starting an USB transfer session, wait indefinitely inside a loop to let the user start the companion app. */ /* If we're starting an USB transfer session, wait indefinitely inside a loop to let the user start the companion app. */
while(true) int idx = 0;
{ Waiter completion_event_waiter = waiterForEvent(&(endpoint->CompletionEvent));
/* Check if the thread exit flag has been enabled. */ Waiter exit_event_waiter = waiterForUEvent(&g_usbDetectionThreadExitEvent);
if (atomic_load(&g_usbDetectionThreadExit))
{
rc = MAKERESULT(Module_Kernel, KernelError_TimedOut);
thread_exit = true;
break;
}
/* Wait for the endpoint completion event (100 ms). */ rc = waitMulti(&idx, -1, completion_event_waiter, exit_event_waiter);
/* Break from this loop if the wait function succeeded, or if we got an result code different than TimedOut. */ if (R_SUCCEEDED(rc) && idx == 1)
rc = eventWait(&(endpoint->CompletionEvent), (u64)100000000); {
if (R_SUCCEEDED(rc) || R_VALUE(rc) != KERNELRESULT(TimedOut)) break; /* Exit event triggered. */
rc = MAKERESULT(Module_Kernel, KernelError_TimedOut);
g_usbDetectionThreadExitFlag = thread_exit = true;
} }
} }