Merge pull request #9033 from leoetlino/scan-thread

IOS/USB: Move scan thread logic into a separate class
This commit is contained in:
LC 2020-09-01 17:56:33 -04:00 committed by GitHub
commit 936702bd04
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 70 additions and 27 deletions

View File

@ -31,19 +31,16 @@ USBHost::USBHost(Kernel& ios, const std::string& device_name) : Device(ios, devi
{ {
} }
USBHost::~USBHost() USBHost::~USBHost() = default;
{
StopThreads();
}
IPCCommandResult USBHost::Open(const OpenRequest& request) IPCCommandResult USBHost::Open(const OpenRequest& request)
{ {
if (!m_has_initialised && !Core::WantsDeterminism()) if (!m_has_initialised && !Core::WantsDeterminism())
{ {
StartThreads(); GetScanThread().Start();
// Force a device scan to complete, because some games (including Your Shape) only care // Force a device scan to complete, because some games (including Your Shape) only care
// about the initial device list (in the first GETDEVICECHANGE reply). // about the initial device list (in the first GETDEVICECHANGE reply).
m_first_scan_complete_event.Wait(); GetScanThread().WaitForFirstScan();
m_has_initialised = true; m_has_initialised = true;
} }
return GetDefaultReply(IPC_SUCCESS); return GetDefaultReply(IPC_SUCCESS);
@ -52,9 +49,9 @@ IPCCommandResult USBHost::Open(const OpenRequest& request)
void USBHost::UpdateWantDeterminism(const bool new_want_determinism) void USBHost::UpdateWantDeterminism(const bool new_want_determinism)
{ {
if (new_want_determinism) if (new_want_determinism)
StopThreads(); GetScanThread().Stop();
else if (IsOpened()) else if (IsOpened())
StartThreads(); GetScanThread().Start();
} }
void USBHost::DoState(PointerWrap& p) void USBHost::DoState(PointerWrap& p)
@ -112,7 +109,6 @@ bool USBHost::UpdateDevices(const bool always_add_hooks)
return false; return false;
DetectRemovedDevices(plugged_devices, hooks); DetectRemovedDevices(plugged_devices, hooks);
DispatchHooks(hooks); DispatchHooks(hooks);
m_first_scan_complete_event.Set();
return true; return true;
} }
@ -177,33 +173,44 @@ void USBHost::DispatchHooks(const DeviceChangeHooks& hooks)
OnDeviceChangeEnd(); OnDeviceChangeEnd();
} }
void USBHost::StartThreads() USBHost::ScanThread::~ScanThread()
{
Stop();
}
void USBHost::ScanThread::WaitForFirstScan()
{
m_first_scan_complete_event.Wait();
}
void USBHost::ScanThread::Start()
{ {
if (Core::WantsDeterminism()) if (Core::WantsDeterminism())
return; return;
if (m_scan_thread_running.TestAndSet()) if (m_thread_running.TestAndSet())
{ {
m_scan_thread = std::thread([this] { m_thread = std::thread([this] {
Common::SetCurrentThreadName("USB Scan Thread"); Common::SetCurrentThreadName("USB Scan Thread");
while (m_scan_thread_running.IsSet()) while (m_thread_running.IsSet())
{ {
UpdateDevices(); if (m_host->UpdateDevices())
m_first_scan_complete_event.Set();
Common::SleepCurrentThread(50); Common::SleepCurrentThread(50);
} }
}); });
} }
} }
void USBHost::StopThreads() void USBHost::ScanThread::Stop()
{ {
if (m_scan_thread_running.TestAndClear()) if (m_thread_running.TestAndClear())
m_scan_thread.join(); m_thread.join();
// Clear all devices and dispatch removal hooks. // Clear all devices and dispatch removal hooks.
DeviceChangeHooks hooks; DeviceChangeHooks hooks;
DetectRemovedDevices(std::set<u64>(), hooks); m_host->DetectRemovedDevices(std::set<u64>(), hooks);
DispatchHooks(hooks); m_host->DispatchHooks(hooks);
} }
IPCCommandResult USBHost::HandleTransfer(std::shared_ptr<USB::Device> device, u32 request, IPCCommandResult USBHost::HandleTransfer(std::shared_ptr<USB::Device> device, u32 request,

View File

@ -46,6 +46,23 @@ protected:
}; };
using DeviceChangeHooks = std::map<std::shared_ptr<USB::Device>, ChangeEvent>; using DeviceChangeHooks = std::map<std::shared_ptr<USB::Device>, ChangeEvent>;
class ScanThread final
{
public:
explicit ScanThread(USBHost* host) : m_host(host) {}
~ScanThread();
void Start();
void Stop();
void WaitForFirstScan();
private:
USBHost* m_host = nullptr;
Common::Flag m_thread_running;
std::thread m_thread;
Common::Event m_first_scan_complete_event;
Common::Flag m_is_initialized;
};
std::map<u64, std::shared_ptr<USB::Device>> m_devices; std::map<u64, std::shared_ptr<USB::Device>> m_devices;
mutable std::mutex m_devices_mutex; mutable std::mutex m_devices_mutex;
@ -53,25 +70,18 @@ protected:
virtual void OnDeviceChange(ChangeEvent event, std::shared_ptr<USB::Device> changed_device); virtual void OnDeviceChange(ChangeEvent event, std::shared_ptr<USB::Device> changed_device);
virtual void OnDeviceChangeEnd(); virtual void OnDeviceChangeEnd();
virtual bool ShouldAddDevice(const USB::Device& device) const; virtual bool ShouldAddDevice(const USB::Device& device) const;
virtual ScanThread& GetScanThread() = 0;
IPCCommandResult HandleTransfer(std::shared_ptr<USB::Device> device, u32 request, IPCCommandResult HandleTransfer(std::shared_ptr<USB::Device> device, u32 request,
std::function<s32()> submit) const; std::function<s32()> submit) const;
private: private:
void StartThreads();
void StopThreads();
bool AddDevice(std::unique_ptr<USB::Device> device); bool AddDevice(std::unique_ptr<USB::Device> device);
bool UpdateDevices(bool always_add_hooks = false); bool UpdateDevices(bool always_add_hooks = false);
bool AddNewDevices(std::set<u64>& new_devices, DeviceChangeHooks& hooks, bool always_add_hooks); bool AddNewDevices(std::set<u64>& new_devices, DeviceChangeHooks& hooks, bool always_add_hooks);
void DetectRemovedDevices(const std::set<u64>& plugged_devices, DeviceChangeHooks& hooks); void DetectRemovedDevices(const std::set<u64>& plugged_devices, DeviceChangeHooks& hooks);
void DispatchHooks(const DeviceChangeHooks& hooks); void DispatchHooks(const DeviceChangeHooks& hooks);
// Device scanning thread
Common::Flag m_scan_thread_running;
std::thread m_scan_thread;
Common::Event m_first_scan_complete_event;
bool m_has_initialised = false; bool m_has_initialised = false;
LibusbUtils::Context m_context; LibusbUtils::Context m_context;
}; };

View File

@ -24,6 +24,8 @@ namespace IOS::HLE::Device
{ {
OH0::OH0(Kernel& ios, const std::string& device_name) : USBHost(ios, device_name) OH0::OH0(Kernel& ios, const std::string& device_name) : USBHost(ios, device_name)
{ {
static_assert(offsetof(OH0, m_scan_thread) == sizeof(OH0) - sizeof(ScanThread),
"ScanThread must be the last data member");
} }
IPCCommandResult OH0::Open(const OpenRequest& request) IPCCommandResult OH0::Open(const OpenRequest& request)

View File

@ -66,6 +66,8 @@ private:
template <typename T> template <typename T>
void TriggerHook(std::map<T, u32>& hooks, T value, ReturnCode return_value); void TriggerHook(std::map<T, u32>& hooks, T value, ReturnCode return_value);
ScanThread& GetScanThread() override { return m_scan_thread; }
struct DeviceEntry struct DeviceEntry
{ {
u32 unknown; u32 unknown;
@ -79,6 +81,8 @@ private:
std::map<u64, u32> m_removal_hooks; std::map<u64, u32> m_removal_hooks;
std::set<u64> m_opened_devices; std::set<u64> m_opened_devices;
std::mutex m_hooks_mutex; std::mutex m_hooks_mutex;
ScanThread m_scan_thread{this};
}; };
} // namespace Device } // namespace Device
} // namespace IOS::HLE } // namespace IOS::HLE

View File

