diff --git a/src/gui/panels/remote_peer_panel.cpp b/src/gui/panels/remote_peer_panel.cpp index fdd56db..ec5243e 100644 --- a/src/gui/panels/remote_peer_panel.cpp +++ b/src/gui/panels/remote_peer_panel.cpp @@ -211,7 +211,7 @@ int Render::ConnectTo(const std::string& remote_id, const char* password, props->control_window_max_width_ = title_bar_height_ * 10.0f; props->control_window_max_height_ = title_bar_height_ * 7.0f; - props->connection_status_ = ConnectionStatus::Connecting; + props->connection_status_.store(ConnectionStatus::Connecting); show_connection_status_window_ = true; if (!props->peer_) { @@ -231,7 +231,7 @@ int Render::ConnectTo(const std::string& remote_id, const char* password, AddDataStream(props->peer_, props->file_feedback_label_.c_str(), true); AddDataStream(props->peer_, props->clipboard_label_.c_str(), true); - props->connection_status_ = ConnectionStatus::Connecting; + props->connection_status_.store(ConnectionStatus::Connecting); peer_to_init = props->peer_; local_id = props->local_id_; diff --git a/src/gui/render.cpp b/src/gui/render.cpp index d9e353c..041ab53 100644 --- a/src/gui/render.cpp +++ b/src/gui/render.cpp @@ -13,6 +13,9 @@ #include #include #include +#include +#include +#include #include #include @@ -2175,11 +2178,13 @@ void Render::HandleWindowsServiceIntegration() { return; } - const bool has_connected_remote = - std::any_of(connection_status_.begin(), connection_status_.end(), - [](const auto& entry) { - return entry.second == ConnectionStatus::Connected; - }); + const bool has_connected_remote = [&] { + std::shared_lock lock(connection_status_mutex_); + return std::any_of(connection_status_.begin(), connection_status_.end(), + [](const auto& entry) { + return entry.second == ConnectionStatus::Connected; + }); + }(); if (!has_connected_remote) { ResetLocalWindowsServiceState(false); return; diff --git a/src/gui/render.h b/src/gui/render.h index 46aaa2c..d62eb29 100644 --- a/src/gui/render.h +++ b/src/gui/render.h @@ -184,7 +184,11 @@ class Render { SDL_Rect stream_render_rect_; SDL_Rect stream_render_rect_last_; ImVec2 control_window_pos_; - ConnectionStatus connection_status_ = ConnectionStatus::Closed; + // Written from the minirtc/libnice callback thread (OnConnectionStatusCb) + // and the SDL main thread (remote_peer_panel connect button); read from + // the SDL main render thread (stream/control windows) and the SDL audio + // thread (SdlCaptureAudioIn). Atomic so those reads/writes are defined. + std::atomic connection_status_ = ConnectionStatus::Closed; TraversalMode traversal_mode_ = TraversalMode::UnknownMode; int fps_ = 0; int frame_count_ = 0; @@ -816,6 +820,11 @@ class Render { void WaitForThumbnailSaveTasks(); /* ------ server mode ------ */ + // connection_status_ / connection_host_names_ are read on the main + // render thread (DrawServerWindow, HandleWindowsServiceIntegration) and + // written from minirtc/libnice callback threads (OnConnectionStatusCb, + // OnReceiveDataBufferCb). Guard every access with this shared mutex. + std::shared_mutex connection_status_mutex_; std::unordered_map connection_status_; std::unordered_map connection_host_names_; std::string selected_server_remote_id_ = ""; diff --git a/src/gui/render_callback.cpp b/src/gui/render_callback.cpp index d430134..0fe4610 100644 --- a/src/gui/render_callback.cpp +++ b/src/gui/render_callback.cpp @@ -6,6 +6,9 @@ #include #include #include +#include +#include +#include #include #include @@ -489,7 +492,7 @@ void Render::SendKeyboardHeartbeat(bool force) { } const auto props = props_it->second; - if (props->connection_status_ != ConnectionStatus::Connected || + if (props->connection_status_.load() != ConnectionStatus::Connected || !props->peer_) { last_keyboard_heartbeat_tick_ = now; return; @@ -531,7 +534,7 @@ int Render::SendKeyCommand(int key_code, bool is_down, uint32_t scan_code, if (!target_id.empty()) { if (client_properties_.find(target_id) != client_properties_.end()) { auto props = client_properties_[target_id]; - if (props->connection_status_ == ConnectionStatus::Connected && + if (props->connection_status_.load() == ConnectionStatus::Connected && props->peer_) { std::string msg = remote_action.to_json(); int ret = SendReliableDataFrame(props->peer_, msg.c_str(), msg.size(), @@ -928,10 +931,10 @@ void Render::SdlCaptureAudioIn(void* userdata, Uint8* stream, int len) { } if (1) { - // std::shared_lock lock(render->client_properties_mutex_); + std::shared_lock lock(render->client_properties_mutex_); for (const auto& it : render->client_properties_) { auto props = it.second; - if (props->connection_status_ == ConnectionStatus::Connected) { + if (props->connection_status_.load() == ConnectionStatus::Connected) { if (props->peer_) { SendAudioFrame(props->peer_, (const char*)stream, len, render->audio_label_.c_str()); @@ -1324,12 +1327,20 @@ void Render::OnReceiveDataBufferCb(const char* data, size_t size, return; } - // std::shared_lock lock(render->client_properties_mutex_); if (remote_action.type == ControlType::host_infomation) { - if (render->client_properties_.find(remote_id) != - render->client_properties_.end()) { + bool is_client_mode = false; + std::shared_ptr props; + { + std::shared_lock lock(render->client_properties_mutex_); + auto props_it = render->client_properties_.find(remote_id); + if (props_it != render->client_properties_.end()) { + is_client_mode = true; + props = props_it->second; + } + } + + if (is_client_mode) { // client mode - auto props = render->client_properties_.find(remote_id)->second; if (props && props->remote_host_name_.empty()) { props->remote_host_name_ = std::string(remote_action.i.host_name, remote_action.i.host_name_size); @@ -1345,10 +1356,13 @@ void Render::OnReceiveDataBufferCb(const char* data, size_t size, FreeRemoteAction(remote_action); } else { // server mode - render->connection_host_names_[remote_id] = std::string( - remote_action.i.host_name, remote_action.i.host_name_size); - LOG_INFO("Remote hostname: [{}]", - render->connection_host_names_[remote_id]); + std::string host_name(remote_action.i.host_name, + remote_action.i.host_name_size); + { + std::unique_lock lock(render->connection_status_mutex_); + render->connection_host_names_[remote_id] = host_name; + } + LOG_INFO("Remote hostname: [{}]", host_name); FreeRemoteAction(remote_action); } } else { @@ -1480,14 +1494,19 @@ void Render::OnConnectionStatusCb(ConnectionStatus status, const char* user_id, if (!render) return; std::string remote_id(user_id, user_id_size); - // std::shared_lock lock(render->client_properties_mutex_); - auto it = render->client_properties_.find(remote_id); - auto props = (it != render->client_properties_.end()) ? it->second : nullptr; + std::shared_ptr props; + { + std::shared_lock lock(render->client_properties_mutex_); + auto it = render->client_properties_.find(remote_id); + if (it != render->client_properties_.end()) { + props = it->second; + } + } if (props) { render->is_client_mode_ = true; render->show_connection_status_window_ = true; - props->connection_status_ = status; + props->connection_status_.store(status); switch (status) { case ConnectionStatus::Connected: { @@ -1603,7 +1622,10 @@ void Render::OnConnectionStatusCb(ConnectionStatus status, const char* user_id, } else { render->is_client_mode_ = false; render->show_connection_status_window_ = true; - render->connection_status_[remote_id] = status; + { + std::unique_lock lock(render->connection_status_mutex_); + render->connection_status_[remote_id] = status; + } switch (status) { case ConnectionStatus::Connected: { @@ -1659,11 +1681,14 @@ void Render::OnConnectionStatusCb(ConnectionStatus status, const char* user_id, render->start_speaker_capturer_ = true; render->remote_client_id_ = remote_id; render->start_mouse_controller_ = true; - if (std::all_of(render->connection_status_.begin(), - render->connection_status_.end(), [](const auto& kv) { - return kv.first.find("web") != std::string::npos; - })) { - render->show_cursor_ = true; + { + std::shared_lock lock(render->connection_status_mutex_); + if (std::all_of(render->connection_status_.begin(), + render->connection_status_.end(), [](const auto& kv) { + return kv.first.find("web") != std::string::npos; + })) { + render->show_cursor_ = true; + } } break; @@ -1672,12 +1697,18 @@ void Render::OnConnectionStatusCb(ConnectionStatus status, const char* user_id, case ConnectionStatus::Failed: case ConnectionStatus::Closed: { render->ReleaseRemotePressedKeys(remote_id, "connection_closed"); - if (std::all_of(render->connection_status_.begin(), - render->connection_status_.end(), [](const auto& kv) { - return kv.second == ConnectionStatus::Closed || - kv.second == ConnectionStatus::Failed || - kv.second == ConnectionStatus::Disconnected; - })) { + bool all_disconnected = false; + { + std::shared_lock lock(render->connection_status_mutex_); + all_disconnected = std::all_of( + render->connection_status_.begin(), + render->connection_status_.end(), [](const auto& kv) { + return kv.second == ConnectionStatus::Closed || + kv.second == ConnectionStatus::Failed || + kv.second == ConnectionStatus::Disconnected; + }); + } + if (all_disconnected) { render->need_to_destroy_server_window_ = true; render->is_server_mode_ = false; #if defined(__linux__) && !defined(__APPLE__) @@ -1704,18 +1735,24 @@ void Render::OnConnectionStatusCb(ConnectionStatus status, const char* user_id, render->audio_capture_ = false; } - render->connection_status_.erase(remote_id); - render->connection_host_names_.erase(remote_id); + { + std::unique_lock lock(render->connection_status_mutex_); + render->connection_status_.erase(remote_id); + render->connection_host_names_.erase(remote_id); + } if (render->screen_capturer_) { render->screen_capturer_->ResetToInitialMonitor(); } } - if (std::all_of(render->connection_status_.begin(), - render->connection_status_.end(), [](const auto& kv) { - return kv.first.find("web") == std::string::npos; - })) { - render->show_cursor_ = false; + { + std::shared_lock lock(render->connection_status_mutex_); + if (std::all_of(render->connection_status_.begin(), + render->connection_status_.end(), [](const auto& kv) { + return kv.first.find("web") == std::string::npos; + })) { + render->show_cursor_ = false; + } } break; diff --git a/src/gui/toolbars/control_bar.cpp b/src/gui/toolbars/control_bar.cpp index 89dc9c4..5de751d 100644 --- a/src/gui/toolbars/control_bar.cpp +++ b/src/gui/toolbars/control_bar.cpp @@ -191,7 +191,7 @@ int Render::ControlBar(std::shared_ptr& props) { RemoteAction remote_action; remote_action.type = ControlType::display_id; remote_action.d = i; - if (props->connection_status_ == ConnectionStatus::Connected) { + if (props->connection_status_.load() == ConnectionStatus::Connected) { std::string msg = remote_action.to_json(); SendReliableDataFrame(props->peer_, msg.c_str(), msg.size(), props->control_data_label_.c_str()); @@ -215,7 +215,7 @@ int Render::ControlBar(std::shared_ptr& props) { auto send_service_command = [&](ServiceCommandFlag flag, const char* log_action) { - if (props->connection_status_ == ConnectionStatus::Connected && + if (props->connection_status_.load() == ConnectionStatus::Connected && props->peer_) { RemoteAction remote_action; remote_action.type = ControlType::service_command; diff --git a/src/gui/windows/connection_status_window.cpp b/src/gui/windows/connection_status_window.cpp index 9edc442..88d5f93 100644 --- a/src/gui/windows/connection_status_window.cpp +++ b/src/gui/windows/connection_status_window.cpp @@ -31,8 +31,9 @@ bool Render::ConnectionStatusWindow( ImGui::SetWindowFontScale(0.5f); std::string text; + const ConnectionStatus status = props->connection_status_.load(); - if (ConnectionStatus::Connecting == props->connection_status_) { + if (ConnectionStatus::Connecting == status) { text = localization::p2p_connecting[localization_language_index_]; ImGui::SetCursorPosX(connection_status_window_width * 0.43f); ImGui::SetCursorPosY(connection_status_window_height * 0.67f); @@ -48,7 +49,7 @@ bool Render::ConnectionStatusWindow( } ret_flag = true; } - } else if (ConnectionStatus::Gathering == props->connection_status_) { + } else if (ConnectionStatus::Gathering == status) { text = localization::p2p_gathering[localization_language_index_]; ImGui::SetCursorPosX(connection_status_window_width * 0.43f); ImGui::SetCursorPosY(connection_status_window_height * 0.67f); @@ -64,7 +65,7 @@ bool Render::ConnectionStatusWindow( } ret_flag = true; } - } else if (ConnectionStatus::Connected == props->connection_status_) { + } else if (ConnectionStatus::Connected == status) { text = localization::p2p_connected[localization_language_index_]; ImGui::SetCursorPosX(connection_status_window_width * 0.43f); ImGui::SetCursorPosY(connection_status_window_height * 0.67f); @@ -74,7 +75,7 @@ bool Render::ConnectionStatusWindow( ImGui::IsKeyPressed(ImGuiKey_Escape)) { show_connection_status_window_ = false; } - } else if (ConnectionStatus::Disconnected == props->connection_status_) { + } else if (ConnectionStatus::Disconnected == status) { text = localization::p2p_disconnected[localization_language_index_]; ImGui::SetCursorPosX(connection_status_window_width * 0.43f); ImGui::SetCursorPosY(connection_status_window_height * 0.67f); @@ -84,7 +85,7 @@ bool Render::ConnectionStatusWindow( ImGui::IsKeyPressed(ImGuiKey_Escape)) { show_connection_status_window_ = false; } - } else if (ConnectionStatus::Failed == props->connection_status_) { + } else if (ConnectionStatus::Failed == status) { text = localization::p2p_failed[localization_language_index_]; ImGui::SetCursorPosX(connection_status_window_width * 0.43f); ImGui::SetCursorPosY(connection_status_window_height * 0.67f); @@ -94,7 +95,7 @@ bool Render::ConnectionStatusWindow( ImGui::IsKeyPressed(ImGuiKey_Escape)) { show_connection_status_window_ = false; } - } else if (ConnectionStatus::Closed == props->connection_status_) { + } else if (ConnectionStatus::Closed == status) { text = localization::p2p_closed[localization_language_index_]; ImGui::SetCursorPosX(connection_status_window_width * 0.43f); ImGui::SetCursorPosY(connection_status_window_height * 0.67f); @@ -104,7 +105,7 @@ bool Render::ConnectionStatusWindow( ImGui::IsKeyPressed(ImGuiKey_Escape)) { show_connection_status_window_ = false; } - } else if (ConnectionStatus::IncorrectPassword == props->connection_status_) { + } else if (ConnectionStatus::IncorrectPassword == status) { if (!password_validating_) { if (password_validating_time_ == 1) { text = localization::input_password[localization_language_index_]; @@ -167,8 +168,7 @@ bool Render::ConnectionStatusWindow( ImGui::SetCursorPosX(connection_status_window_width * 0.43f); ImGui::SetCursorPosY(connection_status_window_height * 0.67f); } - } else if (ConnectionStatus::NoSuchTransmissionId == - props->connection_status_) { + } else if (ConnectionStatus::NoSuchTransmissionId == status) { text = localization::no_such_id[localization_language_index_]; ImGui::SetCursorPosX(connection_status_window_width * 0.43f); ImGui::SetCursorPosY(connection_status_window_height * 0.67f); diff --git a/src/gui/windows/server_window.cpp b/src/gui/windows/server_window.cpp index 6b1e17e..db274e8 100644 --- a/src/gui/windows/server_window.cpp +++ b/src/gui/windows/server_window.cpp @@ -1,6 +1,8 @@ #include #include #include +#include +#include #include #include @@ -149,14 +151,17 @@ int Render::RemoteClientInfoWindow() { float font_scale = localization_language_index_ == 0 ? 0.5f : 0.45f; std::vector> remote_entries; - remote_entries.reserve(connection_status_.size()); - for (const auto& kv : connection_status_) { - const auto host_it = connection_host_names_.find(kv.first); - const std::string display_name = - (host_it != connection_host_names_.end() && !host_it->second.empty()) - ? host_it->second - : kv.first; - remote_entries.emplace_back(kv.first, display_name); + { + std::shared_lock lock(connection_status_mutex_); + remote_entries.reserve(connection_status_.size()); + for (const auto& kv : connection_status_) { + const auto host_it = connection_host_names_.find(kv.first); + const std::string display_name = + (host_it != connection_host_names_.end() && !host_it->second.empty()) + ? host_it->second + : kv.first; + remote_entries.emplace_back(kv.first, display_name); + } } auto find_display_name_by_remote_id = @@ -220,10 +225,14 @@ int Render::RemoteClientInfoWindow() { ImGui::SetWindowFontScale(font_scale); if (!selected_server_remote_id_.empty()) { - auto it = connection_status_.find(selected_server_remote_id_); - const ConnectionStatus status = (it == connection_status_.end()) - ? ConnectionStatus::Closed - : it->second; + ConnectionStatus status = ConnectionStatus::Closed; + { + std::shared_lock lock(connection_status_mutex_); + auto it = connection_status_.find(selected_server_remote_id_); + if (it != connection_status_.end()) { + status = it->second; + } + } ImGui::Text( "%s", @@ -376,4 +385,4 @@ int Render::RemoteClientInfoWindow() { return 0; } -} // namespace crossdesk +} // namespace crossdesk diff --git a/src/gui/windows/stream_window.cpp b/src/gui/windows/stream_window.cpp index 06e13d7..543b82f 100644 --- a/src/gui/windows/stream_window.cpp +++ b/src/gui/windows/stream_window.cpp @@ -7,7 +7,7 @@ namespace crossdesk { void Render::DrawConnectionStatusText( std::shared_ptr& props) { std::string text; - switch (props->connection_status_) { + switch (props->connection_status_.load()) { case ConnectionStatus::Disconnected: text = localization::p2p_disconnected[localization_language_index_]; break; @@ -34,7 +34,7 @@ void Render::DrawConnectionStatusText( void Render::DrawReceivingScreenText( std::shared_ptr& props) { if (!props->connection_established_ || - props->connection_status_ != ConnectionStatus::Connected) { + props->connection_status_.load() != ConnectionStatus::Connected) { return; }