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;
ObjectID(const HookTable * h)40 static string ObjectID(const HookTable* h) { return "(hook_table)"; }
41 }
42
HookTable(EventDispatcher * event_dispatcher)43 HookTable::HookTable(EventDispatcher* event_dispatcher)
44 : event_dispatcher_(event_dispatcher) {}
45
Add(const string & name,const Closure & start_callback)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
~HookTable()52 HookTable::~HookTable() {
53 timeout_callback_.Cancel();
54 }
55
Remove(const std::string & name)56 void HookTable::Remove(const std::string& name) {
57 SLOG(this, 2) << __func__ << ": " << name;
58 hook_table_.erase(name);
59 }
60
ActionComplete(const std::string & name)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
Run(int timeout_ms,const ResultCallback & done)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
AllActionsComplete() const109 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
ActionsTimedOut()120 void HookTable::ActionsTimedOut() {
121 done_callback_.Run(Error(Error::kOperationTimeout));
122 done_callback_.Reset();
123 }
124
125 } // namespace shill
126