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 #ifndef SHILL_HOOK_TABLE_H_
     18 #define SHILL_HOOK_TABLE_H_
     19 
     20 // HookTable provides a facility for starting a set of generic actions and
     21 // reporting for their completion.  For example, on shutdown, each service gets
     22 // disconnected.  A disconnect action may be instantaneous or it may require
     23 // some time to complete.  Users of this facility use the Add() function to
     24 // provide a closure for starting an action. Users report the completion of an
     25 // action.  When an event occurs, the Run() function is called, which starts
     26 // each action and sets a timer.  Upon completion or timeout, Run() calls a
     27 // user-supplied callback to notify the caller of the state of actions.
     28 //
     29 // Usage example.  Add an action to a hook table like this:
     30 //
     31 //   HookTable hook_table_(&event_dispatcher);
     32 //   Closure start_callback = Bind(&MyService::Disconnect, &my_service);
     33 //   hook_table_.Add("MyService", start_callback);
     34 //
     35 // The code that catches an event runs the actions of the hook table like this:
     36 //
     37 //   ResultCallback done_callback = Bind(Manager::OnDisconnect, &manager);
     38 //   hook_table_.Run(kTimeout, done_callback);
     39 //
     40 // When |my_service| has completed its disconnect process,
     41 // Manager::OnDisconnect() gets called with Error::kSuccess.  If |my_service|
     42 // does not finish its disconnect processing before kTimeout, then it gets
     43 // called with kOperationTimeout.
     44 
     45 #include <map>
     46 #include <string>
     47 
     48 #include <base/cancelable_callback.h>
     49 #include <base/macros.h>
     50 #include <gtest/gtest_prod.h>
     51 
     52 #include "shill/callbacks.h"
     53 
     54 namespace shill {
     55 class EventDispatcher;
     56 class Error;
     57 
     58 class HookTable {
     59  public:
     60   explicit HookTable(EventDispatcher* event_dispatcher);
     61   ~HookTable();
     62 
     63   // Adds a closure to the hook table.  |name| should be unique; otherwise, a
     64   // previous closure by the same name will be replaced.  |start| will be called
     65   // when Run() is called.
     66   void Add(const std::string& name, const base::Closure& start);
     67 
     68   // Users call this function to report the completion of an action |name|.
     69   void ActionComplete(const std::string& name);
     70 
     71   // Removes the action associated with |name| from the hook table.  If |name|
     72   // does not exist, the hook table is unchanged.
     73   void Remove(const std::string& name);
     74 
     75   // Runs the actions that have been added to the HookTable via Add().  It
     76   // starts a timer for completion in |timeout_ms|.  If all actions complete
     77   // successfully within the timeout period, |done| is called with a value of
     78   // Error::kSuccess.  Otherwise, it is called with Error::kOperationTimeout.
     79   void Run(int timeout_ms, const ResultCallback& done);
     80 
     81   bool IsEmpty() const { return hook_table_.empty(); }
     82 
     83  private:
     84   friend class HookTableTest;
     85 
     86   // For each action, there is a |start| callback which is stored in this
     87   // structure.
     88   struct HookAction {
     89     explicit HookAction(const base::Closure& start_callback)
     90         : start_callback(start_callback),
     91           started(false),
     92           completed(false) {}
     93     const base::Closure start_callback;
     94     bool started;
     95     bool completed;
     96   };
     97 
     98   // Each action is stored in this table.  The key is |name| passed to Add().
     99   typedef std::map<std::string, HookAction> HookTableMap;
    100 
    101   // Returns true if all started actions have completed; false otherwise.  If no
    102   // actions have started, returns true.
    103   bool AllActionsComplete() const;
    104 
    105   // This function runs if all the actions do not complete before the timeout
    106   // period.  It invokes the user-supplied callback to Run() with an error value
    107   // kOperationTimeout.
    108   void ActionsTimedOut();
    109 
    110   // Each action is stored in this table.
    111   HookTableMap hook_table_;
    112 
    113   // This is the user-supplied callback to Run().
    114   ResultCallback done_callback_;
    115 
    116   // This callback is created in Run() and is queued to the event dispatcher to
    117   // run after a timeout period.  If all the actions complete before the
    118   // timeout, then this callback is canceled.
    119   base::CancelableClosure timeout_callback_;
    120 
    121   // Used for setting a timeout action to run in case all the actions do not
    122   // complete in time.
    123   EventDispatcher* const event_dispatcher_;
    124 
    125   DISALLOW_COPY_AND_ASSIGN(HookTable);
    126 };
    127 
    128 }  // namespace shill
    129 
    130 #endif  // SHILL_HOOK_TABLE_H_
    131