diff options
| author | 2021-12-17 23:43:19 -0800 | |
|---|---|---|
| committer | 2021-12-17 23:43:19 -0800 | |
| commit | 212b497d5c60676036dcd541ca54cf0ab260f344 (patch) | |
| tree | bdfb846bdec1bbd04cf827017f3adba5cccb9a6c /src | |
| parent | Merge pull request #7399 from ameerj/art-refactor (diff) | |
| parent | [input_common] Move variable declaration closer to usage (diff) | |
| download | yuzu-212b497d5c60676036dcd541ca54cf0ab260f344.tar.gz yuzu-212b497d5c60676036dcd541ca54cf0ab260f344.tar.xz yuzu-212b497d5c60676036dcd541ca54cf0ab260f344.zip | |
Merge pull request #7302 from VPeruS/check-deadlock
[input_common] Fixed thread hang
Diffstat (limited to 'src')
| -rw-r--r-- | src/input_common/drivers/udp_client.cpp | 74 | ||||
| -rw-r--r-- | src/input_common/helpers/udp_protocol.h | 21 | ||||
| -rw-r--r-- | src/tests/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | src/tests/input_common/calibration_configuration_job.cpp | 136 |
4 files changed, 190 insertions, 44 deletions
diff --git a/src/input_common/drivers/udp_client.cpp b/src/input_common/drivers/udp_client.cpp index 4ab991a7d..a1ce4525d 100644 --- a/src/input_common/drivers/udp_client.cpp +++ b/src/input_common/drivers/udp_client.cpp | |||
| @@ -536,42 +536,46 @@ CalibrationConfigurationJob::CalibrationConfigurationJob( | |||
| 536 | std::function<void(u16, u16, u16, u16)> data_callback) { | 536 | std::function<void(u16, u16, u16, u16)> data_callback) { |
| 537 | 537 | ||
| 538 | std::thread([=, this] { | 538 | std::thread([=, this] { |
| 539 | u16 min_x{UINT16_MAX}; | ||
| 540 | u16 min_y{UINT16_MAX}; | ||
| 541 | u16 max_x{}; | ||
| 542 | u16 max_y{}; | ||
| 543 | |||
| 539 | Status current_status{Status::Initialized}; | 544 | Status current_status{Status::Initialized}; |
| 540 | SocketCallback callback{ | 545 | SocketCallback callback{[](Response::Version) {}, [](Response::PortInfo) {}, |
| 541 | [](Response::Version) {}, [](Response::PortInfo) {}, | 546 | [&](Response::PadData data) { |
| 542 | [&](Response::PadData data) { | 547 | constexpr u16 CALIBRATION_THRESHOLD = 100; |
| 543 | static constexpr u16 CALIBRATION_THRESHOLD = 100; | 548 | |
| 544 | static constexpr u16 MAX_VALUE = UINT16_MAX; | 549 | if (current_status == Status::Initialized) { |
| 545 | 550 | // Receiving data means the communication is ready now | |
| 546 | if (current_status == Status::Initialized) { | 551 | current_status = Status::Ready; |
| 547 | // Receiving data means the communication is ready now | 552 | status_callback(current_status); |
| 548 | current_status = Status::Ready; | 553 | } |
| 549 | status_callback(current_status); | 554 | if (data.touch[0].is_active == 0) { |
| 550 | } | 555 | return; |
| 551 | const auto& touchpad_0 = data.touch[0]; | 556 | } |
| 552 | if (touchpad_0.is_active == 0) { | 557 | LOG_DEBUG(Input, "Current touch: {} {}", data.touch[0].x, |
| 553 | return; | 558 | data.touch[0].y); |
| 554 | } | 559 | min_x = std::min(min_x, static_cast<u16>(data.touch[0].x)); |
| 555 | LOG_DEBUG(Input, "Current touch: {} {}", touchpad_0.x, touchpad_0.y); | 560 | min_y = std::min(min_y, static_cast<u16>(data.touch[0].y)); |
| 556 | const u16 min_x = std::min(MAX_VALUE, static_cast<u16>(touchpad_0.x)); | 561 | if (current_status == Status::Ready) { |
| 557 | const u16 min_y = std::min(MAX_VALUE, static_cast<u16>(touchpad_0.y)); | 562 | // First touch - min data (min_x/min_y) |
| 558 | if (current_status == Status::Ready) { | 563 | current_status = Status::Stage1Completed; |
| 559 | // First touch - min data (min_x/min_y) | 564 | status_callback(current_status); |
| 560 | current_status = Status::Stage1Completed; | 565 | } |
| 561 | status_callback(current_status); | 566 | if (data.touch[0].x - min_x > CALIBRATION_THRESHOLD && |
| 562 | } | 567 | data.touch[0].y - min_y > CALIBRATION_THRESHOLD) { |
| 563 | if (touchpad_0.x - min_x > CALIBRATION_THRESHOLD && | 568 | // Set the current position as max value and finishes |
| 564 | touchpad_0.y - min_y > CALIBRATION_THRESHOLD) { | 569 | // configuration |
| 565 | // Set the current position as max value and finishes configuration | 570 | max_x = data.touch[0].x; |
| 566 | const u16 max_x = touchpad_0.x; | 571 | max_y = data.touch[0].y; |
| 567 | const u16 max_y = touchpad_0.y; | 572 | current_status = Status::Completed; |
| 568 | current_status = Status::Completed; | 573 | data_callback(min_x, min_y, max_x, max_y); |
| 569 | data_callback(min_x, min_y, max_x, max_y); | 574 | status_callback(current_status); |
| 570 | status_callback(current_status); | 575 | |
| 571 | 576 | complete_event.Set(); | |
| 572 | complete_event.Set(); | 577 | } |
| 573 | } | 578 | }}; |
| 574 | }}; | ||
| 575 | Socket socket{host, port, std::move(callback)}; | 579 | Socket socket{host, port, std::move(callback)}; |
| 576 | std::thread worker_thread{SocketLoop, &socket}; | 580 | std::thread worker_thread{SocketLoop, &socket}; |
| 577 | complete_event.Wait(); | 581 | complete_event.Wait(); |
diff --git a/src/input_common/helpers/udp_protocol.h b/src/input_common/helpers/udp_protocol.h index bcba12c58..2d5d54ddb 100644 --- a/src/input_common/helpers/udp_protocol.h +++ b/src/input_common/helpers/udp_protocol.h | |||
| @@ -54,6 +54,18 @@ struct Message { | |||
| 54 | template <typename T> | 54 | template <typename T> |
| 55 | constexpr Type GetMessageType(); | 55 | constexpr Type GetMessageType(); |
| 56 | 56 | ||
| 57 | template <typename T> | ||
| 58 | Message<T> CreateMessage(const u32 magic, const T data, const u32 sender_id) { | ||
| 59 | boost::crc_32_type crc; | ||
| 60 | Header header{ | ||
| 61 | magic, PROTOCOL_VERSION, sizeof(T) + sizeof(Type), 0, sender_id, GetMessageType<T>(), | ||
| 62 | }; | ||
| 63 | Message<T> message{header, data}; | ||
| 64 | crc.process_bytes(&message, sizeof(Message<T>)); | ||
| 65 | message.header.crc = crc.checksum(); | ||
| 66 | return message; | ||
| 67 | } | ||
| 68 | |||
| 57 | namespace Request { | 69 | namespace Request { |
| 58 | 70 | ||
| 59 | enum RegisterFlags : u8 { | 71 | enum RegisterFlags : u8 { |
| @@ -101,14 +113,7 @@ static_assert(std::is_trivially_copyable_v<PadData>, | |||
| 101 | */ | 113 | */ |
| 102 | template <typename T> | 114 | template <typename T> |
| 103 | Message<T> Create(const T data, const u32 client_id = 0) { | 115 | Message<T> Create(const T data, const u32 client_id = 0) { |
| 104 | boost::crc_32_type crc; | 116 | return CreateMessage(CLIENT_MAGIC, data, client_id); |
| 105 | Header header{ | ||
| 106 | CLIENT_MAGIC, PROTOCOL_VERSION, sizeof(T) + sizeof(Type), 0, client_id, GetMessageType<T>(), | ||
| 107 | }; | ||
| 108 | Message<T> message{header, data}; | ||
| 109 | crc.process_bytes(&message, sizeof(Message<T>)); | ||
| 110 | message.header.crc = crc.checksum(); | ||
| 111 | return message; | ||
| 112 | } | 117 | } |
| 113 | } // namespace Request | 118 | } // namespace Request |
| 114 | 119 | ||
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index c4c012f3d..4a20c0768 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt | |||
| @@ -10,11 +10,12 @@ add_executable(tests | |||
| 10 | core/network/network.cpp | 10 | core/network/network.cpp |
| 11 | tests.cpp | 11 | tests.cpp |
| 12 | video_core/buffer_base.cpp | 12 | video_core/buffer_base.cpp |
| 13 | input_common/calibration_configuration_job.cpp | ||
| 13 | ) | 14 | ) |
| 14 | 15 | ||
| 15 | create_target_directory_groups(tests) | 16 | create_target_directory_groups(tests) |
| 16 | 17 | ||
| 17 | target_link_libraries(tests PRIVATE common core) | 18 | target_link_libraries(tests PRIVATE common core input_common) |
| 18 | target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} catch-single-include Threads::Threads) | 19 | target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} catch-single-include Threads::Threads) |
| 19 | 20 | ||
| 20 | add_test(NAME tests COMMAND tests) | 21 | add_test(NAME tests COMMAND tests) |
diff --git a/src/tests/input_common/calibration_configuration_job.cpp b/src/tests/input_common/calibration_configuration_job.cpp new file mode 100644 index 000000000..8c77d81e9 --- /dev/null +++ b/src/tests/input_common/calibration_configuration_job.cpp | |||
| @@ -0,0 +1,136 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <array> | ||
| 6 | #include <string> | ||
| 7 | #include <thread> | ||
| 8 | #include <boost/asio.hpp> | ||
| 9 | #include <boost/crc.hpp> | ||
| 10 | #include <catch2/catch.hpp> | ||
| 11 | |||
| 12 | #include "input_common/drivers/udp_client.h" | ||
| 13 | #include "input_common/helpers/udp_protocol.h" | ||
| 14 | |||
| 15 | class FakeCemuhookServer { | ||
| 16 | public: | ||
| 17 | FakeCemuhookServer() | ||
| 18 | : socket(io_service, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 0)) {} | ||
| 19 | |||
| 20 | ~FakeCemuhookServer() { | ||
| 21 | is_running = false; | ||
| 22 | boost::system::error_code error_code; | ||
| 23 | socket.shutdown(boost::asio::socket_base::shutdown_both, error_code); | ||
| 24 | socket.close(); | ||
| 25 | if (handler.joinable()) { | ||
| 26 | handler.join(); | ||
| 27 | } | ||
| 28 | } | ||
| 29 | |||
| 30 | u16 GetPort() { | ||
| 31 | return socket.local_endpoint().port(); | ||
| 32 | } | ||
| 33 | |||
| 34 | std::string GetHost() { | ||
| 35 | return socket.local_endpoint().address().to_string(); | ||
| 36 | } | ||
| 37 | |||
| 38 | void Run(const std::vector<InputCommon::CemuhookUDP::Response::TouchPad> touch_movement_path) { | ||
| 39 | constexpr size_t HeaderSize = sizeof(InputCommon::CemuhookUDP::Header); | ||
| 40 | constexpr size_t PadDataSize = | ||
| 41 | sizeof(InputCommon::CemuhookUDP::Message<InputCommon::CemuhookUDP::Response::PadData>); | ||
| 42 | |||
| 43 | REQUIRE(touch_movement_path.size() > 0); | ||
| 44 | is_running = true; | ||
| 45 | handler = std::thread([touch_movement_path, this]() { | ||
| 46 | auto current_touch_position = touch_movement_path.begin(); | ||
| 47 | while (is_running) { | ||
| 48 | boost::asio::ip::udp::endpoint sender_endpoint; | ||
| 49 | boost::system::error_code error_code; | ||
| 50 | auto received_size = socket.receive_from(boost::asio::buffer(receive_buffer), | ||
| 51 | sender_endpoint, 0, error_code); | ||
| 52 | |||
| 53 | if (received_size < HeaderSize) { | ||
| 54 | continue; | ||
| 55 | } | ||
| 56 | |||
| 57 | InputCommon::CemuhookUDP::Header header{}; | ||
| 58 | std::memcpy(&header, receive_buffer.data(), HeaderSize); | ||
| 59 | switch (header.type) { | ||
| 60 | case InputCommon::CemuhookUDP::Type::PadData: { | ||
| 61 | InputCommon::CemuhookUDP::Response::PadData pad_data{}; | ||
| 62 | pad_data.touch[0] = *current_touch_position; | ||
| 63 | const auto pad_message = InputCommon::CemuhookUDP::CreateMessage( | ||
| 64 | InputCommon::CemuhookUDP::SERVER_MAGIC, pad_data, 0); | ||
| 65 | std::memcpy(send_buffer.data(), &pad_message, PadDataSize); | ||
| 66 | socket.send_to(boost::asio::buffer(send_buffer, PadDataSize), sender_endpoint, | ||
| 67 | 0, error_code); | ||
| 68 | |||
| 69 | bool can_advance = | ||
| 70 | std::next(current_touch_position) != touch_movement_path.end(); | ||
| 71 | if (can_advance) { | ||
| 72 | std::advance(current_touch_position, 1); | ||
| 73 | } | ||
| 74 | break; | ||
| 75 | } | ||
| 76 | case InputCommon::CemuhookUDP::Type::PortInfo: | ||
| 77 | case InputCommon::CemuhookUDP::Type::Version: | ||
| 78 | default: | ||
| 79 | break; | ||
| 80 | } | ||
| 81 | } | ||
| 82 | }); | ||
| 83 | } | ||
| 84 | |||
| 85 | private: | ||
| 86 | boost::asio::io_service io_service; | ||
| 87 | boost::asio::ip::udp::socket socket; | ||
| 88 | std::array<u8, InputCommon::CemuhookUDP::MAX_PACKET_SIZE> send_buffer; | ||
| 89 | std::array<u8, InputCommon::CemuhookUDP::MAX_PACKET_SIZE> receive_buffer; | ||
| 90 | bool is_running = false; | ||
| 91 | std::thread handler; | ||
| 92 | }; | ||
| 93 | |||
| 94 | TEST_CASE("CalibrationConfigurationJob completed", "[input_common]") { | ||
| 95 | Common::Event complete_event; | ||
| 96 | FakeCemuhookServer server; | ||
| 97 | server.Run({{ | ||
| 98 | .is_active = 1, | ||
| 99 | .x = 0, | ||
| 100 | .y = 0, | ||
| 101 | }, | ||
| 102 | { | ||
| 103 | .is_active = 1, | ||
| 104 | .x = 200, | ||
| 105 | .y = 200, | ||
| 106 | }}); | ||
| 107 | |||
| 108 | InputCommon::CemuhookUDP::CalibrationConfigurationJob::Status status{}; | ||
| 109 | u16 min_x{}; | ||
| 110 | u16 min_y{}; | ||
| 111 | u16 max_x{}; | ||
| 112 | u16 max_y{}; | ||
| 113 | InputCommon::CemuhookUDP::CalibrationConfigurationJob job( | ||
| 114 | server.GetHost(), server.GetPort(), | ||
| 115 | [&status, | ||
| 116 | &complete_event](InputCommon::CemuhookUDP::CalibrationConfigurationJob::Status status_) { | ||
| 117 | status = status_; | ||
| 118 | if (status == | ||
| 119 | InputCommon::CemuhookUDP::CalibrationConfigurationJob::Status::Completed) { | ||
| 120 | complete_event.Set(); | ||
| 121 | } | ||
| 122 | }, | ||
| 123 | [&](u16 min_x_, u16 min_y_, u16 max_x_, u16 max_y_) { | ||
| 124 | min_x = min_x_; | ||
| 125 | min_y = min_y_; | ||
| 126 | max_x = max_x_; | ||
| 127 | max_y = max_y_; | ||
| 128 | }); | ||
| 129 | |||
| 130 | complete_event.WaitUntil(std::chrono::system_clock::now() + std::chrono::seconds(10)); | ||
| 131 | REQUIRE(status == InputCommon::CemuhookUDP::CalibrationConfigurationJob::Status::Completed); | ||
| 132 | REQUIRE(min_x == 0); | ||
| 133 | REQUIRE(min_y == 0); | ||
| 134 | REQUIRE(max_x == 200); | ||
| 135 | REQUIRE(max_y == 200); | ||
| 136 | } | ||