Announce-Service: Add conditional variable for the wait in the announce thread

This commit is contained in:
B3n30 2017-11-07 21:51:11 +01:00
parent 93742f17b3
commit eba2351f9e
4 changed files with 63 additions and 25 deletions

View file

@ -60,15 +60,53 @@ using RoomList = std::vector<Room>;
class Backend : NonCopyable { class Backend : NonCopyable {
public: public:
virtual ~Backend() = default; 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, virtual void SetRoomInformation(const std::string& uid, const std::string& name, const u16 port,
const u32 max_player, const u32 net_version, const u32 max_player, const u32 net_version,
const bool has_password, const std::string& preferred_game, const bool has_password, const std::string& preferred_game,
const u64 preferred_game_id) = 0; 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, virtual void AddPlayer(const std::string& nickname, const MacAddress& mac_address,
const u64 game_id, const std::string& game_name) = 0; 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<Common::WebResult> Announce() = 0; virtual std::future<Common::WebResult> Announce() = 0;
/**
* Empties the stored players
*/
virtual void ClearPlayers() = 0; 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<RoomList> GetRoomList(std::function<void()> func) = 0; virtual std::future<RoomList> GetRoomList(std::function<void()> func) = 0;
/**
* Sends a delete message to the announce service
*/
virtual void Delete() = 0; virtual void Delete() = 0;
}; };
@ -93,7 +131,7 @@ public:
} }
void ClearPlayers() override {} void ClearPlayers() override {}
std::future<RoomList> GetRoomList(std::function<void()> func) override { std::future<RoomList> GetRoomList(std::function<void()> func) override {
return std::async(std::launch::async, [func]() { return std::async(std::launch::deferred, [func]() {
func(); func();
return RoomList{}; return RoomList{};
}); });

View file

@ -19,7 +19,7 @@ namespace Core {
// Time between room is announced to web_service // Time between room is announced to web_service
static constexpr std::chrono::seconds announce_time_interval(15); static constexpr std::chrono::seconds announce_time_interval(15);
AnnounceMultiplayerSession::AnnounceMultiplayerSession() : announce(false), finished(true) { AnnounceMultiplayerSession::AnnounceMultiplayerSession() : announce(false) {
#ifdef ENABLE_WEB_SERVICE #ifdef ENABLE_WEB_SERVICE
backend = std::make_unique<WebService::RoomJson>( backend = std::make_unique<WebService::RoomJson>(
Settings::values.announce_multiplayer_room_endpoint_url, Settings::values.citra_username, Settings::values.announce_multiplayer_room_endpoint_url, Settings::values.citra_username,
@ -39,19 +39,19 @@ void AnnounceMultiplayerSession::Start() {
} }
void AnnounceMultiplayerSession::Stop() { void AnnounceMultiplayerSession::Stop() {
if (!announce && finished) if (!announce)
return; return;
announce = false; announce = false;
// Detaching the loop, to not wait for the sleep to finish. The loop thread will finish soon. // Detaching the loop, to not wait for the sleep to finish. The loop thread will finish soon.
if (announce_multiplayer_thread) { if (announce_multiplayer_thread) {
announce_multiplayer_thread->detach(); cv.notify_all();
announce_multiplayer_thread->join();
announce_multiplayer_thread.reset(); announce_multiplayer_thread.reset();
backend->Delete(); backend->Delete();
} }
} }
std::shared_ptr<std::function<void(const Common::WebResult&)>> AnnounceMultiplayerSession::CallbackHandle AnnounceMultiplayerSession::BindErrorCallback(
AnnounceMultiplayerSession::BindErrorCallback(
std::function<void(const Common::WebResult&)> function) { std::function<void(const Common::WebResult&)> function) {
std::lock_guard<std::mutex> lock(callback_mutex); std::lock_guard<std::mutex> lock(callback_mutex);
auto handle = std::make_shared<std::function<void(const Common::WebResult&)>>(function); auto handle = std::make_shared<std::function<void(const Common::WebResult&)>>(function);
@ -59,8 +59,7 @@ AnnounceMultiplayerSession::BindErrorCallback(
return handle; return handle;
} }
void AnnounceMultiplayerSession::UnbindErrorCallback( void AnnounceMultiplayerSession::UnbindErrorCallback(CallbackHandle handle) {
std::shared_ptr<std::function<void(const Common::WebResult&)>> handle) {
std::lock_guard<std::mutex> lock(callback_mutex); std::lock_guard<std::mutex> lock(callback_mutex);
error_callbacks.erase(handle); error_callbacks.erase(handle);
} }
@ -70,13 +69,11 @@ AnnounceMultiplayerSession::~AnnounceMultiplayerSession() {
} }
void AnnounceMultiplayerSession::AnnounceMultiplayerLoop() { void AnnounceMultiplayerSession::AnnounceMultiplayerLoop() {
while (!finished) {
std::this_thread::sleep_for(announce_time_interval / 10);
}
announce = true; announce = true;
finished = false;
std::future<Common::WebResult> future; std::future<Common::WebResult> future;
while (announce) { while (announce) {
std::unique_lock<std::mutex> lock(cv_m);
cv.wait_for(lock, announce_time_interval);
std::shared_ptr<Network::Room> room = Network::GetRoom().lock(); std::shared_ptr<Network::Room> room = Network::GetRoom().lock();
if (!room) { if (!room) {
announce = false; announce = false;
@ -107,9 +104,7 @@ void AnnounceMultiplayerSession::AnnounceMultiplayerLoop() {
} }
} }
} }
std::this_thread::sleep_for(announce_time_interval);
} }
finished = true;
} }
std::future<AnnounceMultiplayerRoom::RoomList> AnnounceMultiplayerSession::GetRoomList( std::future<AnnounceMultiplayerRoom::RoomList> AnnounceMultiplayerSession::GetRoomList(

View file

@ -5,8 +5,10 @@
#pragma once #pragma once
#include <atomic> #include <atomic>
#include <condition_variable>
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <mutex>
#include <set> #include <set>
#include <thread> #include <thread>
#include "common/announce_multiplayer_room.h" #include "common/announce_multiplayer_room.h"
@ -21,6 +23,7 @@ namespace Core {
*/ */
class AnnounceMultiplayerSession : NonCopyable { class AnnounceMultiplayerSession : NonCopyable {
public: public:
using CallbackHandle = std::shared_ptr<std::function<void(const Common::WebResult&)>>;
AnnounceMultiplayerSession(); AnnounceMultiplayerSession();
~AnnounceMultiplayerSession(); ~AnnounceMultiplayerSession();
@ -29,14 +32,13 @@ public:
* @param function The function that gets called * @param function The function that gets called
* @return A handle that can be used the unbind the function * @return A handle that can be used the unbind the function
*/ */
std::shared_ptr<std::function<void(const Common::WebResult&)>> BindErrorCallback( CallbackHandle BindErrorCallback(std::function<void(const Common::WebResult&)> function);
std::function<void(const Common::WebResult&)> function);
/** /**
* Unbind a function from the error callbacks * Unbind a function from the error callbacks
* @param handle The handle for the function that should get unbind * @param handle The handle for the function that should get unbind
*/ */
void UnbindErrorCallback(std::shared_ptr<std::function<void(const Common::WebResult&)>> handle); void UnbindErrorCallback(CallbackHandle handle);
/** /**
* Starts the announce of a room to web services * Starts the announce of a room to web services
@ -57,13 +59,16 @@ public:
private: private:
std::atomic<bool> announce{false}; std::atomic<bool> announce{false};
std::atomic<bool> 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::mutex callback_mutex;
std::set<std::shared_ptr<std::function<void(const Common::WebResult&)>>> error_callbacks; std::set<CallbackHandle> error_callbacks;
std::unique_ptr<std::thread> announce_multiplayer_thread; std::unique_ptr<std::thread> announce_multiplayer_thread;
std::unique_ptr<AnnounceMultiplayerRoom::Backend> /// Backend interface that logs fields
backend; ///< Backend interface that logs fields std::unique_ptr<AnnounceMultiplayerRoom::Backend> backend;
void AnnounceMultiplayerLoop(); void AnnounceMultiplayerLoop();
}; };

View file

@ -37,7 +37,7 @@ std::future<Common::WebResult> PostJson(const std::string& url, const std::strin
const std::string& token) { const std::string& token) {
if (url.empty()) { if (url.empty()) {
LOG_ERROR(WebService, "URL is invalid"); 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"}; return Common::WebResult{Common::WebResult::Code::InvalidURL, "URL is invalid"};
}); });
} }
@ -45,7 +45,7 @@ std::future<Common::WebResult> PostJson(const std::string& url, const std::strin
const bool are_credentials_provided{!token.empty() && !username.empty()}; const bool are_credentials_provided{!token.empty() && !username.empty()};
if (!allow_anonymous && !are_credentials_provided) { if (!allow_anonymous && !are_credentials_provided) {
LOG_ERROR(WebService, "Credentials must be provided for authenticated requests"); 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, return Common::WebResult{Common::WebResult::Code::CredentialsMissing,
"Credentials needed"}; "Credentials needed"};
}); });
@ -97,13 +97,13 @@ std::future<T> GetJson(std::function<T(const std::string&)> func, const std::str
const std::string& token) { const std::string& token) {
if (url.empty()) { if (url.empty()) {
LOG_ERROR(WebService, "URL is invalid"); 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()}; const bool are_credentials_provided{!token.empty() && !username.empty()};
if (!allow_anonymous && !are_credentials_provided) { if (!allow_anonymous && !are_credentials_provided) {
LOG_ERROR(WebService, "Credentials must be provided for authenticated requests"); 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(); Win32WSAStartup();