Commit f34dabde authored by vlaviano@chromium.org's avatar vlaviano@chromium.org

chrome: dbus: support asynchronous method replies

BUG=chromium-os:23241
TEST=Unit tests and manual testing on device.

Change-Id: I4d665897687030f4ab2379e4f6ddb9b3ebe02af4


Review URL: http://codereview.chromium.org/8637002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@111479 0039d316-1c4b-4281-b951-d872f2087c98
parent 89539dec
...@@ -224,8 +224,9 @@ bool ProxyResolutionServiceProvider::OnOriginThread() { ...@@ -224,8 +224,9 @@ bool ProxyResolutionServiceProvider::OnOriginThread() {
return base::PlatformThread::CurrentId() == origin_thread_id_; return base::PlatformThread::CurrentId() == origin_thread_id_;
} }
dbus::Response* ProxyResolutionServiceProvider::ResolveProxyHandler( void ProxyResolutionServiceProvider::ResolveProxyHandler(
dbus::MethodCall* method_call) { dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) {
DCHECK(OnOriginThread()); DCHECK(OnOriginThread());
VLOG(1) << "Handing method call: " << method_call->ToString(); VLOG(1) << "Handing method call: " << method_call->ToString();
// The method call should contain the three string parameters. // The method call should contain the three string parameters.
...@@ -237,7 +238,8 @@ dbus::Response* ProxyResolutionServiceProvider::ResolveProxyHandler( ...@@ -237,7 +238,8 @@ dbus::Response* ProxyResolutionServiceProvider::ResolveProxyHandler(
!reader.PopString(&signal_interface) || !reader.PopString(&signal_interface) ||
!reader.PopString(&signal_name)) { !reader.PopString(&signal_name)) {
LOG(ERROR) << "Unexpected method call: " << method_call->ToString(); LOG(ERROR) << "Unexpected method call: " << method_call->ToString();
return NULL; response_sender.Run(NULL);
return;
} }
resolver_->ResolveProxy(source_url, resolver_->ResolveProxy(source_url,
...@@ -248,18 +250,20 @@ dbus::Response* ProxyResolutionServiceProvider::ResolveProxyHandler( ...@@ -248,18 +250,20 @@ dbus::Response* ProxyResolutionServiceProvider::ResolveProxyHandler(
// Return an empty response for now. We'll send a signal once the // Return an empty response for now. We'll send a signal once the
// network proxy resolution is completed. // network proxy resolution is completed.
dbus::Response* response = dbus::Response::FromMethodCall(method_call); dbus::Response* response = dbus::Response::FromMethodCall(method_call);
return response; response_sender.Run(response);
} }
// static // static
dbus::Response* ProxyResolutionServiceProvider::CallResolveProxyHandler( void ProxyResolutionServiceProvider::CallResolveProxyHandler(
base::WeakPtr<ProxyResolutionServiceProvider> provider_weak_ptr, base::WeakPtr<ProxyResolutionServiceProvider> provider_weak_ptr,
dbus::MethodCall* method_call) { dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) {
if (!provider_weak_ptr) { if (!provider_weak_ptr) {
LOG(WARNING) << "Called after the object is deleted"; LOG(WARNING) << "Called after the object is deleted";
return NULL; response_sender.Run(NULL);
return;
} }
return provider_weak_ptr->ResolveProxyHandler(method_call); provider_weak_ptr->ResolveProxyHandler(method_call, response_sender);
} }
ProxyResolutionServiceProvider* ProxyResolutionServiceProvider::Create() { ProxyResolutionServiceProvider* ProxyResolutionServiceProvider::Create() {
......
...@@ -15,9 +15,9 @@ ...@@ -15,9 +15,9 @@
#include "base/synchronization/lock.h" #include "base/synchronization/lock.h"
#include "base/threading/platform_thread.h" #include "base/threading/platform_thread.h"
#include "chrome/browser/chromeos/dbus/cros_dbus_service.h" #include "chrome/browser/chromeos/dbus/cros_dbus_service.h"
#include "dbus/exported_object.h"
namespace dbus { namespace dbus {
class ExportedObject;
class MethodCall; class MethodCall;
class Response; class Response;
} }
...@@ -104,13 +104,15 @@ class ProxyResolutionServiceProvider ...@@ -104,13 +104,15 @@ class ProxyResolutionServiceProvider
// Callback to be invoked when ChromeOS clients send network proxy // Callback to be invoked when ChromeOS clients send network proxy
// resolution requests to the service running in chrome executable. // resolution requests to the service running in chrome executable.
// Called on UI thread from dbus request. // Called on UI thread from dbus request.
dbus::Response* ResolveProxyHandler(dbus::MethodCall* method_call); void ResolveProxyHandler(dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender);
// Calls ResolveProxyHandler() if weak_ptr is not NULL. Used to ensure a // Calls ResolveProxyHandler() if weak_ptr is not NULL. Used to ensure a
// safe shutdown. // safe shutdown.
static dbus::Response* CallResolveProxyHandler( static void CallResolveProxyHandler(
base::WeakPtr<ProxyResolutionServiceProvider> weak_ptr, base::WeakPtr<ProxyResolutionServiceProvider> weak_ptr,
dbus::MethodCall* method_call); dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender);
// Returns true if the current thread is on the origin thread. // Returns true if the current thread is on the origin thread.
bool OnOriginThread(); bool OnOriginThread();
......
...@@ -100,11 +100,15 @@ class ObjectProxy; ...@@ -100,11 +100,15 @@ class ObjectProxy;
// //
// Exporting a method: // Exporting a method:
// //
// Response* Echo(dbus::MethodCall* method_call) { // void Echo(dbus::MethodCall* method_call,
// dbus::ExportedObject::ResponseSender response_sender) {
// // Do something with method_call. // // Do something with method_call.
// Response* response = Response::FromMethodCall(method_call); // Response* response = Response::FromMethodCall(method_call);
// // Build response here. // // Build response here.
// return response; // // Can send an immediate response here to implement a synchronous service
// // or store the response_sender and send a response later to implement an
// // asynchronous service.
// response_sender.Run(response);
// } // }
// //
// void OnExported(const std::string& interface_name, // void OnExported(const std::string& interface_name,
......
...@@ -223,6 +223,24 @@ TEST_F(EndToEndAsyncTest, Timeout) { ...@@ -223,6 +223,24 @@ TEST_F(EndToEndAsyncTest, Timeout) {
ASSERT_EQ("", response_strings_[0]); ASSERT_EQ("", response_strings_[0]);
} }
// Tests calling a method that sends its reply asynchronously.
TEST_F(EndToEndAsyncTest, AsyncEcho) {
const char* kHello = "hello";
// Create the method call.
dbus::MethodCall method_call("org.chromium.TestInterface", "AsyncEcho");
dbus::MessageWriter writer(&method_call);
writer.AppendString(kHello);
// Call the method.
const int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT;
CallMethod(&method_call, timeout_ms);
// Check the response.
WaitForResponses(1);
EXPECT_EQ(kHello, response_strings_[0]);
}
TEST_F(EndToEndAsyncTest, NonexistentMethod) { TEST_F(EndToEndAsyncTest, NonexistentMethod) {
dbus::MethodCall method_call("org.chromium.TestInterface", "Nonexistent"); dbus::MethodCall method_call("org.chromium.TestInterface", "Nonexistent");
......
...@@ -224,12 +224,13 @@ DBusHandlerResult ExportedObject::HandleMessage( ...@@ -224,12 +224,13 @@ DBusHandlerResult ExportedObject::HandleMessage(
method_call.release(), method_call.release(),
start_time)); start_time));
} else { } else {
// If the D-Bus thread is not used, just call the method directly. We // If the D-Bus thread is not used, just call the method directly.
// don't need the complicated logic to wait for the method call to be MethodCall* released_method_call = method_call.release();
// complete. iter->second.Run(released_method_call,
// |response| will be deleted in OnMethodCompleted(). base::Bind(&ExportedObject::SendResponse,
Response* response = iter->second.Run(method_call.get()); this,
OnMethodCompleted(method_call.release(), response, start_time); start_time,
released_method_call));
} }
// It's valid to say HANDLED here, and send a method response at a later // It's valid to say HANDLED here, and send a method response at a later
...@@ -241,14 +242,27 @@ void ExportedObject::RunMethod(MethodCallCallback method_call_callback, ...@@ -241,14 +242,27 @@ void ExportedObject::RunMethod(MethodCallCallback method_call_callback,
MethodCall* method_call, MethodCall* method_call,
base::TimeTicks start_time) { base::TimeTicks start_time) {
bus_->AssertOnOriginThread(); bus_->AssertOnOriginThread();
method_call_callback.Run(method_call,
base::Bind(&ExportedObject::SendResponse,
this,
start_time,
method_call));
}
Response* response = method_call_callback.Run(method_call); void ExportedObject::SendResponse(base::TimeTicks start_time,
bus_->PostTaskToDBusThread(FROM_HERE, MethodCall* method_call,
base::Bind(&ExportedObject::OnMethodCompleted, Response* response) {
this, DCHECK(method_call);
method_call, if (bus_->HasDBusThread()) {
response, bus_->PostTaskToDBusThread(FROM_HERE,
start_time)); base::Bind(&ExportedObject::OnMethodCompleted,
this,
method_call,
response,
start_time));
} else {
OnMethodCompleted(method_call, response, start_time);
}
} }
void ExportedObject::OnMethodCompleted(MethodCall* method_call, void ExportedObject::OnMethodCompleted(MethodCall* method_call,
......
...@@ -38,9 +38,15 @@ class ExportedObject : public base::RefCountedThreadSafe<ExportedObject> { ...@@ -38,9 +38,15 @@ class ExportedObject : public base::RefCountedThreadSafe<ExportedObject> {
const std::string& service_name, const std::string& service_name,
const std::string& object_path); const std::string& object_path);
// Called to send a response from an exported method. Response* is the
// response message. Callers should pass a NULL Response* in the event
// of an error that prevents the sending of a response.
typedef base::Callback<void (Response*)> ResponseSender;
// Called when an exported method is called. MethodCall* is the request // Called when an exported method is called. MethodCall* is the request
// message. // message. ResponseSender is the callback that should be used to send a
typedef base::Callback<Response* (MethodCall*)> MethodCallCallback; // response.
typedef base::Callback<void (MethodCall*, ResponseSender)> MethodCallCallback;
// Called when method exporting is done. // Called when method exporting is done.
// Parameters: // Parameters:
...@@ -124,7 +130,14 @@ class ExportedObject : public base::RefCountedThreadSafe<ExportedObject> { ...@@ -124,7 +130,14 @@ class ExportedObject : public base::RefCountedThreadSafe<ExportedObject> {
MethodCall* method_call, MethodCall* method_call,
base::TimeTicks start_time); base::TimeTicks start_time);
// Called on completion of the method run from RunMethod(). // Callback invoked by service provider to send a response to a method call.
// Can be called immediately from a MethodCallCallback to implement a
// synchronous service or called later to implement an asynchronous service.
void SendResponse(base::TimeTicks start_time,
MethodCall* method_call,
Response* response);
// Called on completion of the method run from SendResponse().
// Takes ownership of |method_call| and |response|. // Takes ownership of |method_call| and |response|.
void OnMethodCompleted(MethodCall* method_call, void OnMethodCompleted(MethodCall* method_call,
Response* response, Response* response,
......
...@@ -13,8 +13,8 @@ ...@@ -13,8 +13,8 @@
namespace dbus { namespace dbus {
// Echo, SlowEcho, BrokenMethod. // Echo, SlowEcho, AsyncEcho, BrokenMethod.
const int TestService::kNumMethodsToExport = 3; const int TestService::kNumMethodsToExport = 4;
TestService::Options::Options() { TestService::Options::Options() {
} }
...@@ -146,6 +146,15 @@ void TestService::Run(MessageLoop* message_loop) { ...@@ -146,6 +146,15 @@ void TestService::Run(MessageLoop* message_loop) {
base::Unretained(this))); base::Unretained(this)));
++num_methods; ++num_methods;
exported_object_->ExportMethod(
"org.chromium.TestInterface",
"AsyncEcho",
base::Bind(&TestService::AsyncEcho,
base::Unretained(this)),
base::Bind(&TestService::OnExported,
base::Unretained(this)));
++num_methods;
exported_object_->ExportMethod( exported_object_->ExportMethod(
"org.chromium.TestInterface", "org.chromium.TestInterface",
"BrokenMethod", "BrokenMethod",
...@@ -163,25 +172,44 @@ void TestService::Run(MessageLoop* message_loop) { ...@@ -163,25 +172,44 @@ void TestService::Run(MessageLoop* message_loop) {
message_loop->Run(); message_loop->Run();
} }
Response* TestService::Echo(MethodCall* method_call) { void TestService::Echo(MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) {
MessageReader reader(method_call); MessageReader reader(method_call);
std::string text_message; std::string text_message;
if (!reader.PopString(&text_message)) if (!reader.PopString(&text_message)) {
return NULL; response_sender.Run(NULL);
return;
}
Response* response = Response::FromMethodCall(method_call); Response* response = Response::FromMethodCall(method_call);
MessageWriter writer(response); MessageWriter writer(response);
writer.AppendString(text_message); writer.AppendString(text_message);
return response; response_sender.Run(response);
} }
Response* TestService::SlowEcho(MethodCall* method_call) { void TestService::SlowEcho(
MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) {
base::PlatformThread::Sleep(TestTimeouts::tiny_timeout_ms()); base::PlatformThread::Sleep(TestTimeouts::tiny_timeout_ms());
return Echo(method_call); Echo(method_call, response_sender);
}
void TestService::AsyncEcho(
MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) {
// Schedule a call to Echo() to send an asynchronous response after we return.
message_loop()->PostDelayedTask(FROM_HERE,
base::Bind(&TestService::Echo,
base::Unretained(this),
method_call,
response_sender),
TestTimeouts::tiny_timeout_ms());
} }
Response* TestService::BrokenMethod(MethodCall* method_call) { void TestService::BrokenMethod(
return NULL; MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) {
response_sender.Run(NULL);
} }
} // namespace dbus } // namespace dbus
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/threading/thread.h" #include "base/threading/thread.h"
#include "base/synchronization/waitable_event.h" #include "base/synchronization/waitable_event.h"
#include "dbus/exported_object.h"
namespace base { namespace base {
class MessageLoopProxy; class MessageLoopProxy;
...@@ -18,7 +19,6 @@ class MessageLoopProxy; ...@@ -18,7 +19,6 @@ class MessageLoopProxy;
namespace dbus { namespace dbus {
class Bus; class Bus;
class ExportedObject;
class MethodCall; class MethodCall;
class Response; class Response;
...@@ -89,14 +89,22 @@ class TestService : public base::Thread { ...@@ -89,14 +89,22 @@ class TestService : public base::Thread {
// //
// Echos the text message received from the method call. // Echos the text message received from the method call.
Response* Echo(MethodCall* method_call); void Echo(MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender);
// Echos the text message received from the method call, but sleeps for // Echos the text message received from the method call, but sleeps for
// TestTimeouts::tiny_timeout_ms() before returning the response. // TestTimeouts::tiny_timeout_ms() before returning the response.
Response* SlowEcho(MethodCall* method_call); void SlowEcho(MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender);
// Echos the text message received from the method call, but sends its
// response asynchronously after this callback has returned.
void AsyncEcho(MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender);
// Returns NULL, instead of a valid Response. // Returns NULL, instead of a valid Response.
Response* BrokenMethod(MethodCall* method_call); void BrokenMethod(MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender);
scoped_refptr<base::MessageLoopProxy> dbus_thread_message_loop_proxy_; scoped_refptr<base::MessageLoopProxy> dbus_thread_message_loop_proxy_;
base::WaitableEvent on_all_methods_exported_; base::WaitableEvent on_all_methods_exported_;
......
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