mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-06-30 02:52:05 +08:00
[fix] fix intermittent crash on incoming connection by guarding shared connection status state across threads
This commit is contained in:
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user