diff --git a/src/common/announce_multiplayer_room.h b/src/common/announce_multiplayer_room.h index 091dded8f..f01e7cf74 100644 --- a/src/common/announce_multiplayer_room.h +++ b/src/common/announce_multiplayer_room.h @@ -60,15 +60,53 @@ using RoomList = std::vector; class Backend : NonCopyable { public: virtual ~Backend() = default; + + /** + * Sets the Information that gets used for the announce + * @param uid The Id of the room + * @param name The name of the room + * @param port The port of the room + * @param net_version The version of the libNetwork that gets used + * @param has_password True if the room is passowrd protected + * @param preferred_game The preferred game of the room + * @param preferred_game_id The title id of the preferred game + */ virtual void SetRoomInformation(const std::string& uid, const std::string& name, const u16 port, const u32 max_player, const u32 net_version, const bool has_password, const std::string& preferred_game, const u64 preferred_game_id) = 0; + /** + * Adds a player information to the data that gets announced + * @param nickname The nickname of the player + * @param mac_address The MAC Address of the player + * @param game_id The title id of the game the player plays + * @param game_name The name of the game the player plays + */ virtual void AddPlayer(const std::string& nickname, const MacAddress& mac_address, const u64 game_id, const std::string& game_name) = 0; + + /** + * Send the data to the announce service + * @result The result of the announce attempt + */ virtual std::future Announce() = 0; + + /** + * Empties the stored players + */ virtual void ClearPlayers() = 0; + + /** + * Get the room information from the announce service + * @param func a function that gets exectued when the get finished. + * Can be used as a callback + * @result A list of all rooms the announce service has + */ virtual std::future GetRoomList(std::function func) = 0; + + /** + * Sends a delete message to the announce service + */ virtual void Delete() = 0; }; @@ -93,7 +131,7 @@ public: } void ClearPlayers() override {} std::future GetRoomList(std::function func) override { - return std::async(std::launch::async, [func]() { + return std::async(std::launch::deferred, [func]() { func(); return RoomList{}; }); diff --git a/src/core/announce_multiplayer_session.cpp b/src/core/announce_multiplayer_session.cpp index a3ba78d78..af7d4fe58 100644 --- a/src/core/announce_multiplayer_session.cpp +++ b/src/core/announce_multiplayer_session.cpp @@ -19,7 +19,7 @@ namespace Core { // Time between room is announced to web_service static constexpr std::chrono::seconds announce_time_interval(15); -AnnounceMultiplayerSession::AnnounceMultiplayerSession() : announce(false), finished(true) { +AnnounceMultiplayerSession::AnnounceMultiplayerSession() : announce(false) { #ifdef ENABLE_WEB_SERVICE backend = std::make_unique( Settings::values.announce_multiplayer_room_endpoint_url, Settings::values.citra_username, @@ -39,19 +39,19 @@ void AnnounceMultiplayerSession::Start() { } void AnnounceMultiplayerSession::Stop() { - if (!announce && finished) + if (!announce) return; announce = false; // Detaching the loop, to not wait for the sleep to finish. The loop thread will finish soon. if (announce_multiplayer_thread) { - announce_multiplayer_thread->detach(); + cv.notify_all(); + announce_multiplayer_thread->join(); announce_multiplayer_thread.reset(); backend->Delete(); } } -std::shared_ptr> -AnnounceMultiplayerSession::BindErrorCallback( +AnnounceMultiplayerSession::CallbackHandle AnnounceMultiplayerSession::BindErrorCallback( std::function function) { std::lock_guard lock(callback_mutex); auto handle = std::make_shared>(function); @@ -59,8 +59,7 @@ AnnounceMultiplayerSession::BindErrorCallback( return handle; } -void AnnounceMultiplayerSession::UnbindErrorCallback( - std::shared_ptr> handle) { +void AnnounceMultiplayerSession::UnbindErrorCallback(CallbackHandle handle) { std::lock_guard lock(callback_mutex); error_callbacks.erase(handle); } @@ -70,13 +69,11 @@ AnnounceMultiplayerSession::~AnnounceMultiplayerSession() { } void AnnounceMultiplayerSession::AnnounceMultiplayerLoop() { - while (!finished) { - std::this_thread::sleep_for(announce_time_interval / 10); - } announce = true; - finished = false; std::future future; while (announce) { + std::unique_lock lock(cv_m); + cv.wait_for(lock, announce_time_interval); std::shared_ptr room = Network::GetRoom().lock(); if (!room) { announce = false; @@ -107,9 +104,7 @@ void AnnounceMultiplayerSession::AnnounceMultiplayerLoop() { } } } - std::this_thread::sleep_for(announce_time_interval); } - finished = true; } std::future AnnounceMultiplayerSession::GetRoomList( diff --git a/src/core/announce_multiplayer_session.h b/src/core/announce_multiplayer_session.h index a7a36b82d..6faea3ae3 100644 --- a/src/core/announce_multiplayer_session.h +++ b/src/core/announce_multiplayer_session.h @@ -5,8 +5,10 @@ #pragma once #include +#include #include #include +#include #include #include #include "common/announce_multiplayer_room.h" @@ -21,6 +23,7 @@ namespace Core { */ class AnnounceMultiplayerSession : NonCopyable { public: + using CallbackHandle = std::shared_ptr>; AnnounceMultiplayerSession(); ~AnnounceMultiplayerSession(); @@ -29,14 +32,13 @@ public: * @param function The function that gets called * @return A handle that can be used the unbind the function */ - std::shared_ptr> BindErrorCallback( - std::function function); + CallbackHandle BindErrorCallback(std::function function); /** * Unbind a function from the error callbacks * @param handle The handle for the function that should get unbind */ - void UnbindErrorCallback(std::shared_ptr> handle); + void UnbindErrorCallback(CallbackHandle handle); /** * Starts the announce of a room to web services @@ -57,13 +59,16 @@ public: private: std::atomic announce{false}; - std::atomic finished{true}; + + /// conditional variable to notify the announce thread to end early + std::condition_variable cv; + std::mutex cv_m; ///< mutex for cv std::mutex callback_mutex; - std::set>> error_callbacks; + std::set error_callbacks; std::unique_ptr announce_multiplayer_thread; - std::unique_ptr - backend; ///< Backend interface that logs fields + /// Backend interface that logs fields + std::unique_ptr backend; void AnnounceMultiplayerLoop(); }; diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp index 2b0f5a482..64f6a69bd 100644 --- a/src/web_service/web_backend.cpp +++ b/src/web_service/web_backend.cpp @@ -37,7 +37,7 @@ std::future PostJson(const std::string& url, const std::strin const std::string& token) { if (url.empty()) { LOG_ERROR(WebService, "URL is invalid"); - return std::async(std::launch::async, []() { + return std::async(std::launch::deferred, []() { return Common::WebResult{Common::WebResult::Code::InvalidURL, "URL is invalid"}; }); } @@ -45,7 +45,7 @@ std::future PostJson(const std::string& url, const std::strin const bool are_credentials_provided{!token.empty() && !username.empty()}; if (!allow_anonymous && !are_credentials_provided) { LOG_ERROR(WebService, "Credentials must be provided for authenticated requests"); - return std::async(std::launch::async, []() { + return std::async(std::launch::deferred, []() { return Common::WebResult{Common::WebResult::Code::CredentialsMissing, "Credentials needed"}; }); @@ -97,13 +97,13 @@ std::future GetJson(std::function func, const std::str const std::string& token) { if (url.empty()) { LOG_ERROR(WebService, "URL is invalid"); - return std::async(std::launch::async, [func{std::move(func)}]() { return func(""); }); + return std::async(std::launch::deferred, [func{std::move(func)}]() { return func(""); }); } const bool are_credentials_provided{!token.empty() && !username.empty()}; if (!allow_anonymous && !are_credentials_provided) { LOG_ERROR(WebService, "Credentials must be provided for authenticated requests"); - return std::async(std::launch::async, [func{std::move(func)}]() { return func(""); }); + return std::async(std::launch::deferred, [func{std::move(func)}]() { return func(""); }); } Win32WSAStartup();