[fix] fix intermittent crash on incoming connection by guarding shared connection status state across threads

This commit is contained in:
dijunkun
2026-06-26 17:21:09 +08:00
parent b41630ebb4
commit d843901550
8 changed files with 129 additions and 69 deletions
+2 -2
View File
@@ -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_;
+10 -5
View File
@@ -13,6 +13,9 @@
#include <filesystem>
#include <fstream>
#include <iostream>
#include <memory>
#include <mutex>
#include <shared_mutex>
#include <string>
#include <thread>
@@ -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;
+10 -1
View File
@@ -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<ConnectionStatus> 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<std::string, ConnectionStatus> connection_status_;
std::unordered_map<std::string, std::string> connection_host_names_;
std::string selected_server_remote_id_ = "";
+72 -35
View File
@@ -6,6 +6,9 @@
#include <filesystem>
#include <fstream>
#include <limits>
#include <memory>
#include <mutex>
#include <shared_mutex>
#include <unordered_map>
#include <unordered_set>
@@ -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<SubStreamWindowProperties> 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<SubStreamWindowProperties> 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;
+2 -2
View File
@@ -191,7 +191,7 @@ int Render::ControlBar(std::shared_ptr<SubStreamWindowProperties>& 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<SubStreamWindowProperties>& 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;
+9 -9
View File
@@ -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);
+22 -13
View File
@@ -1,6 +1,8 @@
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <memory>
#include <shared_mutex>
#include <string>
#include <vector>
@@ -149,14 +151,17 @@ int Render::RemoteClientInfoWindow() {
float font_scale = localization_language_index_ == 0 ? 0.5f : 0.45f;
std::vector<std::pair<std::string, std::string>> 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
+2 -2
View File
@@ -7,7 +7,7 @@ namespace crossdesk {
void Render::DrawConnectionStatusText(
std::shared_ptr<SubStreamWindowProperties>& 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<SubStreamWindowProperties>& props) {
if (!props->connection_established_ ||
props->connection_status_ != ConnectionStatus::Connected) {
props->connection_status_.load() != ConnectionStatus::Connected) {
return;
}