Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(7)

Side by Side Diff: remoting/host/chromoting_host.cc

Issue 2829018: Fix thread usage in chromoting host (Closed)
Patch Set: removed useless test Created 10 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « remoting/host/chromoting_host.h ('k') | remoting/host/chromoting_host_context.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "remoting/host/chromoting_host.h" 5 #include "remoting/host/chromoting_host.h"
6 6
7 #include "base/stl_util-inl.h" 7 #include "base/stl_util-inl.h"
8 #include "base/waitable_event.h" 8 #include "base/task.h"
9 #include "build/build_config.h" 9 #include "build/build_config.h"
10 #include "remoting/base/constants.h" 10 #include "remoting/base/constants.h"
11 #include "remoting/base/protocol_decoder.h" 11 #include "remoting/base/protocol_decoder.h"
12 #include "remoting/host/chromoting_host_context.h"
12 #include "remoting/host/host_config.h" 13 #include "remoting/host/host_config.h"
13 #include "remoting/host/session_manager.h" 14 #include "remoting/host/session_manager.h"
14 #include "remoting/jingle_glue/jingle_channel.h" 15 #include "remoting/jingle_glue/jingle_channel.h"
15 16
16 namespace remoting { 17 namespace remoting {
17 18
18 ChromotingHost::ChromotingHost(MutableHostConfig* config, 19 ChromotingHost::ChromotingHost(ChromotingHostContext* context,
20 MutableHostConfig* config,
19 Capturer* capturer, 21 Capturer* capturer,
20 Encoder* encoder, 22 Encoder* encoder,
21 EventExecutor* executor, 23 EventExecutor* executor)
22 base::WaitableEvent* host_done) 24 : context_(context),
23 : main_thread_("MainThread"), 25 config_(config),
24 capture_thread_("CaptureThread"), 26 capturer_(capturer),
25 encode_thread_("EncodeThread"), 27 encoder_(encoder),
26 config_(config), 28 executor_(executor),
27 capturer_(capturer), 29 state_(kInitial) {
28 encoder_(encoder),
29 executor_(executor),
30 host_done_(host_done) {
31 // TODO(ajwong): The thread injection and object ownership is odd here.
32 // Fix so we do not start this thread in the constructor, so we only
33 // take in a session manager, don't let session manager own the
34 // capturer/encoder, and then associate the capturer and encoder threads with
35 // the capturer and encoder objects directly. This will require a
36 // non-refcounted NewRunnableMethod.
37 main_thread_.StartWithOptions(
38 base::Thread::Options(MessageLoop::TYPE_UI, 0));
39 network_thread_.Start();
40 } 30 }
41 31
42 ChromotingHost::~ChromotingHost() { 32 ChromotingHost::~ChromotingHost() {
43 // TODO(ajwong): We really need to inject these threads and get rid of these
44 // start/stops.
45 main_thread_.Stop();
46 network_thread_.Stop();
47 DCHECK(!encode_thread_.IsRunning());
48 DCHECK(!capture_thread_.IsRunning());
49 } 33 }
50 34
51 void ChromotingHost::Run() { 35 void ChromotingHost::Start(Task* shutdown_task) {
52 // Submit a task to perform host registration. We'll also start 36 // Submit a task to perform host registration. We'll also start
53 // listening to connection if registration is done. 37 // listening to connection if registration is done.
54 message_loop()->PostTask( 38 context_->main_message_loop()->PostTask(
55 FROM_HERE, 39 FROM_HERE,
56 NewRunnableMethod(this, &ChromotingHost::RegisterHost)); 40 NewRunnableMethod(this, &ChromotingHost::DoStart, shutdown_task));
57 } 41 }
58 42
59 // This method is called when we need to destroy the host process. 43 // This method is called when we need to destroy the host process.
60 void ChromotingHost::DestroySession() { 44 void ChromotingHost::Shutdown() {
61 DCHECK_EQ(message_loop(), MessageLoop::current()); 45 context_->main_message_loop()->PostTask(
62 46 FROM_HERE,
63 // First we tell the session to pause and then we wait until all 47 NewRunnableMethod(this, &ChromotingHost::DoShutdown));
64 // the tasks are done.
65 if (session_.get()) {
66 session_->Pause();
67
68 // TODO(hclam): Revise the order.
69 DCHECK(encode_thread_.IsRunning());
70 encode_thread_.Stop();
71
72 DCHECK(capture_thread_.IsRunning());
73 capture_thread_.Stop();
74 }
75 }
76
77 // This method talks to the cloud to register the host process. If
78 // successful we will start listening to network requests.
79 void ChromotingHost::RegisterHost() {
80 DCHECK_EQ(message_loop(), MessageLoop::current());
81 DCHECK(!jingle_client_);
82
83 std::string xmpp_login;
84 std::string xmpp_auth_token;
85 if (!config_->GetString(kXmppLoginConfigPath, &xmpp_login) ||
86 !config_->GetString(kXmppAuthTokenConfigPath, &xmpp_auth_token)) {
87 LOG(ERROR) << "XMMP credentials are not defined in config.";
88 return;
89 }
90
91 // Connect to the talk network with a JingleClient.
92 jingle_client_ = new JingleClient(&network_thread_);
93 jingle_client_->Init(xmpp_login, xmpp_auth_token,
94 kChromotingTokenServiceName, this);
95 } 48 }
96 49
97 // This method is called if a client is connected to this object. 50 // This method is called if a client is connected to this object.
98 void ChromotingHost::OnClientConnected(ClientConnection* client) { 51 void ChromotingHost::OnClientConnected(ClientConnection* client) {
99 DCHECK_EQ(message_loop(), MessageLoop::current()); 52 DCHECK_EQ(context_->main_message_loop(), MessageLoop::current());
100 53
101 // Create a new RecordSession if there was none. 54 // Create a new RecordSession if there was none.
102 if (!session_.get()) { 55 if (!session_.get()) {
103 // The first we need to make sure capture and encode thread are
104 // running.
105 capture_thread_.Start();
106 encode_thread_.Start();
107
108 // Then we create a SessionManager passing the message loops that 56 // Then we create a SessionManager passing the message loops that
109 // it should run on. 57 // it should run on.
110 // Note that we pass the ownership of the capturer and encoder to
111 // the session manager.
112 DCHECK(capturer_.get()); 58 DCHECK(capturer_.get());
113 DCHECK(encoder_.get()); 59 DCHECK(encoder_.get());
114 session_ = new SessionManager(capture_thread_.message_loop(), 60 session_ = new SessionManager(context_->capture_message_loop(),
115 encode_thread_.message_loop(), 61 context_->encode_message_loop(),
116 message_loop(), 62 context_->main_message_loop(),
117 capturer_.release(), 63 capturer_.get(),
118 encoder_.release()); 64 encoder_.get());
119 65
120 // Immediately add the client and start the session. 66 // Immediately add the client and start the session.
121 session_->AddClient(client); 67 session_->AddClient(client);
122 session_->Start(); 68 session_->Start();
123 LOG(INFO) << "Session manager started"; 69 LOG(INFO) << "Session manager started";
124 } else { 70 } else {
125 // If a session manager already exists we simply add the new client. 71 // If a session manager already exists we simply add the new client.
126 session_->AddClient(client); 72 session_->AddClient(client);
127 } 73 }
128 } 74 }
129 75
130 void ChromotingHost::OnClientDisconnected(ClientConnection* client) { 76 void ChromotingHost::OnClientDisconnected(ClientConnection* client) {
131 DCHECK_EQ(message_loop(), MessageLoop::current()); 77 DCHECK_EQ(context_->main_message_loop(), MessageLoop::current());
132 78
133 // Remove the client from the session manager. 79 // Remove the client from the session manager and pause the session.
134 if (session_.get()) 80 // TODO(hclam): Pause only if the last client disconnected.
81 if (session_.get()) {
135 session_->RemoveClient(client); 82 session_->RemoveClient(client);
83 session_->Pause();
84 }
136 85
137 // Also remove reference to ClientConnection from this object. 86 // Also remove reference to ClientConnection from this object.
138 client_ = NULL; 87 client_ = NULL;
139
140 // TODO(hclam): If the last client has disconnected we need to destroy
141 // the session manager and shutdown the capture and encode threads.
142 // Right now we assume that there's only one client.
143 DestroySession();
144 } 88 }
145 89
146 //////////////////////////////////////////////////////////////////////////// 90 ////////////////////////////////////////////////////////////////////////////
147 // ClientConnection::EventHandler implementations 91 // ClientConnection::EventHandler implementations
148 void ChromotingHost::HandleMessages(ClientConnection* client, 92 void ChromotingHost::HandleMessages(ClientConnection* client,
149 ClientMessageList* messages) { 93 ClientMessageList* messages) {
150 DCHECK_EQ(message_loop(), MessageLoop::current()); 94 DCHECK_EQ(context_->main_message_loop(), MessageLoop::current());
151 95
152 // Delegate the messages to EventExecutor and delete the unhandled 96 // Delegate the messages to EventExecutor and delete the unhandled
153 // messages. 97 // messages.
154 DCHECK(executor_.get()); 98 DCHECK(executor_.get());
155 executor_->HandleInputEvents(messages); 99 executor_->HandleInputEvents(messages);
156 STLDeleteElements<ClientMessageList>(messages); 100 STLDeleteElements<ClientMessageList>(messages);
157 } 101 }
158 102
159 void ChromotingHost::OnConnectionOpened(ClientConnection* client) { 103 void ChromotingHost::OnConnectionOpened(ClientConnection* client) {
160 DCHECK_EQ(message_loop(), MessageLoop::current()); 104 DCHECK_EQ(context_->main_message_loop(), MessageLoop::current());
161 105
162 // Completes the client connection. 106 // Completes the client connection.
163 LOG(INFO) << "Connection to client established."; 107 LOG(INFO) << "Connection to client established.";
164 OnClientConnected(client_.get()); 108 OnClientConnected(client_.get());
165 } 109 }
166 110
167 void ChromotingHost::OnConnectionClosed(ClientConnection* client) { 111 void ChromotingHost::OnConnectionClosed(ClientConnection* client) {
168 DCHECK_EQ(message_loop(), MessageLoop::current()); 112 DCHECK_EQ(context_->main_message_loop(), MessageLoop::current());
169 113
170 // Completes the client connection. 114 // Completes the client connection.
171 LOG(INFO) << "Connection to client closed."; 115 LOG(INFO) << "Connection to client closed.";
172 OnClientDisconnected(client_.get()); 116 OnClientDisconnected(client_.get());
173 } 117 }
174 118
175 void ChromotingHost::OnConnectionFailed(ClientConnection* client) { 119 void ChromotingHost::OnConnectionFailed(ClientConnection* client) {
176 DCHECK_EQ(message_loop(), MessageLoop::current()); 120 DCHECK_EQ(context_->main_message_loop(), MessageLoop::current());
177 121
178 // The client has disconnected. 122 // The client has disconnected.
179 LOG(ERROR) << "Connection failed unexpectedly."; 123 LOG(ERROR) << "Connection failed unexpectedly.";
180 OnClientDisconnected(client_.get()); 124 OnClientDisconnected(client_.get());
181 } 125 }
182 126
183 //////////////////////////////////////////////////////////////////////////// 127 ////////////////////////////////////////////////////////////////////////////
184 // JingleClient::Callback implementations 128 // JingleClient::Callback implementations
185 void ChromotingHost::OnStateChange(JingleClient* jingle_client, 129 void ChromotingHost::OnStateChange(JingleClient* jingle_client,
186 JingleClient::State state) { 130 JingleClient::State state) {
187 DCHECK_EQ(jingle_client_.get(), jingle_client);
188
189 if (state == JingleClient::CONNECTED) { 131 if (state == JingleClient::CONNECTED) {
132 DCHECK_EQ(jingle_client_.get(), jingle_client);
190 LOG(INFO) << "Host connected as " 133 LOG(INFO) << "Host connected as "
191 << jingle_client->GetFullJid() << "." << std::endl; 134 << jingle_client->GetFullJid() << "." << std::endl;
192 135
193 // Start heartbeating after we connected 136 // Start heartbeating after we connected
194 heartbeat_sender_ = new HeartbeatSender(); 137 heartbeat_sender_ = new HeartbeatSender();
195 heartbeat_sender_->Start(config_, jingle_client_.get()); 138 heartbeat_sender_->Start(config_, jingle_client_.get());
196 } else if (state == JingleClient::CLOSED) { 139 } else if (state == JingleClient::CLOSED) {
197 LOG(INFO) << "Host disconnected from talk network." << std::endl; 140 LOG(INFO) << "Host disconnected from talk network." << std::endl;
198 heartbeat_sender_ = NULL; 141 heartbeat_sender_ = NULL;
199 142
200 // Quit the message loop if disconected.
201 // TODO(sergeyu): We should try reconnecting here instead of terminating 143 // TODO(sergeyu): We should try reconnecting here instead of terminating
202 // the host. 144 // the host.
203 message_loop()->PostTask(FROM_HERE, new MessageLoop::QuitTask()); 145 // Post a shutdown task to properly shutdown the chromoting host.
204 host_done_->Signal(); 146 context_->main_message_loop()->PostTask(
147 FROM_HERE, NewRunnableMethod(this, &ChromotingHost::DoShutdown));
205 } 148 }
206 } 149 }
207 150
208 bool ChromotingHost::OnAcceptConnection( 151 bool ChromotingHost::OnAcceptConnection(
209 JingleClient* jingle_client, const std::string& jid, 152 JingleClient* jingle_client, const std::string& jid,
210 JingleChannel::Callback** channel_callback) { 153 JingleChannel::Callback** channel_callback) {
154 AutoLock auto_lock(lock_);
155 if (state_ != kStarted)
156 return false;
157
211 DCHECK_EQ(jingle_client_.get(), jingle_client); 158 DCHECK_EQ(jingle_client_.get(), jingle_client);
212 159
213 // TODO(hclam): Allow multiple clients to connect to the host. 160 // TODO(hclam): Allow multiple clients to connect to the host.
214 if (client_.get()) 161 if (client_.get())
215 return false; 162 return false;
216 163
217 LOG(INFO) << "Client connected: " << jid << std::endl; 164 LOG(INFO) << "Client connected: " << jid << std::endl;
218 165
219 // If we accept the connected then create a client object and set the 166 // If we accept the connected then create a client object and set the
220 // callback. 167 // callback.
221 client_ = new ClientConnection(message_loop(), new ProtocolDecoder(), this); 168 client_ = new ClientConnection(context_->main_message_loop(),
169 new ProtocolDecoder(), this);
222 *channel_callback = client_.get(); 170 *channel_callback = client_.get();
223 return true; 171 return true;
224 } 172 }
225 173
226 void ChromotingHost::OnNewConnection(JingleClient* jingle_client, 174 void ChromotingHost::OnNewConnection(JingleClient* jingle_client,
227 scoped_refptr<JingleChannel> channel) { 175 scoped_refptr<JingleChannel> channel) {
176 AutoLock auto_lock(lock_);
177 if (state_ != kStarted)
178 return;
179
228 DCHECK_EQ(jingle_client_.get(), jingle_client); 180 DCHECK_EQ(jingle_client_.get(), jingle_client);
229 181
230 // Since the session manager has not started, it is still safe to access 182 // Since the session manager has not started, it is still safe to access
231 // the client directly. Note that we give the ownership of the channel 183 // the client directly. Note that we give the ownership of the channel
232 // to the client. 184 // to the client.
233 client_->set_jingle_channel(channel); 185 client_->set_jingle_channel(channel);
234 } 186 }
235 187
236 MessageLoop* ChromotingHost::message_loop() { 188 void ChromotingHost::DoStart(Task* shutdown_task) {
237 return main_thread_.message_loop(); 189 DCHECK_EQ(context_->main_message_loop(), MessageLoop::current());
190 DCHECK(!jingle_client_);
191 DCHECK(shutdown_task);
192
193 // Make sure this object is not started.
194 {
195 AutoLock auto_lock(lock_);
196 if (state_ != kInitial)
197 return;
198 state_ = kStarted;
199 }
200
201 // Save the shutdown task.
202 shutdown_task_.reset(shutdown_task);
203
204 std::string xmpp_login;
205 std::string xmpp_auth_token;
206 if (!config_->GetString(kXmppLoginConfigPath, &xmpp_login) ||
207 !config_->GetString(kXmppAuthTokenConfigPath, &xmpp_auth_token)) {
208 LOG(ERROR) << "XMMP credentials are not defined in config.";
209 return;
210 }
211
212 // Connect to the talk network with a JingleClient.
213 jingle_client_ = new JingleClient(context_->jingle_thread());
214 jingle_client_->Init(xmpp_login, xmpp_auth_token,
215 kChromotingTokenServiceName, this);
216 }
217
218 void ChromotingHost::DoShutdown() {
219 DCHECK_EQ(context_->main_message_loop(), MessageLoop::current());
220
221 // No-op if this object is not started yet.
222 {
223 AutoLock auto_lock(lock_);
224 if (state_ != kStarted)
225 return;
226 state_ = kStopped;
227 }
228
229 // Tell the session to pause and then disconnect all clients.
230 if (session_.get()) {
231 session_->Pause();
232 session_->RemoveAllClients();
233 }
234
235 // Disconnect all clients.
236 if (client_) {
237 client_->Disconnect();
238 }
239
240 // Disconnect from the talk network.
241 if (jingle_client_) {
242 jingle_client_->Close();
243 }
244
245 // Lastly call the shutdown task.
246 if (shutdown_task_.get()) {
247 shutdown_task_->Run();
248 }
238 } 249 }
239 250
240 } // namespace remoting 251 } // namespace remoting
OLDNEW
« no previous file with comments | « remoting/host/chromoting_host.h ('k') | remoting/host/chromoting_host_context.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698