Index: remoting/host/chromoting_host.cc |
diff --git a/remoting/host/chromoting_host.cc b/remoting/host/chromoting_host.cc |
index 0fc60e2ebba154e5acbb7f5c0274de537770cb21..851067808e5649990742d33a79aa3eda9cdc7e0b 100644 |
--- a/remoting/host/chromoting_host.cc |
+++ b/remoting/host/chromoting_host.cc |
@@ -5,117 +5,63 @@ |
#include "remoting/host/chromoting_host.h" |
#include "base/stl_util-inl.h" |
-#include "base/waitable_event.h" |
+#include "base/task.h" |
#include "build/build_config.h" |
#include "remoting/base/constants.h" |
#include "remoting/base/protocol_decoder.h" |
+#include "remoting/host/chromoting_host_context.h" |
#include "remoting/host/host_config.h" |
#include "remoting/host/session_manager.h" |
#include "remoting/jingle_glue/jingle_channel.h" |
namespace remoting { |
-ChromotingHost::ChromotingHost(MutableHostConfig* config, |
+ChromotingHost::ChromotingHost(ChromotingHostContext* context, |
+ MutableHostConfig* config, |
Capturer* capturer, |
Encoder* encoder, |
- EventExecutor* executor, |
- base::WaitableEvent* host_done) |
- : main_thread_("MainThread"), |
- capture_thread_("CaptureThread"), |
- encode_thread_("EncodeThread"), |
- config_(config), |
- capturer_(capturer), |
- encoder_(encoder), |
- executor_(executor), |
- host_done_(host_done) { |
- // TODO(ajwong): The thread injection and object ownership is odd here. |
- // Fix so we do not start this thread in the constructor, so we only |
- // take in a session manager, don't let session manager own the |
- // capturer/encoder, and then associate the capturer and encoder threads with |
- // the capturer and encoder objects directly. This will require a |
- // non-refcounted NewRunnableMethod. |
- main_thread_.StartWithOptions( |
- base::Thread::Options(MessageLoop::TYPE_UI, 0)); |
- network_thread_.Start(); |
+ EventExecutor* executor) |
+ : context_(context), |
+ config_(config), |
+ capturer_(capturer), |
+ encoder_(encoder), |
+ executor_(executor), |
+ state_(kInitial) { |
} |
ChromotingHost::~ChromotingHost() { |
- // TODO(ajwong): We really need to inject these threads and get rid of these |
- // start/stops. |
- main_thread_.Stop(); |
- network_thread_.Stop(); |
- DCHECK(!encode_thread_.IsRunning()); |
- DCHECK(!capture_thread_.IsRunning()); |
} |
-void ChromotingHost::Run() { |
+void ChromotingHost::Start(Task* shutdown_task) { |
// Submit a task to perform host registration. We'll also start |
// listening to connection if registration is done. |
- message_loop()->PostTask( |
+ context_->main_message_loop()->PostTask( |
FROM_HERE, |
- NewRunnableMethod(this, &ChromotingHost::RegisterHost)); |
+ NewRunnableMethod(this, &ChromotingHost::DoStart, shutdown_task)); |
} |
// This method is called when we need to destroy the host process. |
-void ChromotingHost::DestroySession() { |
- DCHECK_EQ(message_loop(), MessageLoop::current()); |
- |
- // First we tell the session to pause and then we wait until all |
- // the tasks are done. |
- if (session_.get()) { |
- session_->Pause(); |
- |
- // TODO(hclam): Revise the order. |
- DCHECK(encode_thread_.IsRunning()); |
- encode_thread_.Stop(); |
- |
- DCHECK(capture_thread_.IsRunning()); |
- capture_thread_.Stop(); |
- } |
-} |
- |
-// This method talks to the cloud to register the host process. If |
-// successful we will start listening to network requests. |
-void ChromotingHost::RegisterHost() { |
- DCHECK_EQ(message_loop(), MessageLoop::current()); |
- DCHECK(!jingle_client_); |
- |
- std::string xmpp_login; |
- std::string xmpp_auth_token; |
- if (!config_->GetString(kXmppLoginConfigPath, &xmpp_login) || |
- !config_->GetString(kXmppAuthTokenConfigPath, &xmpp_auth_token)) { |
- LOG(ERROR) << "XMMP credentials are not defined in config."; |
- return; |
- } |
- |
- // Connect to the talk network with a JingleClient. |
- jingle_client_ = new JingleClient(&network_thread_); |
- jingle_client_->Init(xmpp_login, xmpp_auth_token, |
- kChromotingTokenServiceName, this); |
+void ChromotingHost::Shutdown() { |
+ context_->main_message_loop()->PostTask( |
+ FROM_HERE, |
+ NewRunnableMethod(this, &ChromotingHost::DoShutdown)); |
} |
// This method is called if a client is connected to this object. |
void ChromotingHost::OnClientConnected(ClientConnection* client) { |
- DCHECK_EQ(message_loop(), MessageLoop::current()); |
+ DCHECK_EQ(context_->main_message_loop(), MessageLoop::current()); |
// Create a new RecordSession if there was none. |
if (!session_.get()) { |
- // The first we need to make sure capture and encode thread are |
- // running. |
- capture_thread_.Start(); |
- encode_thread_.Start(); |
- |
// Then we create a SessionManager passing the message loops that |
// it should run on. |
- // Note that we pass the ownership of the capturer and encoder to |
- // the session manager. |
DCHECK(capturer_.get()); |
DCHECK(encoder_.get()); |
- session_ = new SessionManager(capture_thread_.message_loop(), |
- encode_thread_.message_loop(), |
- message_loop(), |
- capturer_.release(), |
- encoder_.release()); |
+ session_ = new SessionManager(context_->capture_message_loop(), |
+ context_->encode_message_loop(), |
+ context_->main_message_loop(), |
+ capturer_.get(), |
+ encoder_.get()); |
// Immediately add the client and start the session. |
session_->AddClient(client); |
@@ -128,26 +74,24 @@ void ChromotingHost::OnClientConnected(ClientConnection* client) { |
} |
void ChromotingHost::OnClientDisconnected(ClientConnection* client) { |
- DCHECK_EQ(message_loop(), MessageLoop::current()); |
+ DCHECK_EQ(context_->main_message_loop(), MessageLoop::current()); |
- // Remove the client from the session manager. |
- if (session_.get()) |
+ // Remove the client from the session manager and pause the session. |
+ // TODO(hclam): Pause only if the last client disconnected. |
+ if (session_.get()) { |
session_->RemoveClient(client); |
+ session_->Pause(); |
+ } |
// Also remove reference to ClientConnection from this object. |
client_ = NULL; |
- |
- // TODO(hclam): If the last client has disconnected we need to destroy |
- // the session manager and shutdown the capture and encode threads. |
- // Right now we assume that there's only one client. |
- DestroySession(); |
} |
//////////////////////////////////////////////////////////////////////////// |
// ClientConnection::EventHandler implementations |
void ChromotingHost::HandleMessages(ClientConnection* client, |
ClientMessageList* messages) { |
- DCHECK_EQ(message_loop(), MessageLoop::current()); |
+ DCHECK_EQ(context_->main_message_loop(), MessageLoop::current()); |
// Delegate the messages to EventExecutor and delete the unhandled |
// messages. |
@@ -157,7 +101,7 @@ void ChromotingHost::HandleMessages(ClientConnection* client, |
} |
void ChromotingHost::OnConnectionOpened(ClientConnection* client) { |
- DCHECK_EQ(message_loop(), MessageLoop::current()); |
+ DCHECK_EQ(context_->main_message_loop(), MessageLoop::current()); |
// Completes the client connection. |
LOG(INFO) << "Connection to client established."; |
@@ -165,7 +109,7 @@ void ChromotingHost::OnConnectionOpened(ClientConnection* client) { |
} |
void ChromotingHost::OnConnectionClosed(ClientConnection* client) { |
- DCHECK_EQ(message_loop(), MessageLoop::current()); |
+ DCHECK_EQ(context_->main_message_loop(), MessageLoop::current()); |
// Completes the client connection. |
LOG(INFO) << "Connection to client closed."; |
@@ -173,7 +117,7 @@ void ChromotingHost::OnConnectionClosed(ClientConnection* client) { |
} |
void ChromotingHost::OnConnectionFailed(ClientConnection* client) { |
- DCHECK_EQ(message_loop(), MessageLoop::current()); |
+ DCHECK_EQ(context_->main_message_loop(), MessageLoop::current()); |
// The client has disconnected. |
LOG(ERROR) << "Connection failed unexpectedly."; |
@@ -183,10 +127,9 @@ void ChromotingHost::OnConnectionFailed(ClientConnection* client) { |
//////////////////////////////////////////////////////////////////////////// |
// JingleClient::Callback implementations |
void ChromotingHost::OnStateChange(JingleClient* jingle_client, |
- JingleClient::State state) { |
- DCHECK_EQ(jingle_client_.get(), jingle_client); |
- |
+ JingleClient::State state) { |
if (state == JingleClient::CONNECTED) { |
+ DCHECK_EQ(jingle_client_.get(), jingle_client); |
LOG(INFO) << "Host connected as " |
<< jingle_client->GetFullJid() << "." << std::endl; |
@@ -197,17 +140,21 @@ void ChromotingHost::OnStateChange(JingleClient* jingle_client, |
LOG(INFO) << "Host disconnected from talk network." << std::endl; |
heartbeat_sender_ = NULL; |
- // Quit the message loop if disconected. |
// TODO(sergeyu): We should try reconnecting here instead of terminating |
// the host. |
- message_loop()->PostTask(FROM_HERE, new MessageLoop::QuitTask()); |
- host_done_->Signal(); |
+ // Post a shutdown task to properly shutdown the chromoting host. |
+ context_->main_message_loop()->PostTask( |
+ FROM_HERE, NewRunnableMethod(this, &ChromotingHost::DoShutdown)); |
} |
} |
bool ChromotingHost::OnAcceptConnection( |
JingleClient* jingle_client, const std::string& jid, |
JingleChannel::Callback** channel_callback) { |
+ AutoLock auto_lock(lock_); |
+ if (state_ != kStarted) |
+ return false; |
+ |
DCHECK_EQ(jingle_client_.get(), jingle_client); |
// TODO(hclam): Allow multiple clients to connect to the host. |
@@ -218,13 +165,18 @@ bool ChromotingHost::OnAcceptConnection( |
// If we accept the connected then create a client object and set the |
// callback. |
- client_ = new ClientConnection(message_loop(), new ProtocolDecoder(), this); |
+ client_ = new ClientConnection(context_->main_message_loop(), |
+ new ProtocolDecoder(), this); |
*channel_callback = client_.get(); |
return true; |
} |
void ChromotingHost::OnNewConnection(JingleClient* jingle_client, |
- scoped_refptr<JingleChannel> channel) { |
+ scoped_refptr<JingleChannel> channel) { |
+ AutoLock auto_lock(lock_); |
+ if (state_ != kStarted) |
+ return; |
+ |
DCHECK_EQ(jingle_client_.get(), jingle_client); |
// Since the session manager has not started, it is still safe to access |
@@ -233,8 +185,67 @@ void ChromotingHost::OnNewConnection(JingleClient* jingle_client, |
client_->set_jingle_channel(channel); |
} |
-MessageLoop* ChromotingHost::message_loop() { |
- return main_thread_.message_loop(); |
+void ChromotingHost::DoStart(Task* shutdown_task) { |
+ DCHECK_EQ(context_->main_message_loop(), MessageLoop::current()); |
+ DCHECK(!jingle_client_); |
+ DCHECK(shutdown_task); |
+ |
+ // Make sure this object is not started. |
+ { |
+ AutoLock auto_lock(lock_); |
+ if (state_ != kInitial) |
+ return; |
+ state_ = kStarted; |
+ } |
+ |
+ // Save the shutdown task. |
+ shutdown_task_.reset(shutdown_task); |
+ |
+ std::string xmpp_login; |
+ std::string xmpp_auth_token; |
+ if (!config_->GetString(kXmppLoginConfigPath, &xmpp_login) || |
+ !config_->GetString(kXmppAuthTokenConfigPath, &xmpp_auth_token)) { |
+ LOG(ERROR) << "XMMP credentials are not defined in config."; |
+ return; |
+ } |
+ |
+ // Connect to the talk network with a JingleClient. |
+ jingle_client_ = new JingleClient(context_->jingle_thread()); |
+ jingle_client_->Init(xmpp_login, xmpp_auth_token, |
+ kChromotingTokenServiceName, this); |
+} |
+ |
+void ChromotingHost::DoShutdown() { |
+ DCHECK_EQ(context_->main_message_loop(), MessageLoop::current()); |
+ |
+ // No-op if this object is not started yet. |
+ { |
+ AutoLock auto_lock(lock_); |
+ if (state_ != kStarted) |
+ return; |
+ state_ = kStopped; |
+ } |
+ |
+ // Tell the session to pause and then disconnect all clients. |
+ if (session_.get()) { |
+ session_->Pause(); |
+ session_->RemoveAllClients(); |
+ } |
+ |
+ // Disconnect all clients. |
+ if (client_) { |
+ client_->Disconnect(); |
+ } |
+ |
+ // Disconnect from the talk network. |
+ if (jingle_client_) { |
+ jingle_client_->Close(); |
+ } |
+ |
+ // Lastly call the shutdown task. |
+ if (shutdown_task_.get()) { |
+ shutdown_task_->Run(); |
+ } |
} |
} // namespace remoting |