@ -24,6 +24,8 @@ namespace IOS::HLE::Device
{ {
USB_HIDv4::USB_HIDv4(Kernel& ios, const std::string& device_name) : USBHost(ios, device_name) USB_HIDv4::USB_HIDv4(Kernel& ios, const std::string& device_name) : USBHost(ios, device_name)
{ {
static_assert(offsetof(USB_HIDv4, m_scan_thread) == sizeof(USB_HIDv4) - sizeof(ScanThread),
"ScanThread must be the last data member");
} }
IPCCommandResult USB_HIDv4::IOCtl(const IOCtlRequest& request) IPCCommandResult USB_HIDv4::IOCtl(const IOCtlRequest& request)

View File

@ -39,6 +39,7 @@ private:
std::vector<u8> GetDeviceEntry(const USB::Device& device) const; std::vector<u8> GetDeviceEntry(const USB::Device& device) const;
void OnDeviceChange(ChangeEvent, std::shared_ptr<USB::Device>) override; void OnDeviceChange(ChangeEvent, std::shared_ptr<USB::Device>) override;
bool ShouldAddDevice(const USB::Device& device) const override; bool ShouldAddDevice(const USB::Device& device) const override;
ScanThread& GetScanThread() override { return m_scan_thread; }
static constexpr u32 VERSION = 0x40001; static constexpr u32 VERSION = 0x40001;
static constexpr u8 HID_CLASS = 0x03; static constexpr u8 HID_CLASS = 0x03;
@ -51,5 +52,7 @@ private:
// IOS device IDs <=> USB device IDs // IOS device IDs <=> USB device IDs
std::map<s32, u64> m_ios_ids; std::map<s32, u64> m_ios_ids;
std::map<u64, s32> m_device_ids; std::map<u64, s32> m_device_ids;
ScanThread m_scan_thread{this};
}; };
} // namespace IOS::HLE::Device } // namespace IOS::HLE::Device

View File

@ -22,6 +22,9 @@ USB_HIDv5::~USB_HIDv5() = default;
IPCCommandResult USB_HIDv5::IOCtl(const IOCtlRequest& request) IPCCommandResult USB_HIDv5::IOCtl(const IOCtlRequest& request)
{ {
static_assert(offsetof(USB_HIDv5, m_scan_thread) == sizeof(USB_HIDv5) - sizeof(ScanThread),
"ScanThread must be the last data member");
request.Log(GetDeviceName(), Common::Log::IOS_USB); request.Log(GetDeviceName(), Common::Log::IOS_USB);
switch (request.request) switch (request.request)
{ {

View File

@ -27,11 +27,16 @@ private:
bool ShouldAddDevice(const USB::Device& device) const override; bool ShouldAddDevice(const USB::Device& device) const override;
bool HasInterfaceNumberInIDs() const override { return true; } bool HasInterfaceNumberInIDs() const override { return true; }
ScanThread& GetScanThread() override { return m_scan_thread; }
struct AdditionalDeviceData struct AdditionalDeviceData
{ {
u8 interrupt_in_endpoint = 0; u8 interrupt_in_endpoint = 0;
u8 interrupt_out_endpoint = 0; u8 interrupt_out_endpoint = 0;
}; };
std::array<AdditionalDeviceData, 32> m_additional_device_data{}; std::array<AdditionalDeviceData, 32> m_additional_device_data{};
ScanThread m_scan_thread{this};
}; };
} // namespace IOS::HLE::Device } // namespace IOS::HLE::Device

View File

@ -22,6 +22,9 @@ USB_VEN::~USB_VEN() = default;
IPCCommandResult USB_VEN::IOCtl(const IOCtlRequest& request) IPCCommandResult USB_VEN::IOCtl(const IOCtlRequest& request)
{ {
static_assert(offsetof(USB_VEN, m_scan_thread) == sizeof(USB_VEN) - sizeof(ScanThread),
"ScanThread must be the last data member");
request.Log(GetDeviceName(), Common::Log::IOS_USB); request.Log(GetDeviceName(), Common::Log::IOS_USB);
switch (request.request) switch (request.request)
{ {

View File

@ -26,5 +26,9 @@ private:
s32 SubmitTransfer(USB::Device& device, const IOCtlVRequest& ioctlv); s32 SubmitTransfer(USB::Device& device, const IOCtlVRequest& ioctlv);
bool HasInterfaceNumberInIDs() const override { return false; } bool HasInterfaceNumberInIDs() const override { return false; }
ScanThread& GetScanThread() override { return m_scan_thread; }
ScanThread m_scan_thread{this};
}; };
} // namespace IOS::HLE::Device } // namespace IOS::HLE::Device