OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 "dbus/exported_object.h" | 5 #include "dbus/exported_object.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/logging.h" | 8 #include "base/logging.h" |
9 #include "base/memory/ref_counted.h" | 9 #include "base/memory/ref_counted.h" |
10 #include "base/message_loop.h" | 10 #include "base/message_loop.h" |
(...skipping 21 matching lines...) Expand all Loading... |
32 } | 32 } |
33 | 33 |
34 } // namespace | 34 } // namespace |
35 | 35 |
36 ExportedObject::ExportedObject(Bus* bus, | 36 ExportedObject::ExportedObject(Bus* bus, |
37 const std::string& service_name, | 37 const std::string& service_name, |
38 const std::string& object_path) | 38 const std::string& object_path) |
39 : bus_(bus), | 39 : bus_(bus), |
40 service_name_(service_name), | 40 service_name_(service_name), |
41 object_path_(object_path), | 41 object_path_(object_path), |
42 object_is_registered_(false), | 42 object_is_registered_(false) { |
43 response_from_method_(NULL), | |
44 on_method_is_called_(false /* manual_reset */, | |
45 false /* initially_signaled */) { | |
46 } | 43 } |
47 | 44 |
48 ExportedObject::~ExportedObject() { | 45 ExportedObject::~ExportedObject() { |
49 DCHECK(!object_is_registered_); | 46 DCHECK(!object_is_registered_); |
50 } | 47 } |
51 | 48 |
52 bool ExportedObject::ExportMethodAndBlock( | 49 bool ExportedObject::ExportMethodAndBlock( |
53 const std::string& interface_name, | 50 const std::string& interface_name, |
54 const std::string& method_name, | 51 const std::string& method_name, |
55 MethodCallCallback method_call_callback) { | 52 MethodCallCallback method_call_callback) { |
(...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
211 const std::string absolute_method_name = GetAbsoluteMethodName( | 208 const std::string absolute_method_name = GetAbsoluteMethodName( |
212 interface, member); | 209 interface, member); |
213 MethodTable::const_iterator iter = method_table_.find(absolute_method_name); | 210 MethodTable::const_iterator iter = method_table_.find(absolute_method_name); |
214 if (iter == method_table_.end()) { | 211 if (iter == method_table_.end()) { |
215 // Don't know about the method. | 212 // Don't know about the method. |
216 LOG(WARNING) << "Unknown method: " << method_call->ToString(); | 213 LOG(WARNING) << "Unknown method: " << method_call->ToString(); |
217 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; | 214 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
218 } | 215 } |
219 | 216 |
220 const base::TimeTicks start_time = base::TimeTicks::Now(); | 217 const base::TimeTicks start_time = base::TimeTicks::Now(); |
221 Response* response = NULL; | |
222 if (bus_->HasDBusThread()) { | 218 if (bus_->HasDBusThread()) { |
223 response_from_method_ = NULL; | |
224 // Post a task to run the method in the origin thread. | 219 // Post a task to run the method in the origin thread. |
225 bus_->PostTaskToOriginThread(FROM_HERE, | 220 bus_->PostTaskToOriginThread(FROM_HERE, |
226 base::Bind(&ExportedObject::RunMethod, | 221 base::Bind(&ExportedObject::RunMethod, |
227 this, | 222 this, |
228 iter->second, | 223 iter->second, |
229 method_call.get())); | 224 method_call.release(), |
230 // Wait until the method call is done. Blocking is not desirable but we | 225 start_time)); |
231 // should return the response to the dbus-daemon in the function, so we | |
232 // don't have a choice. We wait in the D-Bus thread, so it should be ok. | |
233 { | |
234 // We need a timeout here in case the method gets stuck. | |
235 const int kTimeoutSecs = 10; | |
236 const base::TimeDelta timeout( | |
237 base::TimeDelta::FromSeconds(kTimeoutSecs)); | |
238 | |
239 const bool signaled = on_method_is_called_.TimedWait(timeout); | |
240 // Method not called is a fatal error. The method is likely stuck | |
241 // infinitely in the origin thread. No way to stop it from here. | |
242 CHECK(signaled) << "Method " << absolute_method_name << " not called"; | |
243 } | |
244 response = response_from_method_; | |
245 } else { | 226 } else { |
246 // If the D-Bus thread is not used, just call the method directly. We | 227 // If the D-Bus thread is not used, just call the method directly. We |
247 // don't need the complicated logic to wait for the method call to be | 228 // don't need the complicated logic to wait for the method call to be |
248 // complete. | 229 // complete. |
249 response = iter->second.Run(method_call.get()); | 230 // |response| will be deleted in OnMethodCompleted(). |
| 231 Response* response = iter->second.Run(method_call.get()); |
| 232 OnMethodCompleted(method_call.release(), response, start_time); |
250 } | 233 } |
| 234 |
| 235 // It's valid to say HANDLED here, and send a method response at a later |
| 236 // time from OnMethodCompleted() asynchronously. |
| 237 return DBUS_HANDLER_RESULT_HANDLED; |
| 238 } |
| 239 |
| 240 void ExportedObject::RunMethod(MethodCallCallback method_call_callback, |
| 241 MethodCall* method_call, |
| 242 base::TimeTicks start_time) { |
| 243 bus_->AssertOnOriginThread(); |
| 244 |
| 245 Response* response = method_call_callback.Run(method_call); |
| 246 bus_->PostTaskToDBusThread(FROM_HERE, |
| 247 base::Bind(&ExportedObject::OnMethodCompleted, |
| 248 this, |
| 249 method_call, |
| 250 response, |
| 251 start_time)); |
| 252 } |
| 253 |
| 254 void ExportedObject::OnMethodCompleted(MethodCall* method_call, |
| 255 Response* response, |
| 256 base::TimeTicks start_time) { |
| 257 bus_->AssertOnDBusThread(); |
| 258 scoped_ptr<MethodCall> method_call_deleter(method_call); |
| 259 scoped_ptr<Response> response_deleter(response); |
| 260 |
251 // Record if the method call is successful, or not. 1 if successful. | 261 // Record if the method call is successful, or not. 1 if successful. |
252 UMA_HISTOGRAM_ENUMERATION("DBus.ExportedMethodHandleSuccess", | 262 UMA_HISTOGRAM_ENUMERATION("DBus.ExportedMethodHandleSuccess", |
253 response ? 1 : 0, | 263 response ? 1 : 0, |
254 kSuccessRatioHistogramMaxValue); | 264 kSuccessRatioHistogramMaxValue); |
255 | 265 |
| 266 // Check if the bus is still connected. If the method takes long to |
| 267 // complete, the bus may be shut down meanwhile. |
| 268 if (!bus_->is_connected()) |
| 269 return; |
| 270 |
256 if (!response) { | 271 if (!response) { |
257 // Something bad happened in the method call. | 272 // Something bad happened in the method call. |
258 scoped_ptr<dbus::ErrorResponse> error_response( | 273 scoped_ptr<dbus::ErrorResponse> error_response( |
259 ErrorResponse::FromMethodCall(method_call.get(), | 274 ErrorResponse::FromMethodCall( |
260 DBUS_ERROR_FAILED, | 275 method_call, |
261 "error occurred in " + member)); | 276 DBUS_ERROR_FAILED, |
262 dbus_connection_send(connection, error_response->raw_message(), NULL); | 277 "error occurred in " + method_call->GetMember())); |
263 return DBUS_HANDLER_RESULT_HANDLED; | 278 bus_->Send(error_response->raw_message(), NULL); |
| 279 return; |
264 } | 280 } |
265 | 281 |
266 // The method call was successful. | 282 // The method call was successful. |
267 dbus_connection_send(connection, response->raw_message(), NULL); | 283 bus_->Send(response->raw_message(), NULL); |
268 delete response; | 284 |
269 // Record time spent to handle the the method call. Don't include failures. | 285 // Record time spent to handle the the method call. Don't include failures. |
270 UMA_HISTOGRAM_TIMES("DBus.ExportedMethodHandleTime", | 286 UMA_HISTOGRAM_TIMES("DBus.ExportedMethodHandleTime", |
271 base::TimeTicks::Now() - start_time); | 287 base::TimeTicks::Now() - start_time); |
272 | |
273 return DBUS_HANDLER_RESULT_HANDLED; | |
274 } | |
275 | |
276 void ExportedObject::RunMethod(MethodCallCallback method_call_callback, | |
277 MethodCall* method_call) { | |
278 bus_->AssertOnOriginThread(); | |
279 | |
280 response_from_method_ = method_call_callback.Run(method_call); | |
281 on_method_is_called_.Signal(); | |
282 } | 288 } |
283 | 289 |
284 void ExportedObject::OnUnregistered(DBusConnection* connection) { | 290 void ExportedObject::OnUnregistered(DBusConnection* connection) { |
285 } | 291 } |
286 | 292 |
287 DBusHandlerResult ExportedObject::HandleMessageThunk( | 293 DBusHandlerResult ExportedObject::HandleMessageThunk( |
288 DBusConnection* connection, | 294 DBusConnection* connection, |
289 DBusMessage* raw_message, | 295 DBusMessage* raw_message, |
290 void* user_data) { | 296 void* user_data) { |
291 ExportedObject* self = reinterpret_cast<ExportedObject*>(user_data); | 297 ExportedObject* self = reinterpret_cast<ExportedObject*>(user_data); |
292 return self->HandleMessage(connection, raw_message); | 298 return self->HandleMessage(connection, raw_message); |
293 } | 299 } |
294 | 300 |
295 void ExportedObject::OnUnregisteredThunk(DBusConnection *connection, | 301 void ExportedObject::OnUnregisteredThunk(DBusConnection *connection, |
296 void* user_data) { | 302 void* user_data) { |
297 ExportedObject* self = reinterpret_cast<ExportedObject*>(user_data); | 303 ExportedObject* self = reinterpret_cast<ExportedObject*>(user_data); |
298 return self->OnUnregistered(connection); | 304 return self->OnUnregistered(connection); |
299 } | 305 } |
300 | 306 |
301 } // namespace dbus | 307 } // namespace dbus |
OLD | NEW |