1 // Copyright 2014 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <brillo/dbus/async_event_sequencer.h>
6 
7 #include <base/bind.h>
8 #include <base/callback.h>
9 
10 namespace brillo {
11 
12 namespace dbus_utils {
13 
AsyncEventSequencer()14 AsyncEventSequencer::AsyncEventSequencer() {
15 }
~AsyncEventSequencer()16 AsyncEventSequencer::~AsyncEventSequencer() {
17 }
18 
GetHandler(const std::string & descriptive_message,bool failure_is_fatal)19 AsyncEventSequencer::Handler AsyncEventSequencer::GetHandler(
20     const std::string& descriptive_message,
21     bool failure_is_fatal) {
22   CHECK(!started_) << "Cannot create handlers after OnAllTasksCompletedCall()";
23   int unique_registration_id = ++registration_counter_;
24   outstanding_registrations_.insert(unique_registration_id);
25   return base::Bind(&AsyncEventSequencer::HandleFinish,
26                     this,
27                     unique_registration_id,
28                     descriptive_message,
29                     failure_is_fatal);
30 }
31 
GetExportHandler(const std::string & interface_name,const std::string & method_name,const std::string & descriptive_message,bool failure_is_fatal)32 AsyncEventSequencer::ExportHandler AsyncEventSequencer::GetExportHandler(
33     const std::string& interface_name,
34     const std::string& method_name,
35     const std::string& descriptive_message,
36     bool failure_is_fatal) {
37   auto finish_handler = GetHandler(descriptive_message, failure_is_fatal);
38   return base::Bind(&AsyncEventSequencer::HandleDBusMethodExported,
39                     this,
40                     finish_handler,
41                     interface_name,
42                     method_name);
43 }
44 
OnAllTasksCompletedCall(std::vector<CompletionAction> actions)45 void AsyncEventSequencer::OnAllTasksCompletedCall(
46     std::vector<CompletionAction> actions) {
47   CHECK(!started_) << "OnAllTasksCompletedCall called twice!";
48   started_ = true;
49   completion_actions_.assign(actions.begin(), actions.end());
50   // All of our callbacks might have been called already.
51   PossiblyRunCompletionActions();
52 }
53 
54 namespace {
IgnoreSuccess(const AsyncEventSequencer::CompletionTask & task,bool)55 void IgnoreSuccess(const AsyncEventSequencer::CompletionTask& task,
56                    bool /*success*/) {
57   task.Run();
58 }
DoNothing(bool)59 void DoNothing(bool /* success */) {
60 }
61 }  // namespace
62 
WrapCompletionTask(const CompletionTask & task)63 AsyncEventSequencer::CompletionAction AsyncEventSequencer::WrapCompletionTask(
64     const CompletionTask& task) {
65   return base::Bind(&IgnoreSuccess, task);
66 }
67 
68 AsyncEventSequencer::CompletionAction
GetDefaultCompletionAction()69 AsyncEventSequencer::GetDefaultCompletionAction() {
70   return base::Bind(&DoNothing);
71 }
72 
HandleFinish(int registration_number,const std::string & error_message,bool failure_is_fatal,bool success)73 void AsyncEventSequencer::HandleFinish(int registration_number,
74                                        const std::string& error_message,
75                                        bool failure_is_fatal,
76                                        bool success) {
77   RetireRegistration(registration_number);
78   CheckForFailure(failure_is_fatal, success, error_message);
79   PossiblyRunCompletionActions();
80 }
81 
HandleDBusMethodExported(const AsyncEventSequencer::Handler & finish_handler,const std::string & expected_interface_name,const std::string & expected_method_name,const std::string & actual_interface_name,const std::string & actual_method_name,bool success)82 void AsyncEventSequencer::HandleDBusMethodExported(
83     const AsyncEventSequencer::Handler& finish_handler,
84     const std::string& expected_interface_name,
85     const std::string& expected_method_name,
86     const std::string& actual_interface_name,
87     const std::string& actual_method_name,
88     bool success) {
89   CHECK_EQ(expected_method_name, actual_method_name)
90       << "Exported DBus method '" << actual_method_name << "' "
91       << "but expected '" << expected_method_name << "'";
92   CHECK_EQ(expected_interface_name, actual_interface_name)
93       << "Exported method DBus interface '" << actual_interface_name << "' "
94       << "but expected '" << expected_interface_name << "'";
95   finish_handler.Run(success);
96 }
97 
RetireRegistration(int registration_number)98 void AsyncEventSequencer::RetireRegistration(int registration_number) {
99   const size_t handlers_retired =
100       outstanding_registrations_.erase(registration_number);
101   CHECK_EQ(1U, handlers_retired) << "Tried to retire invalid handler "
102                                  << registration_number << ")";
103 }
104 
CheckForFailure(bool failure_is_fatal,bool success,const std::string & error_message)105 void AsyncEventSequencer::CheckForFailure(bool failure_is_fatal,
106                                           bool success,
107                                           const std::string& error_message) {
108   if (failure_is_fatal) {
109     CHECK(success) << error_message;
110   }
111   if (!success) {
112     LOG(ERROR) << error_message;
113     had_failures_ = true;
114   }
115 }
116 
PossiblyRunCompletionActions()117 void AsyncEventSequencer::PossiblyRunCompletionActions() {
118   if (!started_ || !outstanding_registrations_.empty()) {
119     // Don't run completion actions if we have any outstanding
120     // Handlers outstanding or if any more handlers might
121     // be scheduled in the future.
122     return;
123   }
124   for (const auto& completion_action : completion_actions_) {
125     // Should this be put on the message loop or run directly?
126     completion_action.Run(!had_failures_);
127   }
128   // Discard our references to those actions.
129   completion_actions_.clear();
130 }
131 
132 }  // namespace dbus_utils
133 
134 }  // namespace brillo
135