[fix] use lock and null pointer checks to prevent crashes

This commit is contained in:
dijunkun
2025-11-28 00:41:29 +08:00
parent 82f32cbe8f
commit 3d8249bffa
6 changed files with 189 additions and 91 deletions

View File

@@ -82,6 +82,7 @@ int Render::RemoteWindow() {
for (auto& [id, props] : recent_connections_) { for (auto& [id, props] : recent_connections_) {
if (id.find(remote_id) != std::string::npos) { if (id.find(remote_id) != std::string::npos) {
found = true; found = true;
std::shared_lock lock(client_properties_mutex_);
if (client_properties_.find(remote_id) != if (client_properties_.find(remote_id) !=
client_properties_.end()) { client_properties_.end()) {
if (!client_properties_[remote_id]->connection_established_) { if (!client_properties_[remote_id]->connection_established_) {
@@ -111,6 +112,7 @@ int Render::RemoteWindow() {
if (elapsed >= 1000) { if (elapsed >= 1000) {
last_rejoin_check_time_ = now; last_rejoin_check_time_ = now;
need_to_rejoin_ = false; need_to_rejoin_ = false;
std::shared_lock lock(client_properties_mutex_);
for (const auto& [_, props] : client_properties_) { for (const auto& [_, props] : client_properties_) {
if (props->rejoin_) { if (props->rejoin_) {
ConnectTo(props->remote_id_, props->remote_password_, ConnectTo(props->remote_id_, props->remote_password_,
@@ -145,6 +147,13 @@ int Render::ConnectTo(const std::string& remote_id, const char* password,
LOG_INFO("Connect to [{}]", remote_id); LOG_INFO("Connect to [{}]", remote_id);
focused_remote_id_ = remote_id; focused_remote_id_ = remote_id;
std::shared_lock shared_lock(client_properties_mutex_);
bool exists =
(client_properties_.find(remote_id) != client_properties_.end());
shared_lock.unlock();
if (!exists) {
std::unique_lock unique_lock(client_properties_mutex_);
if (client_properties_.find(remote_id) == client_properties_.end()) { if (client_properties_.find(remote_id) == client_properties_.end()) {
client_properties_[remote_id] = client_properties_[remote_id] =
std::make_shared<SubStreamWindowProperties>(); std::make_shared<SubStreamWindowProperties>();
@@ -176,7 +185,11 @@ int Render::ConnectTo(const std::string& remote_id, const char* password,
props->connection_status_ = ConnectionStatus::Connecting; props->connection_status_ = ConnectionStatus::Connecting;
} }
unique_lock.unlock();
}
int ret = -1; int ret = -1;
std::shared_lock read_lock(client_properties_mutex_);
auto props = client_properties_[remote_id]; auto props = client_properties_[remote_id];
if (!props->connection_established_) { if (!props->connection_established_) {
props->remember_password_ = remember_password; props->remember_password_ = remember_password;
@@ -188,6 +201,7 @@ int Render::ConnectTo(const std::string& remote_id, const char* password,
} }
std::string remote_id_with_pwd = remote_id + "@" + password; std::string remote_id_with_pwd = remote_id + "@" + password;
if (props->peer_) {
ret = JoinConnection(props->peer_, remote_id_with_pwd.c_str()); ret = JoinConnection(props->peer_, remote_id_with_pwd.c_str());
if (0 == ret) { if (0 == ret) {
props->rejoin_ = false; props->rejoin_ = false;
@@ -196,6 +210,8 @@ int Render::ConnectTo(const std::string& remote_id, const char* password,
need_to_rejoin_ = true; need_to_rejoin_ = true;
} }
} }
}
read_lock.unlock();
return 0; return 0;
} }

View File

@@ -947,6 +947,7 @@ int Render::DrawStreamWindow() {
ImGui::Render(); ImGui::Render();
SDL_RenderClear(stream_renderer_); SDL_RenderClear(stream_renderer_);
std::shared_lock lock(client_properties_mutex_);
for (auto& it : client_properties_) { for (auto& it : client_properties_) {
auto props = it.second; auto props = it.second;
if (props->tab_selected_) { if (props->tab_selected_) {
@@ -1283,12 +1284,18 @@ void Render::CleanupPeers() {
DestroyPeer(&peer_); DestroyPeer(&peer_);
} }
{
std::shared_lock lock(client_properties_mutex_);
for (auto& it : client_properties_) { for (auto& it : client_properties_) {
auto props = it.second; auto props = it.second;
CleanupPeer(props); CleanupPeer(props);
} }
}
{
std::unique_lock lock(client_properties_mutex_);
client_properties_.clear(); client_properties_.clear();
}
} }
void Render::CleanSubStreamWindowProperties( void Render::CleanSubStreamWindowProperties(
@@ -1305,6 +1312,7 @@ void Render::CleanSubStreamWindowProperties(
} }
void Render::UpdateRenderRect() { void Render::UpdateRenderRect() {
std::shared_lock lock(client_properties_mutex_);
for (auto& [_, props] : client_properties_) { for (auto& [_, props] : client_properties_) {
if (!props->reset_control_bar_pos_) { if (!props->reset_control_bar_pos_) {
props->mouse_diff_control_bar_pos_x_ = 0; props->mouse_diff_control_bar_pos_x_ = 0;
@@ -1379,6 +1387,8 @@ void Render::ProcessSdlEvent(const SDL_Event& event) {
DestroyStreamWindow(); DestroyStreamWindow();
DestroyStreamWindowContext(); DestroyStreamWindowContext();
{
std::shared_lock lock(client_properties_mutex_);
for (auto& [host_name, props] : client_properties_) { for (auto& [host_name, props] : client_properties_) {
thumbnail_->SaveToThumbnail( thumbnail_->SaveToThumbnail(
(char*)props->dst_buffer_, props->video_width_, (char*)props->dst_buffer_, props->video_width_,
@@ -1406,7 +1416,12 @@ void Render::ProcessSdlEvent(const SDL_Event& event) {
SDL_FlushEvents(STREAM_REFRESH_EVENT, STREAM_REFRESH_EVENT); SDL_FlushEvents(STREAM_REFRESH_EVENT, STREAM_REFRESH_EVENT);
memset(audio_buffer_, 0, 720); memset(audio_buffer_, 0, 720);
} }
}
{
std::unique_lock lock(client_properties_mutex_);
client_properties_.clear(); client_properties_.clear();
}
rejoin_ = false; rejoin_ = false;
is_client_mode_ = false; is_client_mode_ = false;

View File

@@ -15,6 +15,7 @@
#include <mutex> #include <mutex>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include <optional> #include <optional>
#include <shared_mutex>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
@@ -30,6 +31,7 @@
#include "screen_capturer_factory.h" #include "screen_capturer_factory.h"
#include "speaker_capturer_factory.h" #include "speaker_capturer_factory.h"
#include "thumbnail.h" #include "thumbnail.h"
#if _WIN32 #if _WIN32
#include "win_tray.h" #include "win_tray.h"
#endif #endif
@@ -504,6 +506,7 @@ class Render {
/* ------ sub stream window property start ------ */ /* ------ sub stream window property start ------ */
std::unordered_map<std::string, std::shared_ptr<SubStreamWindowProperties>> std::unordered_map<std::string, std::shared_ptr<SubStreamWindowProperties>>
client_properties_; client_properties_;
std::shared_mutex client_properties_mutex_;
void CloseTab(decltype(client_properties_)::iterator& it); void CloseTab(decltype(client_properties_)::iterator& it);
/* ------ stream window property end ------ */ /* ------ stream window property end ------ */

View File

@@ -21,16 +21,19 @@ int Render::SendKeyCommand(int key_code, bool is_down) {
remote_action.k.key_value = key_code; remote_action.k.key_value = key_code;
if (!controlled_remote_id_.empty()) { if (!controlled_remote_id_.empty()) {
std::shared_lock lock(client_properties_mutex_);
if (client_properties_.find(controlled_remote_id_) != if (client_properties_.find(controlled_remote_id_) !=
client_properties_.end()) { client_properties_.end()) {
auto props = client_properties_[controlled_remote_id_]; auto props = client_properties_[controlled_remote_id_];
if (props->connection_status_ == ConnectionStatus::Connected) { if (props->connection_status_ == ConnectionStatus::Connected) {
std::string msg = remote_action.to_json(); std::string msg = remote_action.to_json();
if (props->peer_) {
SendDataFrame(props->peer_, msg.c_str(), msg.size(), SendDataFrame(props->peer_, msg.c_str(), msg.size(),
props->data_label_.c_str()); props->data_label_.c_str());
} }
} }
} }
}
return 0; return 0;
} }
@@ -42,6 +45,7 @@ int Render::ProcessMouseEvent(const SDL_Event& event) {
float ratio_x, ratio_y = 0; float ratio_x, ratio_y = 0;
RemoteAction remote_action; RemoteAction remote_action;
std::shared_lock lock(client_properties_mutex_);
for (auto& it : client_properties_) { for (auto& it : client_properties_) {
auto props = it.second; auto props = it.second;
if (!props->control_mouse_) { if (!props->control_mouse_) {
@@ -94,8 +98,10 @@ int Render::ProcessMouseEvent(const SDL_Event& event) {
} }
std::string msg = remote_action.to_json(); std::string msg = remote_action.to_json();
if (props->peer_) {
SendDataFrame(props->peer_, msg.c_str(), msg.size(), SendDataFrame(props->peer_, msg.c_str(), msg.size(),
props->data_label_.c_str()); props->data_label_.c_str());
}
} else if (SDL_EVENT_MOUSE_WHEEL == event.type && } else if (SDL_EVENT_MOUSE_WHEEL == event.type &&
last_mouse_event.button.x >= props->stream_render_rect_.x && last_mouse_event.button.x >= props->stream_render_rect_.x &&
last_mouse_event.button.x <= props->stream_render_rect_.x + last_mouse_event.button.x <= props->stream_render_rect_.x +
@@ -139,10 +145,12 @@ int Render::ProcessMouseEvent(const SDL_Event& event) {
render_height; render_height;
std::string msg = remote_action.to_json(); std::string msg = remote_action.to_json();
if (props->peer_) {
SendDataFrame(props->peer_, msg.c_str(), msg.size(), SendDataFrame(props->peer_, msg.c_str(), msg.size(),
props->data_label_.c_str()); props->data_label_.c_str());
} }
} }
}
return 0; return 0;
} }
@@ -154,13 +162,16 @@ void Render::SdlCaptureAudioIn(void* userdata, Uint8* stream, int len) {
} }
if (1) { if (1) {
for (auto it : render->client_properties_) { std::shared_lock lock(render->client_properties_mutex_);
for (const auto& it : render->client_properties_) {
auto props = it.second; auto props = it.second;
if (props->connection_status_ == ConnectionStatus::Connected) { if (props->connection_status_ == ConnectionStatus::Connected) {
if (props->peer_) {
SendAudioFrame(props->peer_, (const char*)stream, len, SendAudioFrame(props->peer_, (const char*)stream, len,
render->audio_label_.c_str()); render->audio_label_.c_str());
} }
} }
}
} else { } else {
memcpy(render->audio_buffer_, stream, len); memcpy(render->audio_buffer_, stream, len);
@@ -207,6 +218,7 @@ void Render::OnReceiveVideoBufferCb(const XVideoFrame* video_frame,
} }
std::string remote_id(user_id, user_id_size); std::string remote_id(user_id, user_id_size);
std::shared_lock lock(render->client_properties_mutex_);
if (render->client_properties_.find(remote_id) == if (render->client_properties_.find(remote_id) ==
render->client_properties_.end()) { render->client_properties_.end()) {
return; return;
@@ -302,6 +314,7 @@ void Render::OnReceiveDataBufferCb(const char* data, size_t size,
} }
std::string remote_id(user_id, user_id_size); std::string remote_id(user_id, user_id_size);
std::shared_lock lock(render->client_properties_mutex_);
if (render->client_properties_.find(remote_id) != if (render->client_properties_.find(remote_id) !=
render->client_properties_.end()) { render->client_properties_.end()) {
// local // local
@@ -373,6 +386,7 @@ void Render::OnSignalStatusCb(SignalStatus status, const char* user_id,
} }
std::string remote_id(client_id.begin() + 2, client_id.end()); std::string remote_id(client_id.begin() + 2, client_id.end());
std::shared_lock lock(render->client_properties_mutex_);
if (render->client_properties_.find(remote_id) == if (render->client_properties_.find(remote_id) ==
render->client_properties_.end()) { render->client_properties_.end()) {
return; return;
@@ -402,6 +416,7 @@ void Render::OnConnectionStatusCb(ConnectionStatus status, const char* user_id,
if (!render) return; if (!render) return;
std::string remote_id(user_id, user_id_size); 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 it = render->client_properties_.find(remote_id);
auto props = (it != render->client_properties_.end()) ? it->second : nullptr; auto props = (it != render->client_properties_.end()) ? it->second : nullptr;
@@ -428,7 +443,7 @@ void Render::OnConnectionStatusCb(ConnectionStatus status, const char* user_id,
case ConnectionStatus::Closed: { case ConnectionStatus::Closed: {
props->connection_established_ = false; props->connection_established_ = false;
props->mouse_control_button_pressed_ = false; props->mouse_control_button_pressed_ = false;
if (props->dst_buffer_) { if (props->dst_buffer_ && props->stream_texture_) {
memset(props->dst_buffer_, 0, props->dst_buffer_capacity_); memset(props->dst_buffer_, 0, props->dst_buffer_capacity_);
SDL_UpdateTexture(props->stream_texture_, NULL, props->dst_buffer_, SDL_UpdateTexture(props->stream_texture_, NULL, props->dst_buffer_,
props->texture_width_); props->texture_width_);
@@ -562,6 +577,7 @@ void Render::NetStatusReport(const char* client_id, size_t client_id_size,
} }
std::string remote_id(user_id, user_id_size); std::string remote_id(user_id, user_id_size);
std::shared_lock lock(render->client_properties_mutex_);
if (render->client_properties_.find(remote_id) == if (render->client_properties_.find(remote_id) ==
render->client_properties_.end()) { render->client_properties_.end()) {
return; return;

View File

@@ -32,6 +32,7 @@ int Render::MainWindow() {
StatusBar(); StatusBar();
if (show_connection_status_window_) { if (show_connection_status_window_) {
std::unique_lock lock(client_properties_mutex_);
for (auto it = client_properties_.begin(); for (auto it = client_properties_.begin();
it != client_properties_.end();) { it != client_properties_.end();) {
auto& props = it->second; auto& props = it->second;

View File

@@ -32,6 +32,8 @@ void Render::DrawConnectionStatusText(
} }
void Render::CloseTab(decltype(client_properties_)::iterator& it) { void Render::CloseTab(decltype(client_properties_)::iterator& it) {
std::unique_lock lock(client_properties_mutex_);
if (it != client_properties_.end()) {
CleanupPeer(it->second); CleanupPeer(it->second);
it = client_properties_.erase(it); it = client_properties_.erase(it);
if (client_properties_.empty()) { if (client_properties_.empty()) {
@@ -39,6 +41,7 @@ void Render::CloseTab(decltype(client_properties_)::iterator& it) {
event.type = SDL_EVENT_QUIT; event.type = SDL_EVENT_QUIT;
SDL_PushEvent(&event); SDL_PushEvent(&event);
} }
}
} }
int Render::StreamWindow() { int Render::StreamWindow() {
@@ -79,11 +82,22 @@ int Render::StreamWindow() {
ImGuiTabBarFlags_AutoSelectNewTabs)) { ImGuiTabBarFlags_AutoSelectNewTabs)) {
is_tab_bar_hovered_ = ImGui::IsWindowHovered(); is_tab_bar_hovered_ = ImGui::IsWindowHovered();
std::shared_lock lock(client_properties_mutex_);
for (auto it = client_properties_.begin(); for (auto it = client_properties_.begin();
it != client_properties_.end();) { it != client_properties_.end();) {
auto& props = it->second; auto& props = it->second;
if (!props->tab_opened_) { if (!props->tab_opened_) {
CloseTab(it); std::string remote_id_to_close = props->remote_id_;
lock.unlock();
{
std::unique_lock unique_lock(client_properties_mutex_);
auto close_it = client_properties_.find(remote_id_to_close);
if (close_it != client_properties_.end()) {
CloseTab(close_it);
}
}
lock.lock();
it = client_properties_.begin();
continue; continue;
} }
@@ -122,12 +136,23 @@ int Render::StreamWindow() {
focused_remote_id_ = props->remote_id_; focused_remote_id_ = props->remote_id_;
if (!props->peer_) { if (!props->peer_) {
it = client_properties_.erase(it); std::string remote_id_to_erase = props->remote_id_;
lock.unlock();
{
std::unique_lock unique_lock(client_properties_mutex_);
auto erase_it = client_properties_.find(remote_id_to_erase);
if (erase_it != client_properties_.end()) {
erase_it = client_properties_.erase(erase_it);
if (client_properties_.empty()) { if (client_properties_.empty()) {
SDL_Event event; SDL_Event event;
event.type = SDL_EVENT_QUIT; event.type = SDL_EVENT_QUIT;
SDL_PushEvent(&event); SDL_PushEvent(&event);
} }
}
}
lock.lock();
it = client_properties_.begin();
continue;
} else { } else {
DrawConnectionStatusText(props); DrawConnectionStatusText(props);
++it; ++it;
@@ -147,11 +172,22 @@ int Render::StreamWindow() {
ImGui::End(); // End TabBar ImGui::End(); // End TabBar
} else { } else {
std::shared_lock lock(client_properties_mutex_);
for (auto it = client_properties_.begin(); for (auto it = client_properties_.begin();
it != client_properties_.end();) { it != client_properties_.end();) {
auto& props = it->second; auto& props = it->second;
if (!props->tab_opened_) { if (!props->tab_opened_) {
CloseTab(it); std::string remote_id_to_close = props->remote_id_;
lock.unlock();
{
std::unique_lock unique_lock(client_properties_mutex_);
auto close_it = client_properties_.find(remote_id_to_close);
if (close_it != client_properties_.end()) {
CloseTab(close_it);
}
}
lock.lock();
it = client_properties_.begin();
continue; continue;
} }
@@ -181,12 +217,23 @@ int Render::StreamWindow() {
if (!props->peer_) { if (!props->peer_) {
fullscreen_button_pressed_ = false; fullscreen_button_pressed_ = false;
SDL_SetWindowFullscreen(stream_window_, false); SDL_SetWindowFullscreen(stream_window_, false);
it = client_properties_.erase(it); std::string remote_id_to_erase = props->remote_id_;
lock.unlock();
{
std::unique_lock unique_lock(client_properties_mutex_);
auto erase_it = client_properties_.find(remote_id_to_erase);
if (erase_it != client_properties_.end()) {
client_properties_.erase(erase_it);
if (client_properties_.empty()) { if (client_properties_.empty()) {
SDL_Event event; SDL_Event event;
event.type = SDL_EVENT_QUIT; event.type = SDL_EVENT_QUIT;
SDL_PushEvent(&event); SDL_PushEvent(&event);
} }
}
}
lock.lock();
it = client_properties_.begin();
continue;
} else { } else {
DrawConnectionStatusText(props); DrawConnectionStatusText(props);
++it; ++it;