Christopher Wiley | 2ab1bec | 2014-04-11 11:04:49 -0700 | [diff] [blame] | 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 "buffet/async_event_sequencer.h" |
| 6 | |
| 7 | namespace buffet { |
| 8 | |
| 9 | namespace dbus_utils { |
| 10 | |
| 11 | AsyncEventSequencer::AsyncEventSequencer() { } |
| 12 | AsyncEventSequencer::~AsyncEventSequencer() { } |
| 13 | |
| 14 | AsyncEventSequencer::Handler AsyncEventSequencer::GetHandler( |
| 15 | const std::string& descriptive_message, bool failure_is_fatal) { |
| 16 | CHECK(!started_) << "Cannot create handlers after OnAllTasksCompletedCall()"; |
| 17 | int unique_registration_id = ++registration_counter_; |
| 18 | outstanding_registrations_.insert(unique_registration_id); |
| 19 | return base::Bind(&AsyncEventSequencer::HandleFinish, this, |
| 20 | unique_registration_id, descriptive_message, |
| 21 | failure_is_fatal); |
| 22 | } |
| 23 | |
| 24 | AsyncEventSequencer::ExportHandler AsyncEventSequencer::GetExportHandler( |
| 25 | const std::string& interface_name, const std::string& method_name, |
| 26 | const std::string& descriptive_message, bool failure_is_fatal) { |
| 27 | auto finish_handler = GetHandler(descriptive_message, failure_is_fatal); |
| 28 | return base::Bind(&AsyncEventSequencer::HandleDBusMethodExported, this, |
| 29 | finish_handler, |
| 30 | interface_name, |
| 31 | method_name); |
| 32 | } |
| 33 | |
| 34 | void AsyncEventSequencer::OnAllTasksCompletedCall( |
| 35 | std::vector<CompletionAction> actions) { |
| 36 | CHECK(!started_) << "OnAllTasksCompletedCall called twice!"; |
| 37 | started_ = true; |
| 38 | completion_actions_.assign(actions.begin(), actions.end()); |
| 39 | // All of our callbacks might have been called already. |
| 40 | PossiblyRunCompletionActions(); |
| 41 | } |
| 42 | |
Christopher Wiley | adb901d | 2014-05-07 09:58:45 -0700 | [diff] [blame] | 43 | namespace { |
| 44 | void IgnoreSuccess(const AsyncEventSequencer::CompletionTask& task, |
| 45 | bool /*success*/) { task.Run(); } |
| 46 | } // namespace |
| 47 | |
| 48 | AsyncEventSequencer::CompletionAction AsyncEventSequencer::WrapCompletionTask( |
| 49 | const CompletionTask& task) { |
| 50 | return base::Bind(&IgnoreSuccess, task); |
| 51 | } |
| 52 | |
Christopher Wiley | 2ab1bec | 2014-04-11 11:04:49 -0700 | [diff] [blame] | 53 | void AsyncEventSequencer::HandleFinish(int registration_number, |
| 54 | const std::string& error_message, |
| 55 | bool failure_is_fatal, bool success) { |
| 56 | RetireRegistration(registration_number); |
| 57 | CheckForFailure(failure_is_fatal, success, error_message); |
| 58 | PossiblyRunCompletionActions(); |
| 59 | } |
| 60 | |
| 61 | void AsyncEventSequencer::HandleDBusMethodExported( |
| 62 | const AsyncEventSequencer::Handler& finish_handler, |
| 63 | const std::string& expected_interface_name, |
| 64 | const std::string& expected_method_name, |
| 65 | const std::string& actual_interface_name, |
| 66 | const std::string& actual_method_name, bool success) { |
Alex Vakulenko | 3379706 | 2014-05-12 15:55:25 -0700 | [diff] [blame] | 67 | CHECK_EQ(expected_method_name, actual_method_name) |
Christopher Wiley | 2ab1bec | 2014-04-11 11:04:49 -0700 | [diff] [blame] | 68 | << "Exported DBus method '" << actual_method_name << "' " |
| 69 | << "but expected '" << expected_method_name << "'"; |
Alex Vakulenko | 3379706 | 2014-05-12 15:55:25 -0700 | [diff] [blame] | 70 | CHECK_EQ(expected_interface_name, actual_interface_name) |
Christopher Wiley | 2ab1bec | 2014-04-11 11:04:49 -0700 | [diff] [blame] | 71 | << "Exported method DBus interface '" << actual_interface_name << "' " |
| 72 | << "but expected '" << expected_interface_name << "'"; |
| 73 | finish_handler.Run(success); |
| 74 | } |
| 75 | |
| 76 | |
| 77 | void AsyncEventSequencer::RetireRegistration(int registration_number) { |
| 78 | const size_t handlers_retired = outstanding_registrations_.erase( |
| 79 | registration_number); |
Alex Vakulenko | 3379706 | 2014-05-12 15:55:25 -0700 | [diff] [blame] | 80 | CHECK_EQ(1, handlers_retired) |
Christopher Wiley | 2ab1bec | 2014-04-11 11:04:49 -0700 | [diff] [blame] | 81 | << "Tried to retire invalid handler " << registration_number << ")"; |
| 82 | } |
| 83 | |
| 84 | void AsyncEventSequencer::CheckForFailure(bool failure_is_fatal, bool success, |
| 85 | const std::string& error_message) { |
| 86 | if (failure_is_fatal) { |
| 87 | CHECK(success) << error_message; |
| 88 | } |
| 89 | if (!success) { |
| 90 | LOG(ERROR) << error_message; |
| 91 | had_failures_ = true; |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | void AsyncEventSequencer::PossiblyRunCompletionActions() { |
| 96 | if (!started_ || !outstanding_registrations_.empty()) { |
| 97 | // Don't run completion actions if we have any outstanding |
| 98 | // Handlers outstanding or if any more handlers might |
| 99 | // be scheduled in the future. |
| 100 | return; |
| 101 | } |
Alex Vakulenko | a0424dd | 2014-06-13 16:10:17 -0700 | [diff] [blame] | 102 | for (const auto& completion_action : completion_actions_) { |
Christopher Wiley | 2ab1bec | 2014-04-11 11:04:49 -0700 | [diff] [blame] | 103 | // Should this be put on the message loop or run directly? |
| 104 | completion_action.Run(!had_failures_); |
| 105 | } |
| 106 | // Discard our references to those actions. |
| 107 | completion_actions_.clear(); |
| 108 | } |
| 109 | |
| 110 | } // namespace dbus_utils |
| 111 | |
| 112 | } // namespace buffet |