summaryrefslogtreecommitdiffstats
path: root/remoting
diff options
context:
space:
mode:
authorhclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-06-28 21:49:30 +0000
committerhclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-06-28 21:49:30 +0000
commit92698ce853664cdd1dee6e7b4a7e6d382dfb8786 (patch)
tree7e81512de53a8d8a333e1a23743b83a5caa50996 /remoting
parent75f30cc2d6e90120b4ac43fcea1c38e783299f6e (diff)
downloadchromium_src-92698ce853664cdd1dee6e7b4a7e6d382dfb8786.zip
chromium_src-92698ce853664cdd1dee6e7b4a7e6d382dfb8786.tar.gz
chromium_src-92698ce853664cdd1dee6e7b4a7e6d382dfb8786.tar.bz2
Fix thread usage in chromoting host
There are several things done in this patch: 1. Isloate thread start and stop to ChromotingHostContext 2. SessionManager now doesn't own capturer and encoder, ownership moved to ChromotingHost 3. Fix up the sequence of actions when ChromotingHost shuts down TEST=remoting_unittests BUG=none Review URL: http://codereview.chromium.org/2829018 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@51050 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting')
-rw-r--r--remoting/host/chromoting_host.cc211
-rw-r--r--remoting/host/chromoting_host.h67
-rw-r--r--remoting/host/chromoting_host_context.cc56
-rw-r--r--remoting/host/chromoting_host_context.h51
-rw-r--r--remoting/host/chromoting_host_context_unittest.cc28
-rw-r--r--remoting/host/client_connection.cc27
-rw-r--r--remoting/host/client_connection.h5
-rw-r--r--remoting/host/client_connection_unittest.cc31
-rw-r--r--remoting/host/encoder_verbatim.cc3
-rw-r--r--remoting/host/session_manager.cc53
-rw-r--r--remoting/host/session_manager.h10
-rw-r--r--remoting/host/simple_host_process.cc27
-rw-r--r--remoting/remoting.gyp3
13 files changed, 408 insertions, 164 deletions
diff --git a/remoting/host/chromoting_host.cc b/remoting/host/chromoting_host.cc
index 0fc60e2..8510678 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
diff --git a/remoting/host/chromoting_host.h b/remoting/host/chromoting_host.h
index 930bf89..fd07791 100644
--- a/remoting/host/chromoting_host.h
+++ b/remoting/host/chromoting_host.h
@@ -17,12 +17,11 @@
#include "remoting/jingle_glue/jingle_client.h"
#include "remoting/jingle_glue/jingle_thread.h"
-namespace base {
-class WaitableEvent;
-} // namespace base
+class Task;
namespace remoting {
+class ChromotingHostContext;
class MutableHostConfig;
// A class to implement the functionality of a host process.
@@ -51,24 +50,24 @@ class MutableHostConfig;
// return to the idle state. We then go to step (2) if there a new
// incoming connection.
class ChromotingHost : public base::RefCountedThreadSafe<ChromotingHost>,
- public ClientConnection::EventHandler,
- public JingleClient::Callback {
+ public ClientConnection::EventHandler,
+ public JingleClient::Callback {
public:
- ChromotingHost(MutableHostConfig* config, Capturer* capturer,
- Encoder* encoder, EventExecutor* executor,
- base::WaitableEvent* host_done);
+ ChromotingHost(ChromotingHostContext* context, MutableHostConfig* config,
+ Capturer* capturer, Encoder* encoder, EventExecutor* executor);
virtual ~ChromotingHost();
- // Run the host porcess. This method returns only after the message loop
- // of the host process exits.
- void Run();
+ // Start the host porcess. This methods starts the chromoting host
+ // asynchronously.
+ //
+ // |shutdown_task| is called if Start() has failed ot Shutdown() is called
+ // and all related operations are completed.
+ //
+ // This method can only be called once during the lifetime of this object.
+ void Start(Task* shutdown_task);
// This method is called when we need to the host process.
- void DestroySession();
-
- // This method talks to the cloud to register the host process. If
- // successful we will start listening to network requests.
- void RegisterHost();
+ void Shutdown();
// This method is called if a client is connected to this object.
void OnClientConnected(ClientConnection* client);
@@ -95,20 +94,21 @@ class ChromotingHost : public base::RefCountedThreadSafe<ChromotingHost>,
scoped_refptr<JingleChannel> channel);
private:
- // The message loop that this class runs on.
- MessageLoop* message_loop();
-
- // The main thread that this object runs on.
- base::Thread main_thread_;
+ enum State {
+ kInitial,
+ kStarted,
+ kStopped,
+ };
- // Used to handle the Jingle connection.
- JingleThread network_thread_;
+ // This method connects to the talk network and start listening for incoming
+ // connections.
+ void DoStart(Task* shutdown_task);
- // A thread that hosts capture operations.
- base::Thread capture_thread_;
+ // This method shuts down the host process.
+ void DoShutdown();
- // A thread that hosts encode operations.
- base::Thread encode_thread_;
+ // The context that the chromoting host runs on.
+ ChromotingHostContext* context_;
scoped_refptr<MutableHostConfig> config_;
@@ -137,8 +137,17 @@ class ChromotingHost : public base::RefCountedThreadSafe<ChromotingHost>,
// Session manager for the host process.
scoped_refptr<SessionManager> session_;
- // Signals the host is ready to be destroyed.
- base::WaitableEvent* host_done_;
+ // This task gets executed when this object fails to connect to the
+ // talk network or Shutdown() is called.
+ scoped_ptr<Task> shutdown_task_;
+
+ // Tracks the internal state of the host.
+ // This variable is written on the main thread of ChromotingHostContext
+ // and read by jingle thread.
+ State state_;
+
+ // Lock is to lock the access to |state_|.
+ Lock lock_;
DISALLOW_COPY_AND_ASSIGN(ChromotingHost);
};
diff --git a/remoting/host/chromoting_host_context.cc b/remoting/host/chromoting_host_context.cc
new file mode 100644
index 0000000..2f889a1
--- /dev/null
+++ b/remoting/host/chromoting_host_context.cc
@@ -0,0 +1,56 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "remoting/host/chromoting_host_context.h"
+
+#include <string>
+
+#include "base/thread.h"
+#include "remoting/jingle_glue/jingle_thread.h"
+
+namespace remoting {
+
+ChromotingHostContext::ChromotingHostContext()
+ : main_thread_("ChromotingMainThread"),
+ capture_thread_("ChromotingCaptureThread"),
+ encode_thread_("ChromotingEncodeThread") {
+}
+
+ChromotingHostContext::~ChromotingHostContext() {
+}
+
+void ChromotingHostContext::Start() {
+ // Start all the threads.
+ main_thread_.StartWithOptions(
+ base::Thread::Options(MessageLoop::TYPE_UI, 0));
+ capture_thread_.Start();
+ encode_thread_.Start();
+ jingle_thread_.Start();
+}
+
+void ChromotingHostContext::Stop() {
+ // Stop all the threads.
+ jingle_thread_.Stop();
+ encode_thread_.Stop();
+ capture_thread_.Stop();
+ main_thread_.Stop();
+}
+
+JingleThread* ChromotingHostContext::jingle_thread() {
+ return &jingle_thread_;
+}
+
+MessageLoop* ChromotingHostContext::main_message_loop() {
+ return main_thread_.message_loop();
+}
+
+MessageLoop* ChromotingHostContext::capture_message_loop() {
+ return capture_thread_.message_loop();
+}
+
+MessageLoop* ChromotingHostContext::encode_message_loop() {
+ return encode_thread_.message_loop();
+}
+
+} // namespace remoting
diff --git a/remoting/host/chromoting_host_context.h b/remoting/host/chromoting_host_context.h
new file mode 100644
index 0000000..e336c1e
--- /dev/null
+++ b/remoting/host/chromoting_host_context.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef REMOTING_CHROMOTING_HOST_CONTEXT_H_
+#define REMOTING_CHROMOTING_HOST_CONTEXT_H_
+
+#include <string>
+
+#include "base/thread.h"
+#include "remoting/jingle_glue/jingle_thread.h"
+#include "testing/gtest/include/gtest/gtest_prod.h"
+
+namespace remoting {
+
+// A class that manages threads and running context for the chromoting host
+// process.
+class ChromotingHostContext {
+ public:
+ ChromotingHostContext();
+ virtual ~ChromotingHostContext();
+
+ virtual void Start();
+ virtual void Stop();
+
+ virtual JingleThread* jingle_thread();
+ virtual MessageLoop* main_message_loop();
+ virtual MessageLoop* capture_message_loop();
+ virtual MessageLoop* encode_message_loop();
+
+ private:
+ FRIEND_TEST(ChromotingHostContextTest, StartAndStop);
+
+ // A thread that host network operations.
+ JingleThread jingle_thread_;
+
+ // A thread that host ChromotingHost.
+ base::Thread main_thread_;
+
+ // A thread that host all capture operations.
+ base::Thread capture_thread_;
+
+ // A thread that host all encode operations.
+ base::Thread encode_thread_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChromotingHostContext);
+};
+
+} // namespace remoting
+
+#endif // REMOTING_HOST_CHROMOTING_HOST_CONTEXT_H_
diff --git a/remoting/host/chromoting_host_context_unittest.cc b/remoting/host/chromoting_host_context_unittest.cc
new file mode 100644
index 0000000..6b481d7
--- /dev/null
+++ b/remoting/host/chromoting_host_context_unittest.cc
@@ -0,0 +1,28 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/message_loop.h"
+#include "remoting/host/chromoting_host_context.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace remoting {
+
+// A simple test that starts and stop the context. This tests the context
+// operates properly and all threads and message loops are valid.
+TEST(ChromotingHostContextTest, StartAndStop) {
+ ChromotingHostContext context;
+ context.Start();
+ EXPECT_TRUE(context.jingle_thread());
+ EXPECT_TRUE(context.main_message_loop());
+ EXPECT_TRUE(context.capture_message_loop());
+ EXPECT_TRUE(context.encode_message_loop());
+ context.Stop();
+
+ // Expect all the threads are stopped.
+ EXPECT_FALSE(context.main_thread_.IsRunning());
+ EXPECT_FALSE(context.capture_thread_.IsRunning());
+ EXPECT_FALSE(context.encode_thread_.IsRunning());
+}
+
+} // namespace remoting
diff --git a/remoting/host/client_connection.cc b/remoting/host/client_connection.cc
index d2c0df0..d69a326 100644
--- a/remoting/host/client_connection.cc
+++ b/remoting/host/client_connection.cc
@@ -38,7 +38,10 @@ ClientConnection::~ClientConnection() {
void ClientConnection::SendInitClientMessage(int width, int height) {
DCHECK_EQ(loop_, MessageLoop::current());
DCHECK(!update_stream_size_);
- DCHECK(channel_.get());
+
+ // If we are disconnected then return.
+ if (!channel_)
+ return;
HostMessage msg;
msg.mutable_init_client()->set_width(width);
@@ -49,7 +52,10 @@ void ClientConnection::SendInitClientMessage(int width, int height) {
void ClientConnection::SendBeginUpdateStreamMessage() {
DCHECK_EQ(loop_, MessageLoop::current());
- DCHECK(channel_.get());
+
+ // If we are disconnected then return.
+ if (!channel_)
+ return;
HostMessage msg;
msg.mutable_begin_update_stream();
@@ -65,7 +71,10 @@ void ClientConnection::SendUpdateStreamPacketMessage(
const UpdateStreamPacketHeader* header,
scoped_refptr<DataBuffer> data) {
DCHECK_EQ(loop_, MessageLoop::current());
- DCHECK(channel_.get());
+
+ // If we are disconnected then return.
+ if (!channel_)
+ return;
HostMessage msg;
msg.mutable_update_stream_packet()->mutable_header()->CopyFrom(*header);
@@ -81,7 +90,10 @@ void ClientConnection::SendUpdateStreamPacketMessage(
void ClientConnection::SendEndUpdateStreamMessage() {
DCHECK_EQ(loop_, MessageLoop::current());
- DCHECK(channel_.get());
+
+ // If we are disconnected then return.
+ if (!channel_)
+ return;
HostMessage msg;
msg.mutable_end_update_stream();
@@ -116,8 +128,11 @@ int ClientConnection::GetPendingUpdateStreamMessages() {
void ClientConnection::Disconnect() {
DCHECK_EQ(loop_, MessageLoop::current());
- DCHECK(channel_.get());
- channel_->Close();
+ // If there is a channel then close it and release the reference.
+ if (channel_) {
+ channel_->Close();
+ channel_ = NULL;
+ }
}
void ClientConnection::OnStateChange(JingleChannel* channel,
diff --git a/remoting/host/client_connection.h b/remoting/host/client_connection.h
index 810eb61..3e85cc9 100644
--- a/remoting/host/client_connection.h
+++ b/remoting/host/client_connection.h
@@ -90,7 +90,10 @@ class ClientConnection : public base::RefCountedThreadSafe<ClientConnection>,
// TODO(hclam): Report this number accurately.
virtual int GetPendingUpdateStreamMessages();
- // Disconnect the remote viewer.
+ // Disconnect the client connection. This method is allowed to be called
+ // more than once and calls after the first one will be ignored.
+ //
+ // After this method is called all the send method calls will be ignored.
virtual void Disconnect();
/////////////////////////////////////////////////////////////////////////////
diff --git a/remoting/host/client_connection_unittest.cc b/remoting/host/client_connection_unittest.cc
index f82c887..b7cb10c 100644
--- a/remoting/host/client_connection_unittest.cc
+++ b/remoting/host/client_connection_unittest.cc
@@ -12,6 +12,7 @@
using ::testing::_;
using ::testing::NotNull;
+using ::testing::StrictMock;
namespace remoting {
@@ -23,13 +24,13 @@ class ClientConnectionTest : public testing::Test {
protected:
virtual void SetUp() {
decoder_ = new MockProtocolDecoder();
- channel_ = new MockJingleChannel();
+ channel_ = new StrictMock<MockJingleChannel>();
// Allocate a ClientConnection object with the mock objects. we give the
// ownership of decoder to the viewer.
viewer_ = new ClientConnection(&message_loop_,
- decoder_,
- &handler_);
+ decoder_,
+ &handler_);
viewer_->set_jingle_channel(channel_.get());
}
@@ -37,9 +38,12 @@ class ClientConnectionTest : public testing::Test {
MessageLoop message_loop_;
MockProtocolDecoder* decoder_;
MockClientConnectionEventHandler handler_;
- scoped_refptr<MockJingleChannel> channel_;
scoped_refptr<ClientConnection> viewer_;
+ // |channel_| is wrapped with StrictMock because we limit strictly the calls
+ // to it.
+ scoped_refptr<StrictMock<MockJingleChannel> > channel_;
+
private:
DISALLOW_COPY_AND_ASSIGN(ClientConnectionTest);
};
@@ -95,4 +99,23 @@ TEST_F(ClientConnectionTest, ParseMessages) {
message_loop_.RunAllPending();
}
+// Test that we can close client connection more than once and operations
+// after the connection is closed has no effect.
+TEST_F(ClientConnectionTest, Close) {
+ EXPECT_CALL(*channel_, Close());
+ viewer_->Disconnect();
+
+ viewer_->SendBeginUpdateStreamMessage();
+ scoped_ptr<UpdateStreamPacketHeader> header(new UpdateStreamPacketHeader);
+ header->set_x(0);
+ header->set_y(0);
+ header->set_width(640);
+ header->set_height(480);
+
+ scoped_refptr<media::DataBuffer> data = new media::DataBuffer(10);
+ viewer_->SendUpdateStreamPacketMessage(header.get(), data);
+ viewer_->SendEndUpdateStreamMessage();
+ viewer_->Disconnect();
+}
+
} // namespace remoting
diff --git a/remoting/host/encoder_verbatim.cc b/remoting/host/encoder_verbatim.cc
index 3151c72..ce0d405 100644
--- a/remoting/host/encoder_verbatim.cc
+++ b/remoting/host/encoder_verbatim.cc
@@ -30,7 +30,8 @@ void EncoderVerbatim::Encode(const DirtyRects& dirty_rects,
EncodingState state = EncodingInProgress;
if (i == 0) {
state |= EncodingStarting;
- } else if (i == num_rects - 1) {
+ }
+ if (i == num_rects - 1) {
state |= EncodingEnded;
}
data_available_callback->Run(header.release(),
diff --git a/remoting/host/session_manager.cc b/remoting/host/session_manager.cc
index cd6fc47..1e04482 100644
--- a/remoting/host/session_manager.cc
+++ b/remoting/host/session_manager.cc
@@ -139,6 +139,12 @@ void SessionManager::RemoveClient(scoped_refptr<ClientConnection> client) {
NewRunnableMethod(this, &SessionManager::DoRemoveClient, client));
}
+void SessionManager::RemoveAllClients() {
+ network_loop_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &SessionManager::DoRemoveAllClients));
+}
+
void SessionManager::DoCapture() {
DCHECK_EQ(capture_loop_, MessageLoop::current());
@@ -166,8 +172,7 @@ void SessionManager::DoCapture() {
ScheduleNextCapture();
// And finally perform one capture.
- DCHECK(capturer_.get());
- capturer_->CaptureDirtyRects(
+ capturer()->CaptureDirtyRects(
NewRunnableMethod(this, &SessionManager::CaptureDoneTask));
}
@@ -190,13 +195,11 @@ void SessionManager::DoEncode(const CaptureData *capture_data) {
DCHECK_EQ(encode_loop_, MessageLoop::current());
- DCHECK(encoder_.get());
-
// TODO(hclam): Enable |force_refresh| if a new client was
// added.
- encoder_->SetSize(capture_data->width_, capture_data->height_);
- encoder_->SetPixelFormat(capture_data->pixel_format_);
- encoder_->Encode(
+ encoder()->SetSize(capture_data->width_, capture_data->height_);
+ encoder()->SetPixelFormat(capture_data->pixel_format_);
+ encoder()->Encode(
capture_data->dirty_rects_,
capture_data->data_,
capture_data->data_strides_,
@@ -239,7 +242,7 @@ void SessionManager::DoGetInitInfo(scoped_refptr<ClientConnection> client) {
network_loop_->PostTask(
FROM_HERE,
NewRunnableMethod(this, &SessionManager::DoSendInit, client,
- capturer_->GetWidth(), capturer_->GetHeight()));
+ capturer()->GetWidth(), capturer()->GetHeight()));
// And then add the client to the list so it can receive update stream.
// It is important we do so in such order or the client will receive
@@ -288,8 +291,16 @@ void SessionManager::DoRemoveClient(scoped_refptr<ClientConnection> client) {
// TODO(hclam): Is it correct to do to a scoped_refptr?
ClientConnectionList::iterator it
= std::find(clients_.begin(), clients_.end(), client);
- if (it != clients_.end())
+ if (it != clients_.end()) {
clients_.erase(it);
+ }
+}
+
+void SessionManager::DoRemoveAllClients() {
+ DCHECK_EQ(network_loop_, MessageLoop::current());
+
+ // Clear the list of clients.
+ clients_.clear();
}
void SessionManager::DoRateControl() {
@@ -354,12 +365,12 @@ void SessionManager::CaptureDoneTask() {
scoped_ptr<CaptureData> data(new CaptureData);
// Save results of the capture.
- capturer_->GetData(data->data_);
- capturer_->GetDataStride(data->data_strides_);
- capturer_->GetDirtyRects(&data->dirty_rects_);
- data->pixel_format_ = capturer_->GetPixelFormat();
- data->width_ = capturer_->GetWidth();
- data->height_ = capturer_->GetHeight();
+ capturer()->GetData(data->data_);
+ capturer()->GetDataStride(data->data_strides_);
+ capturer()->GetDirtyRects(&data->dirty_rects_);
+ data->pixel_format_ = capturer()->GetPixelFormat();
+ data->width_ = capturer()->GetWidth();
+ data->height_ = capturer()->GetHeight();
encode_loop_->PostTask(
FROM_HERE,
@@ -384,10 +395,20 @@ void SessionManager::EncodeDataAvailableTask(
data,
state));
- if (state == Encoder::EncodingEnded) {
+ if (state & Encoder::EncodingEnded) {
capture_loop_->PostTask(
FROM_HERE, NewRunnableMethod(this, &SessionManager::DoFinishEncode));
}
}
+Capturer* SessionManager::capturer() {
+ DCHECK_EQ(capture_loop_, MessageLoop::current());
+ return capturer_.get();
+}
+
+Encoder* SessionManager::encoder() {
+ DCHECK_EQ(encode_loop_, MessageLoop::current());
+ return encoder_.get();
+}
+
} // namespace remoting
diff --git a/remoting/host/session_manager.h b/remoting/host/session_manager.h
index 9c8ad50..d624a35 100644
--- a/remoting/host/session_manager.h
+++ b/remoting/host/session_manager.h
@@ -67,7 +67,7 @@ class SessionManager : public base::RefCountedThreadSafe<SessionManager> {
public:
// Construct a SessionManager. Message loops and threads are provided.
- // Ownership of Capturer and Encoder are given to this object.
+ // This object does not own capturer and encoder.
SessionManager(MessageLoop* capture_loop,
MessageLoop* encode_loop,
MessageLoop* network_loop,
@@ -95,6 +95,9 @@ class SessionManager : public base::RefCountedThreadSafe<SessionManager> {
// Remove a client from receiving screen updates.
void RemoveClient(scoped_refptr<ClientConnection> client);
+ // Remove all clients.
+ void RemoveAllClients();
+
private:
// Stores the data and information of a capture to pass off to the
@@ -130,6 +133,7 @@ class SessionManager : public base::RefCountedThreadSafe<SessionManager> {
void DoSetMaxRate(double max_rate);
void DoAddClient(scoped_refptr<ClientConnection> client);
void DoRemoveClient(scoped_refptr<ClientConnection> client);
+ void DoRemoveAllClients();
void DoRateControl();
// Hepler method to schedule next capture using the current rate.
@@ -145,6 +149,10 @@ class SessionManager : public base::RefCountedThreadSafe<SessionManager> {
const scoped_refptr<media::DataBuffer>& data,
Encoder::EncodingState state);
+ // Getters for capturer and encoder.
+ Capturer* capturer();
+ Encoder* encoder();
+
// Message loops used by this class.
MessageLoop* capture_loop_;
MessageLoop* encode_loop_;
diff --git a/remoting/host/simple_host_process.cc b/remoting/host/simple_host_process.cc
index 0f12c43..8697b3d 100644
--- a/remoting/host/simple_host_process.cc
+++ b/remoting/host/simple_host_process.cc
@@ -27,6 +27,7 @@
#include "base/waitable_event.h"
#include "remoting/host/capturer_fake.h"
#include "remoting/host/chromoting_host.h"
+#include "remoting/host/chromoting_host_context.h"
#include "remoting/host/encoder_verbatim.h"
#include "remoting/host/json_host_config.h"
@@ -52,6 +53,10 @@ const char kHomePath[] = "HOME";
static char* GetEnvironmentVar(const char* x) { return getenv(x); }
#endif
+void ShutdownTask(MessageLoop* message_loop) {
+ message_loop->PostTask(FROM_HERE, new MessageLoop::QuitTask());
+}
+
const std::string kFakeSwitchName = "fake";
const std::string kConfigSwitchName = "config";
@@ -96,6 +101,7 @@ int main(int argc, char** argv) {
if (fake) {
// Inject a fake capturer.
+ LOG(INFO) << "Usage a fake capturer.";
capturer.reset(new remoting::CapturerFake());
}
@@ -111,16 +117,25 @@ int main(int argc, char** argv) {
return 1;
}
- base::WaitableEvent host_done(false, false);
+ // Allocate a chromoting context and starts it.
+ remoting::ChromotingHostContext context;
+ context.Start();
+
+ // Construct a chromoting host.
scoped_refptr<remoting::ChromotingHost> host =
- new remoting::ChromotingHost(config,
+ new remoting::ChromotingHost(&context,
+ config,
capturer.release(),
encoder.release(),
- executor.release(),
- &host_done);
- host->Run();
- host_done.Wait();
+ executor.release());
+
+ // Let the chromoting host runs until the shutdown task is executed.
+ MessageLoop message_loop;
+ host->Start(NewRunnableFunction(&ShutdownTask, &message_loop));
+ message_loop.Run();
+ // And then stop the chromoting context.
+ context.Stop();
file_io_thread.Stop();
return 0;
}
diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp
index 1e037ef..341f10d 100644
--- a/remoting/remoting.gyp
+++ b/remoting/remoting.gyp
@@ -145,6 +145,8 @@
'host/capturer.h',
'host/chromoting_host.cc',
'host/chromoting_host.h',
+ 'host/chromoting_host_context.cc',
+ 'host/chromoting_hsot_context.h',
'host/client_connection.cc',
'host/client_connection.h',
'host/differ.h',
@@ -323,6 +325,7 @@
'base/protocol_decoder_unittest.cc',
'client/mock_objects.h',
'client/decoder_verbatim_unittest.cc',
+ 'host/chromoting_host_context_unittest.cc',
'host/differ_unittest.cc',
'host/differ_block_unittest.cc',
'host/json_host_config_unittest.cc',