Commit 0d0f9b5a authored by Yuwei Huang's avatar Yuwei Huang Committed by Commit Bot

[CRD iOS] Fix crashes when session disconnects

We are still seeing quite a few crashes happen when session disconnects
remotely. Here are two issues that I have found:

* It turns out calling WeakPtrFactory::InvalidateWeakPtrs() only
  invalidate WeakPtrs that are currently created, while calling
  WeakPtrFactory::GetWeakPtr() again will create new valid WeakPtrs.
* ChromotingClient owns IceTransport, which runs a timer to periodically
  send pending transport messages. The timer should be stopped
  immediately after the session is disconnected, so we need to
  immediately destroy the ChromotingClient instance in this case.

Bug: 840492
Change-Id: I6625af62001b0cdfb5745dcc863ed59617e440cf
Reviewed-on: https://chromium-review.googlesource.com/1170174
Commit-Queue: Yuwei Huang <yuweih@chromium.org>
Reviewed-by: default avatarJoe Downing <joedow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#582771}
parent f5467f4a
...@@ -87,9 +87,7 @@ void GetFeedbackDataOnNetworkThread( ...@@ -87,9 +87,7 @@ void GetFeedbackDataOnNetworkThread(
} }
} }
} // namespace struct SessionContext {
struct ChromotingSession::SessionContext {
ChromotingClientRuntime* runtime; ChromotingClientRuntime* runtime;
base::WeakPtr<ChromotingSession::Delegate> delegate; base::WeakPtr<ChromotingSession::Delegate> delegate;
base::WeakPtr<ClientTelemetryLogger> logger; base::WeakPtr<ClientTelemetryLogger> logger;
...@@ -102,6 +100,8 @@ struct ChromotingSession::SessionContext { ...@@ -102,6 +100,8 @@ struct ChromotingSession::SessionContext {
ConnectToHostInfo info; ConnectToHostInfo info;
}; };
} // namespace
class ChromotingSession::Core : public ClientUserInterface, class ChromotingSession::Core : public ClientUserInterface,
public protocol::ClipboardStub { public protocol::ClipboardStub {
public: public:
...@@ -121,7 +121,7 @@ class ChromotingSession::Core : public ClientUserInterface, ...@@ -121,7 +121,7 @@ class ChromotingSession::Core : public ClientUserInterface,
void EnableVideoChannel(bool enable); void EnableVideoChannel(bool enable);
void SendClientMessage(const std::string& type, const std::string& data); void SendClientMessage(const std::string& type, const std::string& data);
// Logs the disconnect event and invalidates weak pointers. // Logs the disconnect event and invalidates the instance.
void Disconnect(); void Disconnect();
// ClientUserInterface implementation. // ClientUserInterface implementation.
...@@ -144,6 +144,10 @@ class ChromotingSession::Core : public ClientUserInterface, ...@@ -144,6 +144,10 @@ class ChromotingSession::Core : public ClientUserInterface,
base::WeakPtr<Core> GetWeakPtr(); base::WeakPtr<Core> GetWeakPtr();
private: private:
// Destroys the client and invalidates weak pointers. This doesn't destroy the
// instance itself.
void Invalidate();
void ConnectOnNetworkThread(); void ConnectOnNetworkThread();
void LogPerfStats(); void LogPerfStats();
...@@ -166,20 +170,25 @@ class ChromotingSession::Core : public ClientUserInterface, ...@@ -166,20 +170,25 @@ class ChromotingSession::Core : public ClientUserInterface,
const std::string& shared_secret); const std::string& shared_secret);
scoped_refptr<AutoThreadTaskRunner> ui_task_runner() { scoped_refptr<AutoThreadTaskRunner> ui_task_runner() {
return session_context_->runtime->ui_task_runner(); DCHECK(runtime_);
return runtime_->ui_task_runner();
} }
scoped_refptr<AutoThreadTaskRunner> network_task_runner() { scoped_refptr<AutoThreadTaskRunner> network_task_runner() {
return session_context_->runtime->network_task_runner(); DCHECK(runtime_);
return runtime_->network_task_runner();
} }
// Storing |runtime_| out of |session_context_| so that we can still use the
// task runners after |session_context_| is destroyed.
ChromotingClientRuntime* const runtime_;
std::unique_ptr<SessionContext> session_context_; std::unique_ptr<SessionContext> session_context_;
std::unique_ptr<ClientContext> client_context_; std::unique_ptr<ClientContext> client_context_;
std::unique_ptr<protocol::PerformanceTracker> perf_tracker_; std::unique_ptr<protocol::PerformanceTracker> perf_tracker_;
// |signaling_| must outlive |client_|, so it must be declared above // |signaling_| must outlive |client_|.
// |client_|.
std::unique_ptr<XmppSignalStrategy> signaling_; std::unique_ptr<XmppSignalStrategy> signaling_;
std::unique_ptr<OAuthTokenGetter> token_getter_; std::unique_ptr<OAuthTokenGetter> token_getter_;
std::unique_ptr<ChromotingClient> client_; std::unique_ptr<ChromotingClient> client_;
...@@ -193,14 +202,23 @@ class ChromotingSession::Core : public ClientUserInterface, ...@@ -193,14 +202,23 @@ class ChromotingSession::Core : public ClientUserInterface,
base::RepeatingTimer perf_stats_logging_timer_; base::RepeatingTimer perf_stats_logging_timer_;
// weak_factory_.GetWeakPtr() creates new valid WeakPtrs after
// weak_factory_.InvalidateWeakPtrs() is called. We store and return
// |weak_ptr_| in GetWeakPtr() so that its copies are still invalidated once
// InvalidateWeakPtrs() is called.
base::WeakPtr<Core> weak_ptr_;
base::WeakPtrFactory<Core> weak_factory_; base::WeakPtrFactory<Core> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(Core); DISALLOW_COPY_AND_ASSIGN(Core);
}; };
ChromotingSession::Core::Core(std::unique_ptr<SessionContext> session_context) ChromotingSession::Core::Core(std::unique_ptr<SessionContext> session_context)
: session_context_(std::move(session_context)), weak_factory_(this) { : runtime_(session_context->runtime),
session_context_(std::move(session_context)),
weak_factory_(this) {
DCHECK(ui_task_runner()->BelongsToCurrentThread()); DCHECK(ui_task_runner()->BelongsToCurrentThread());
weak_ptr_ = weak_factory_.GetWeakPtr();
network_task_runner()->PostTask( network_task_runner()->PostTask(
FROM_HERE, base::BindOnce(&Core::ConnectOnNetworkThread, GetWeakPtr())); FROM_HERE, base::BindOnce(&Core::ConnectOnNetworkThread, GetWeakPtr()));
} }
...@@ -315,8 +333,8 @@ void ChromotingSession::Core::Disconnect() { ...@@ -315,8 +333,8 @@ void ChromotingSession::Core::Disconnect() {
session_context_->logger->LogSessionStateChange( session_context_->logger->LogSessionStateChange(
session_state_to_log, ChromotingEvent::ConnectionError::NONE); session_state_to_log, ChromotingEvent::ConnectionError::NONE);
session_state_ = protocol::ConnectionToHost::CLOSED; session_state_ = protocol::ConnectionToHost::CLOSED;
// Prevent all pending and future calls from ChromotingSession.
weak_factory_.InvalidateWeakPtrs(); Invalidate();
} }
} }
...@@ -351,7 +369,7 @@ void ChromotingSession::Core::OnConnectionState( ...@@ -351,7 +369,7 @@ void ChromotingSession::Core::OnConnectionState(
if (state == protocol::ConnectionToHost::CLOSED || if (state == protocol::ConnectionToHost::CLOSED ||
state == protocol::ConnectionToHost::FAILED) { state == protocol::ConnectionToHost::FAILED) {
weak_factory_.InvalidateWeakPtrs(); Invalidate();
} }
} }
...@@ -417,7 +435,19 @@ void ChromotingSession::Core::InjectClipboardEvent( ...@@ -417,7 +435,19 @@ void ChromotingSession::Core::InjectClipboardEvent(
} }
base::WeakPtr<ChromotingSession::Core> ChromotingSession::Core::GetWeakPtr() { base::WeakPtr<ChromotingSession::Core> ChromotingSession::Core::GetWeakPtr() {
return weak_factory_.GetWeakPtr(); return weak_ptr_;
}
void ChromotingSession::Core::Invalidate() {
// Prevent all pending and future calls from ChromotingSession.
weak_factory_.InvalidateWeakPtrs();
client_.reset();
token_getter_.reset();
signaling_.reset();
perf_tracker_.reset();
client_context_.reset();
session_context_.reset();
} }
void ChromotingSession::Core::ConnectOnNetworkThread() { void ChromotingSession::Core::ConnectOnNetworkThread() {
...@@ -450,18 +480,18 @@ void ChromotingSession::Core::ConnectOnNetworkThread() { ...@@ -450,18 +480,18 @@ void ChromotingSession::Core::ConnectOnNetworkThread() {
xmpp_config.username = session_context_->info.username; xmpp_config.username = session_context_->info.username;
xmpp_config.auth_token = session_context_->info.auth_token; xmpp_config.auth_token = session_context_->info.auth_token;
signaling_.reset(new XmppSignalStrategy( signaling_.reset(
net::ClientSocketFactory::GetDefaultFactory(), new XmppSignalStrategy(net::ClientSocketFactory::GetDefaultFactory(),
session_context_->runtime->url_requester(), xmpp_config)); runtime_->url_requester(), xmpp_config));
token_getter_ = session_context_->runtime->CreateOAuthTokenGetter(); token_getter_ = runtime_->CreateOAuthTokenGetter();
scoped_refptr<protocol::TransportContext> transport_context = scoped_refptr<protocol::TransportContext> transport_context =
new protocol::TransportContext( new protocol::TransportContext(
signaling_.get(), signaling_.get(),
std::make_unique<protocol::ChromiumPortAllocatorFactory>(), std::make_unique<protocol::ChromiumPortAllocatorFactory>(),
std::make_unique<ChromiumUrlRequestFactory>( std::make_unique<ChromiumUrlRequestFactory>(
session_context_->runtime->url_requester()), runtime_->url_requester()),
protocol::NetworkSettings( protocol::NetworkSettings(
protocol::NetworkSettings::NAT_TRAVERSAL_FULL), protocol::NetworkSettings::NAT_TRAVERSAL_FULL),
protocol::TransportRole::CLIENT); protocol::TransportRole::CLIENT);
...@@ -587,13 +617,13 @@ ChromotingSession::ChromotingSession( ...@@ -587,13 +617,13 @@ ChromotingSession::ChromotingSession(
std::unique_ptr<protocol::CursorShapeStub> cursor_shape_stub, std::unique_ptr<protocol::CursorShapeStub> cursor_shape_stub,
std::unique_ptr<protocol::VideoRenderer> video_renderer, std::unique_ptr<protocol::VideoRenderer> video_renderer,
std::unique_ptr<protocol::AudioStub> audio_player, std::unique_ptr<protocol::AudioStub> audio_player,
const ConnectToHostInfo& info) { const ConnectToHostInfo& info)
: runtime_(ChromotingClientRuntime::GetInstance()) {
DCHECK(delegate); DCHECK(delegate);
DCHECK(cursor_shape_stub); DCHECK(cursor_shape_stub);
DCHECK(video_renderer); DCHECK(video_renderer);
// Don't DCHECK audio_player since it will bind audio_player to the ui thread. // Don't DCHECK audio_player since it will bind audio_player to the ui thread.
runtime_ = ChromotingClientRuntime::GetInstance();
DCHECK(runtime_->ui_task_runner()->BelongsToCurrentThread()); DCHECK(runtime_->ui_task_runner()->BelongsToCurrentThread());
logger_ = std::make_unique<ClientTelemetryLogger>( logger_ = std::make_unique<ClientTelemetryLogger>(
...@@ -601,38 +631,28 @@ ChromotingSession::ChromotingSession( ...@@ -601,38 +631,28 @@ ChromotingSession::ChromotingSession(
info.session_entry_point); info.session_entry_point);
// logger is set when connection is started. // logger is set when connection is started.
session_context_ = std::make_unique<SessionContext>(); auto session_context = std::make_unique<SessionContext>();
session_context_->runtime = runtime_; session_context->runtime = runtime_;
session_context_->delegate = delegate; session_context->delegate = delegate;
session_context_->logger = logger_->GetWeakPtr(); session_context->logger = logger_->GetWeakPtr();
session_context_->audio_player = std::move(audio_player); session_context->audio_player = std::move(audio_player);
session_context_->audio_player_weak_factory = session_context->audio_player_weak_factory =
std::make_unique<base::WeakPtrFactory<protocol::AudioStub>>( std::make_unique<base::WeakPtrFactory<protocol::AudioStub>>(
session_context_->audio_player.get()); session_context->audio_player.get());
session_context_->cursor_shape_stub = std::move(cursor_shape_stub); session_context->cursor_shape_stub = std::move(cursor_shape_stub);
session_context_->video_renderer = std::move(video_renderer); session_context->video_renderer = std::move(video_renderer);
session_context_->info = info; session_context->info = info;
core_ = std::make_unique<Core>(std::move(session_context));
} }
ChromotingSession::~ChromotingSession() { ChromotingSession::~ChromotingSession() {
DCHECK(runtime_->ui_task_runner()->BelongsToCurrentThread()); DCHECK(runtime_->ui_task_runner()->BelongsToCurrentThread());
if (core_) { runtime_->network_task_runner()->DeleteSoon(FROM_HERE, core_.release());
runtime_->network_task_runner()->DeleteSoon(FROM_HERE, core_.release());
}
runtime_->network_task_runner()->DeleteSoon(FROM_HERE, logger_.release()); runtime_->network_task_runner()->DeleteSoon(FROM_HERE, logger_.release());
} }
void ChromotingSession::Connect() {
DCHECK(runtime_->ui_task_runner()->BelongsToCurrentThread());
DCHECK(session_context_) << "Session has already been connected before.";
core_ = std::make_unique<Core>(std::move(session_context_));
}
void ChromotingSession::Disconnect() {
RunCoreTaskOnNetworkThread(FROM_HERE, &Core::Disconnect);
}
void ChromotingSession::GetFeedbackData( void ChromotingSession::GetFeedbackData(
GetFeedbackDataCallback callback) const { GetFeedbackDataCallback callback) const {
DCHECK(runtime_->ui_task_runner()->BelongsToCurrentThread()); DCHECK(runtime_->ui_task_runner()->BelongsToCurrentThread());
...@@ -713,11 +733,6 @@ void ChromotingSession::RunCoreTaskOnNetworkThread( ...@@ -713,11 +733,6 @@ void ChromotingSession::RunCoreTaskOnNetworkThread(
Args&&... args) { Args&&... args) {
DCHECK(runtime_->ui_task_runner()->BelongsToCurrentThread()); DCHECK(runtime_->ui_task_runner()->BelongsToCurrentThread());
if (!core_) {
LOG(WARNING) << "Session is not connected.";
return;
}
runtime_->network_task_runner()->PostTask( runtime_->network_task_runner()->PostTask(
FROM_HERE, FROM_HERE,
base::BindOnce(std::forward<Functor>(core_functor), core_->GetWeakPtr(), base::BindOnce(std::forward<Functor>(core_functor), core_->GetWeakPtr(),
......
...@@ -86,7 +86,8 @@ class ChromotingSession : public ClientInputInjector { ...@@ -86,7 +86,8 @@ class ChromotingSession : public ClientInputInjector {
using GetFeedbackDataCallback = using GetFeedbackDataCallback =
base::OnceCallback<void(std::unique_ptr<FeedbackData>)>; base::OnceCallback<void(std::unique_ptr<FeedbackData>)>;
// Initiates a connection with the specified host. // Initiates a connection with the specified host. This will start the
// connection immediately.
ChromotingSession(base::WeakPtr<ChromotingSession::Delegate> delegate, ChromotingSession(base::WeakPtr<ChromotingSession::Delegate> delegate,
std::unique_ptr<protocol::CursorShapeStub> cursor_stub, std::unique_ptr<protocol::CursorShapeStub> cursor_stub,
std::unique_ptr<protocol::VideoRenderer> video_renderer, std::unique_ptr<protocol::VideoRenderer> video_renderer,
...@@ -95,13 +96,6 @@ class ChromotingSession : public ClientInputInjector { ...@@ -95,13 +96,6 @@ class ChromotingSession : public ClientInputInjector {
~ChromotingSession() override; ~ChromotingSession() override;
// Starts the connection. Can be called no more than once.
void Connect();
// Terminates the current connection (if it hasn't already failed) and cleans
// up.
void Disconnect();
// Gets the current feedback data and returns it to the callback on the // Gets the current feedback data and returns it to the callback on the
// UI thread. If the session is never connected, then an empty feedback // UI thread. If the session is never connected, then an empty feedback
// will be returned, otherwise feedback for current session (either still // will be returned, otherwise feedback for current session (either still
...@@ -134,7 +128,6 @@ class ChromotingSession : public ClientInputInjector { ...@@ -134,7 +128,6 @@ class ChromotingSession : public ClientInputInjector {
void SendClientMessage(const std::string& type, const std::string& data); void SendClientMessage(const std::string& type, const std::string& data);
private: private:
struct SessionContext;
class Core; class Core;
template <typename Functor, typename... Args> template <typename Functor, typename... Args>
...@@ -143,16 +136,14 @@ class ChromotingSession : public ClientInputInjector { ...@@ -143,16 +136,14 @@ class ChromotingSession : public ClientInputInjector {
Args&&... args); Args&&... args);
// Used to obtain task runner references. // Used to obtain task runner references.
ChromotingClientRuntime* runtime_; ChromotingClientRuntime* const runtime_;
// Becomes null after the session is connected, and thereafter will not be
// reassigned.
std::unique_ptr<SessionContext> session_context_;
// Created when the session is connected, then used, and destroyed on the // Created when the session is connected, then used, and destroyed on the
// network thread when the instance is destroyed. // network thread when the instance is destroyed.
std::unique_ptr<Core> core_; std::unique_ptr<Core> core_;
// TODO(yuweih): Looks like we should be able to move this into the Core and
// post a task to base::Unretained(core_) to get back the feedback.
// Created when the session is created, then used, and destroyed on the // Created when the session is created, then used, and destroyed on the
// network thread when the instance is destroyed. This is stored out of // network thread when the instance is destroyed. This is stored out of
// |core_| to allow accessing logs after |core_| becomes invalid. // |core_| to allow accessing logs after |core_| becomes invalid.
......
...@@ -50,7 +50,6 @@ void JniClient::ConnectToHost(const ConnectToHostInfo& info) { ...@@ -50,7 +50,6 @@ void JniClient::ConnectToHost(const ConnectToHostInfo& info) {
weak_ptr_, display_handler_->CreateCursorShapeStub(), weak_ptr_, display_handler_->CreateCursorShapeStub(),
display_handler_->CreateVideoRenderer(), display_handler_->CreateVideoRenderer(),
std::make_unique<AudioPlayerAndroid>(), info)); std::make_unique<AudioPlayerAndroid>(), info));
session_->Connect();
} }
void JniClient::DisconnectFromHost() { void JniClient::DisconnectFromHost() {
......
...@@ -144,8 +144,6 @@ static void ResolveFeedbackDataCallback( ...@@ -144,8 +144,6 @@ static void ResolveFeedbackDataCallback(
[_displayHandler createVideoRenderer], std::move(audioStream), info)); [_displayHandler createVideoRenderer], std::move(audioStream), info));
_gestureInterpreter.SetContext(_displayHandler.rendererProxy, _session.get()); _gestureInterpreter.SetContext(_displayHandler.rendererProxy, _session.get());
_keyboardInterpreter.SetContext(_session.get()); _keyboardInterpreter.SetContext(_session.get());
_session->Connect();
} }
- (void)disconnectFromHost { - (void)disconnectFromHost {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment