Home | History | Annotate | Download | only in shill
      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