1 // 2 // Copyright (C) 2012 The Android Open Source Project 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 // 16 17 #include "shill/hook_table.h" 18 19 #include <list> 20 #include <string> 21 22 #include <base/bind.h> 23 #include <base/callback.h> 24 #include <base/cancelable_callback.h> 25 26 #include "shill/error.h" 27 #include "shill/event_dispatcher.h" 28 #include "shill/logging.h" 29 30 using base::Bind; 31 using base::Closure; 32 using base::Unretained; 33 using std::list; 34 using std::string; 35 36 namespace shill { 37 38 namespace Logging { 39 static auto kModuleLogScope = ScopeLogger::kManager; 40 static string ObjectID(const HookTable* h) { return "(hook_table)"; } 41 } 42 43 HookTable::HookTable(EventDispatcher* event_dispatcher) 44 : event_dispatcher_(event_dispatcher) {} 45 46 void HookTable::Add(const string& name, const Closure& start_callback) { 47 SLOG(this, 2) << __func__ << ": " << name; 48 Remove(name); 49 hook_table_.emplace(name, HookAction(start_callback)); 50 } 51 52 HookTable::~HookTable() { 53 timeout_callback_.Cancel(); 54 } 55 56 void HookTable::Remove(const std::string& name) { 57 SLOG(this, 2) << __func__ << ": " << name; 58 hook_table_.erase(name); 59 } 60 61 void HookTable::ActionComplete(const std::string& name) { 62 SLOG(this, 2) << __func__ << ": " << name; 63 HookTableMap::iterator it = hook_table_.find(name); 64 if (it != hook_table_.end()) { 65 HookAction* action = &it->second; 66 if (action->started && !action->completed) { 67 action->completed = true; 68 } 69 } 70 if (AllActionsComplete() && !done_callback_.is_null()) { 71 timeout_callback_.Cancel(); 72 done_callback_.Run(Error(Error::kSuccess)); 73 done_callback_.Reset(); 74 } 75 } 76 77 void HookTable::Run(int timeout_ms, const ResultCallback& done) { 78 SLOG(this, 2) << __func__; 79 if (hook_table_.empty()) { 80 done.Run(Error(Error::kSuccess)); 81 return; 82 } 83 done_callback_ = done; 84 timeout_callback_.Reset(Bind(&HookTable::ActionsTimedOut, Unretained(this))); 85 event_dispatcher_->PostDelayedTask(timeout_callback_.callback(), timeout_ms); 86 87 // Mark all actions as having started before we execute any actions. 88 // Otherwise, if the first action completes inline, its call to 89 // ActionComplete() will cause the |done| callback to be invoked before the 90 // rest of the actions get started. 91 // 92 // An action that completes inline could call HookTable::Remove(), which 93 // modifies |hook_table_|. It is thus not safe to iterate through 94 // |hook_table_| to execute the actions. Instead, we keep a list of start 95 // callback of each action and iterate through that to invoke the callback. 96 list<Closure> action_start_callbacks; 97 for (auto& hook_entry : hook_table_) { 98 HookAction* action = &hook_entry.second; 99 action_start_callbacks.push_back(action->start_callback); 100 action->started = true; 101 action->completed = false; 102 } 103 // Now start the actions. 104 for (auto& callback : action_start_callbacks) { 105 callback.Run(); 106 } 107 } 108 109 bool HookTable::AllActionsComplete() const { 110 SLOG(this, 2) << __func__; 111 for (const auto& hook_entry : hook_table_) { 112 const HookAction& action = hook_entry.second; 113 if (action.started && !action.completed) { 114 return false; 115 } 116 } 117 return true; 118 } 119 120 void HookTable::ActionsTimedOut() { 121 done_callback_.Run(Error(Error::kOperationTimeout)); 122 done_callback_.Reset(); 123 } 124 125 } // namespace shill 126