From 3408227bb1c72fda7573c8aa5ff06beee1ca2332 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20H=C3=B6glinger-Stelzer?= Date: Mon, 22 Apr 2019 14:03:14 +0200 Subject: [PATCH] It appears to be working --- app/Tester/Tester.cpp | 12 +++-- src/Internal.h | 10 +--- src/NotificationRequestPool.cpp | 95 +++++++++++++++++++++++---------- src/NotificationRequestPool.h | 17 ++++-- src/ViGEmClient.cpp | 91 +------------------------------ src/XusbNotificationRequest.cpp | 34 ++++++------ src/XusbNotificationRequest.h | 7 ++- 7 files changed, 115 insertions(+), 151 deletions(-) diff --git a/app/Tester/Tester.cpp b/app/Tester/Tester.cpp index f4e1b2e..fd53607 100644 --- a/app/Tester/Tester.cpp +++ b/app/Tester/Tester.cpp @@ -22,10 +22,14 @@ VOID CALLBACK notification( { m.lock(); - //std::cout.width(3); - //std::cout << (int)LargeMotor << " "; - //std::cout.width(3); - //std::cout << (int)SmallMotor << std::endl; + static int count = 1; + + std::cout.width(3); + std::cout << count++ << " "; + std::cout.width(3); + std::cout << (int)LargeMotor << " "; + std::cout.width(3); + std::cout << (int)SmallMotor << std::endl; m.unlock(); } diff --git a/src/Internal.h b/src/Internal.h index 3c3076a..07ff4db 100644 --- a/src/Internal.h +++ b/src/Internal.h @@ -22,6 +22,8 @@ typedef enum _VIGEM_TARGET_STATE VIGEM_TARGET_DISCONNECTED } VIGEM_TARGET_STATE, *PVIGEM_TARGET_STATE; +class NotificationRequestPool; + // // Represents a virtual gamepad object. // @@ -35,14 +37,6 @@ typedef struct _VIGEM_TARGET_T VIGEM_TARGET_TYPE Type; DWORD_PTR Notification; - boost::shared_ptr io_svc; - boost::shared_ptr worker; - boost::shared_ptr worker_threads; - boost::shared_ptr enqueue_lock; - std::shared_ptr pool; - HANDLE WaitHandles[VIGEM_INVERTED_CALL_THREAD_COUNT]; - std::vector notify_req; - } VIGEM_TARGET; diff --git a/src/NotificationRequestPool.cpp b/src/NotificationRequestPool.cpp index d9eda03..7ea1fcc 100644 --- a/src/NotificationRequestPool.cpp +++ b/src/NotificationRequestPool.cpp @@ -1,27 +1,48 @@ #include "NotificationRequestPool.h" +void NotificationRequestPool::strand_dispatch_worker() const +{ + io_svc_->run(); +} + NotificationRequestPool::NotificationRequestPool( - HANDLE bus, - const ULONG serial, - PFN_VIGEM_X360_NOTIFICATION callback) : + PVIGEM_CLIENT client, + PVIGEM_TARGET target, + PFN_VIGEM_X360_NOTIFICATION callback +) : + client_(client), + target_(target), callback_(callback), stop_(false) { + // prepare array of handles and request wrappers for (auto& wait_handle : wait_handles_) { + // create auto-reset event wait_handle = CreateEvent(nullptr, FALSE, FALSE, nullptr); - XusbNotificationRequest req(bus, serial, wait_handle); - requests_.push_back(std::ref(req)); + // create async pending I/O request wrapper + auto req = new XusbNotificationRequest( + client_->hBusDevice, + target_->SerialNo, + wait_handle + ); + requests_.push_back(req); } + // init ASIO io_svc_.reset(new boost::asio::io_service()); worker_.reset(new boost::asio::io_service::work(*io_svc_)); - worker_threads_.reset(new boost::thread_group()); + // launch notification completion thread thread_ = std::make_shared(boost::ref(*this)); + // launch boost I/O service dispatcher thread + worker_thread_ = std::make_shared( + boost::bind(&NotificationRequestPool::strand_dispatch_worker, this)); + + // submit pending I/O to driver for (auto& request : requests_) - request.get().request_async(); + request->request_async(); } NotificationRequestPool::~NotificationRequestPool() @@ -29,46 +50,64 @@ NotificationRequestPool::~NotificationRequestPool() for (auto& wait_handle : wait_handles_) CloseHandle(wait_handle); + io_svc_->stop(); + worker_thread_->join(); + thread_->join(); } void NotificationRequestPool::operator()() { + // used to dispatch notification callback boost::asio::io_service::strand strand(*io_svc_); while (true) { + // wait for the first pending I/O to signal its event const auto ret = WaitForMultipleObjects( requests_.size(), wait_handles_, FALSE, - INFINITE + 25 ); + // timeout has occurred... + if (ret == WAIT_TIMEOUT) + { + // ...check for termination request + boost::mutex::scoped_lock lock(m_); + if (stop_) + // exits function (terminates thread) + break; + + // go for another round + continue; + } + + // index of the request which just got completed const auto index = ret - WAIT_OBJECT_0; - auto& req = requests_[index].get(); + // grab associated request + const auto req = requests_[index]; - //const boost::function pfn = callback_; + // prepare queueing library caller notification callback + const boost::function pfn = callback_; - //strand.post(boost::bind(pfn, - // client, - // target, - // notify.LargeMotor, - // notify.SmallMotor, - // notify.LedNumber - //)); + // submit callback for async yet ordered invocation + strand.post(boost::bind(pfn, + client_, + target_, + req->get_large_motor(), + req->get_small_motor(), + req->get_led_number() + )); - req.request_async(); - - boost::mutex::scoped_lock lock(m_); - if (stop_) - break; + // submit another pending I/O + req->request_async(); } } diff --git a/src/NotificationRequestPool.h b/src/NotificationRequestPool.h index 238692d..9ec91a7 100644 --- a/src/NotificationRequestPool.h +++ b/src/NotificationRequestPool.h @@ -7,6 +7,7 @@ #include #include "XusbNotificationRequest.h" #include "ViGEm/Client.h" +#include "Internal.h" #define VIGEM_INVERTED_CALL_THREAD_COUNT 20 @@ -14,8 +15,12 @@ class NotificationRequestPool { HANDLE wait_handles_[VIGEM_INVERTED_CALL_THREAD_COUNT]{}; + + PVIGEM_CLIENT client_; + PVIGEM_TARGET target_; PFN_VIGEM_X360_NOTIFICATION callback_; - std::vector> requests_; + + std::vector requests_; std::shared_ptr thread_; boost::mutex m_; boost::condition_variable cv_; @@ -23,10 +28,16 @@ class NotificationRequestPool boost::shared_ptr io_svc_; boost::shared_ptr worker_; - boost::shared_ptr worker_threads_; + std::shared_ptr worker_thread_; + + void strand_dispatch_worker() const; public: - NotificationRequestPool(HANDLE bus, ULONG serial, PFN_VIGEM_X360_NOTIFICATION callback); + NotificationRequestPool( + PVIGEM_CLIENT client, + PVIGEM_TARGET target, + PFN_VIGEM_X360_NOTIFICATION callback + ); ~NotificationRequestPool(); void operator()(); diff --git a/src/ViGEmClient.cpp b/src/ViGEmClient.cpp index 017af7f..fbd955a 100644 --- a/src/ViGEmClient.cpp +++ b/src/ViGEmClient.cpp @@ -98,14 +98,6 @@ PVIGEM_TARGET FORCEINLINE VIGEM_TARGET_ALLOC_INIT( target->State = VIGEM_TARGET_INITIALIZED; target->Type = Type; - target->io_svc.reset(new boost::asio::io_service()); - target->worker.reset(new boost::asio::io_service::work(*target->io_svc)); - target->worker_threads.reset(new boost::thread_group()); - target->enqueue_lock.reset(new boost::mutex()); - - for (auto& WaitHandle : target->WaitHandles) - WaitHandle = CreateEvent(nullptr, FALSE, FALSE, nullptr); - return target; } @@ -139,81 +131,6 @@ LONG WINAPI vigem_internal_exception_handler(struct _EXCEPTION_POINTERS* apExcep return EXCEPTION_CONTINUE_SEARCH; } -void vigem_internal_x360_notification_worker( - PVIGEM_TARGET target, - PVIGEM_CLIENT client -) -{ - DWORD error = ERROR_SUCCESS; - DWORD transferred = 0; - OVERLAPPED lOverlapped = { 0 }; - lOverlapped.hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); - boost::asio::io_service::strand strand(*target->io_svc); - - XUSB_REQUEST_NOTIFICATION notify; - XUSB_REQUEST_NOTIFICATION_INIT(¬ify, target->SerialNo); - - static std::mutex m; - - do - { - DeviceIoControl(client->hBusDevice, - IOCTL_XUSB_REQUEST_NOTIFICATION, - ¬ify, - notify.Size, - ¬ify, - notify.Size, - &transferred, - &lOverlapped); - - if (GetOverlappedResult(client->hBusDevice, &lOverlapped, &transferred, TRUE) != 0) - { - m.lock(); - auto timestamp = std::chrono::high_resolution_clock::now(); - std::cout << std::chrono::duration_cast(timestamp.time_since_epoch()).count() << " "; - std::cout.width(3); - std::cout << (int)notify.LargeMotor << " "; - std::cout.width(3); - std::cout << (int)notify.SmallMotor << std::endl; - m.unlock(); - - if (target->Notification == NULL) - { - if (lOverlapped.hEvent) - CloseHandle(lOverlapped.hEvent); - return; - } - - target->enqueue_lock->lock(); - const boost::function pfn = PFN_VIGEM_X360_NOTIFICATION(target->Notification); - - strand.post(boost::bind(pfn, - client, - target, - notify.LargeMotor, - notify.SmallMotor, - notify.LedNumber - )); - target->enqueue_lock->unlock(); - - target->io_svc->poll(); - } - else - { - error = GetLastError(); - } - } while (error != ERROR_OPERATION_ABORTED && error != ERROR_ACCESS_DENIED); - - if (lOverlapped.hEvent) - CloseHandle(lOverlapped.hEvent); -} - PVIGEM_CLIENT vigem_alloc() { SetUnhandledExceptionFilter(vigem_internal_exception_handler); @@ -392,11 +309,7 @@ PVIGEM_TARGET vigem_target_ds4_alloc(void) void vigem_target_free(PVIGEM_TARGET target) { if (target) - { - target->io_svc->stop(); - free(target); - } } VIGEM_ERROR vigem_target_add(PVIGEM_CLIENT vigem, PVIGEM_TARGET target) @@ -605,8 +518,8 @@ VIGEM_ERROR vigem_target_x360_register_notification( //} target->pool = std::make_shared( - vigem->hBusDevice, - target->SerialNo, + vigem, + target, PFN_VIGEM_X360_NOTIFICATION(target->Notification) ); diff --git a/src/XusbNotificationRequest.cpp b/src/XusbNotificationRequest.cpp index 9944bcd..6ccc7a6 100644 --- a/src/XusbNotificationRequest.cpp +++ b/src/XusbNotificationRequest.cpp @@ -5,41 +5,45 @@ XusbNotificationRequest::XusbNotificationRequest( HANDLE bus, ULONG serial, HANDLE notification -) : parent_bus(bus), - payload(), transferred(0), - overlapped() +) : parent_bus_(bus), + payload_(), + overlapped_() { - overlapped.hEvent = notification; - XUSB_REQUEST_NOTIFICATION_INIT(&payload, serial); + memset(&overlapped_, 0, sizeof(OVERLAPPED)); + overlapped_.hEvent = notification; + XUSB_REQUEST_NOTIFICATION_INIT(&payload_, serial); } bool XusbNotificationRequest::request_async() { + // queue request in driver const auto ret = DeviceIoControl( - parent_bus, + parent_bus_, IOCTL_XUSB_REQUEST_NOTIFICATION, - &payload, - payload.Size, - &payload, - payload.Size, + &payload_, + payload_.Size, + &payload_, + payload_.Size, nullptr, - &overlapped + &overlapped_ ); - return (!ret && GetLastError() == ERROR_IO_PENDING); + const auto error = GetLastError(); + + return (!ret && error == ERROR_IO_PENDING); } UCHAR XusbNotificationRequest::get_led_number() const { - return payload.LedNumber; + return payload_.LedNumber; } UCHAR XusbNotificationRequest::get_large_motor() const { - return payload.LargeMotor; + return payload_.LargeMotor; } UCHAR XusbNotificationRequest::get_small_motor() const { - return payload.SmallMotor; + return payload_.SmallMotor; } diff --git a/src/XusbNotificationRequest.h b/src/XusbNotificationRequest.h index 0aa3f8f..dd6b2b0 100644 --- a/src/XusbNotificationRequest.h +++ b/src/XusbNotificationRequest.h @@ -5,10 +5,9 @@ class XusbNotificationRequest { - HANDLE parent_bus; - XUSB_REQUEST_NOTIFICATION payload; - DWORD transferred; - OVERLAPPED overlapped; + HANDLE parent_bus_; + XUSB_REQUEST_NOTIFICATION payload_; + OVERLAPPED overlapped_; public: XusbNotificationRequest(HANDLE bus, ULONG serial, HANDLE notification);