From 9b0563fa878c6afa73e00a967bc24259ec72ef35 Mon Sep 17 00:00:00 2001
From: Morph <39850852+Morph1984@users.noreply.github.com>
Date: Wed, 25 Jan 2023 16:03:32 -0500
Subject: [PATCH 1/2] polyfill_thread: Implement StoppableTimedWait

StoppableTimedWait allows for a timed wait to be stopped immediately after a stop is requested.
This is useful in cases where long duration thread sleeps are needed and allows for immediate joining of waiting threads after a stop is requested.

Co-Authored-By: liamwhite <liamwhite@users.noreply.github.com>
---
 src/common/polyfill_thread.h | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/src/common/polyfill_thread.h b/src/common/polyfill_thread.h
index 5a8d1ce085..b2c929d2f1 100644
--- a/src/common/polyfill_thread.h
+++ b/src/common/polyfill_thread.h
@@ -11,6 +11,8 @@
 
 #ifdef __cpp_lib_jthread
 
+#include <chrono>
+#include <condition_variable>
 #include <stop_token>
 #include <thread>
 
@@ -21,11 +23,23 @@ void CondvarWait(Condvar& cv, Lock& lock, std::stop_token token, Pred&& pred) {
     cv.wait(lock, token, std::move(pred));
 }
 
+template <typename Rep, typename Period>
+bool StoppableTimedWait(std::stop_token token, const std::chrono::duration<Rep, Period>& rel_time) {
+    std::condition_variable_any cv;
+    std::mutex m;
+
+    // Perform the timed wait.
+    std::unique_lock lk{m};
+    return !cv.wait_for(lk, token, rel_time, [&] { return token.stop_requested(); });
+}
+
 } // namespace Common
 
 #else
 
 #include <atomic>
+#include <chrono>
+#include <condition_variable>
 #include <functional>
 #include <list>
 #include <memory>
@@ -318,6 +332,28 @@ void CondvarWait(Condvar& cv, Lock& lock, std::stop_token token, Pred pred) {
     cv.wait(lock, [&] { return pred() || token.stop_requested(); });
 }
 
+template <typename Rep, typename Period>
+bool StoppableTimedWait(std::stop_token token, const std::chrono::duration<Rep, Period>& rel_time) {
+    if (token.stop_requested()) {
+        return false;
+    }
+
+    bool stop_requested = false;
+    std::condition_variable cv;
+    std::mutex m;
+
+    std::stop_callback cb(token, [&] {
+        // Wake up the waiting thread.
+        std::unique_lock lk{m};
+        stop_requested = true;
+        cv.notify_one();
+    });
+
+    // Perform the timed wait.
+    std::unique_lock lk{m};
+    return !cv.wait_for(lk, rel_time, [&] { return stop_requested; });
+}
+
 } // namespace Common
 
 #endif

From c55147b24a2752bd86aa559d2397a9dc54feb582 Mon Sep 17 00:00:00 2001
From: Morph <39850852+Morph1984@users.noreply.github.com>
Date: Wed, 25 Jan 2023 16:03:54 -0500
Subject: [PATCH 2/2] input_common: Make use of StoppableTimedWait

---
 src/input_common/drivers/gc_adapter.cpp | 4 ++--
 src/input_common/drivers/joycon.cpp     | 7 ++++---
 2 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/src/input_common/drivers/gc_adapter.cpp b/src/input_common/drivers/gc_adapter.cpp
index ecb3e9dc29..d09ff178b6 100644
--- a/src/input_common/drivers/gc_adapter.cpp
+++ b/src/input_common/drivers/gc_adapter.cpp
@@ -6,6 +6,7 @@
 
 #include "common/logging/log.h"
 #include "common/param_package.h"
+#include "common/polyfill_thread.h"
 #include "common/settings_input.h"
 #include "common/thread.h"
 #include "input_common/drivers/gc_adapter.h"
@@ -217,8 +218,7 @@ void GCAdapter::AdapterScanThread(std::stop_token stop_token) {
     Common::SetCurrentThreadName("ScanGCAdapter");
     usb_adapter_handle = nullptr;
     pads = {};
-    while (!stop_token.stop_requested() && !Setup()) {
-        std::this_thread::sleep_for(std::chrono::seconds(2));
+    while (!Setup() && Common::StoppableTimedWait(stop_token, std::chrono::seconds{2})) {
     }
 }
 
diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp
index 40cda400d8..cedc94e639 100644
--- a/src/input_common/drivers/joycon.cpp
+++ b/src/input_common/drivers/joycon.cpp
@@ -5,6 +5,7 @@
 
 #include "common/param_package.h"
 #include "common/polyfill_ranges.h"
+#include "common/polyfill_thread.h"
 #include "common/settings.h"
 #include "common/thread.h"
 #include "input_common/drivers/joycon.h"
@@ -67,7 +68,8 @@ void Joycons::Setup() {
 void Joycons::ScanThread(std::stop_token stop_token) {
     constexpr u16 nintendo_vendor_id = 0x057e;
     Common::SetCurrentThreadName("JoyconScanThread");
-    while (!stop_token.stop_requested()) {
+
+    do {
         SDL_hid_device_info* devs = SDL_hid_enumerate(nintendo_vendor_id, 0x0);
         SDL_hid_device_info* cur_dev = devs;
 
@@ -81,8 +83,7 @@ void Joycons::ScanThread(std::stop_token stop_token) {
         }
 
         SDL_hid_free_enumeration(devs);
-        std::this_thread::sleep_for(std::chrono::seconds(5));
-    }
+    } while (Common::StoppableTimedWait(stop_token, std::chrono::seconds{5}));
 }
 
 bool Joycons::IsDeviceNew(SDL_hid_device_info* device_info) const {