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 
IsEmpty()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 {
HookActionHookAction89     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