Gary Morain | f80ef06 | 2012-05-16 14:57:04 -0700 | [diff] [blame^] | 1 | // Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #ifndef SHILL_HOOK_TABLE_H_ |
| 6 | #define SHILL_HOOK_TABLE_H_ |
| 7 | |
| 8 | // HookTable provides a facility for starting a set of generic actions and |
| 9 | // polling for their completion. For example, on shutdown, each service gets |
| 10 | // disconnected. A disconnect action may be instantaneous or it may require |
| 11 | // some time to complete. Users of this facility use the Add() function to |
| 12 | // provide a closure for starting an action and a callback for polling its |
| 13 | // completion. When an event occurs, the Run() function is called, which starts |
| 14 | // each action and polls for completion. Upon completion or timeout, Run() |
| 15 | // calls a user-supplied callback to notify the caller of the state of actions. |
| 16 | // |
| 17 | // Usage example. Add an action to a hook table like this: |
| 18 | // |
| 19 | // HookTable hook_table_(&event_dispatcher); |
| 20 | // Closure start_cb = Bind(&MyService::Disconnect, &my_service); |
| 21 | // Callback poll_cb = Bind(&MyService::IsConnected, &my_service); |
| 22 | // hook_table_.Add("MyService", start_cb, poll_cb); |
| 23 | // |
| 24 | // The code that catches an event runs the actions of the hook table like this: |
| 25 | // |
| 26 | // Callback<void(const Error &)> done_cb = |
| 27 | // Bind(Manager::OnDisconnect, &manager); |
| 28 | // hook_table_.Run(kTimeout, done_cb); |
| 29 | // |
| 30 | // When |my_service| has completed its disconnect process, |
| 31 | // Manager::OnDisconnect() gets called with Error::kSuccess. If |my_service| |
| 32 | // does not finish its disconnect processing before kTimeout, then it gets |
| 33 | // called with kOperationTimeout. |
| 34 | |
| 35 | #include <map> |
| 36 | #include <string> |
| 37 | |
| 38 | #include <base/basictypes.h> |
| 39 | #include <base/callback.h> |
| 40 | #include <gtest/gtest_prod.h> |
| 41 | |
| 42 | namespace shill { |
| 43 | class Error; |
| 44 | class EventDispatcher; |
| 45 | |
| 46 | class HookTable { |
| 47 | public: |
| 48 | explicit HookTable(EventDispatcher *event_dispatcher); |
| 49 | |
| 50 | // Adds a closure to the hook table. |name| should be unique; otherwise, a |
| 51 | // previous closure by the same name will NOT be replaced. |start| will be |
| 52 | // called when Run() is called. Run() will poll to see if the action has |
| 53 | // completed by calling |poll|, which should return true when the action has |
| 54 | // completed. |
| 55 | void Add(const std::string &name, const base::Closure &start, |
| 56 | const base::Callback<bool()> &poll); |
| 57 | |
| 58 | // Runs the actions that have been added to the HookTable via Add(). It polls |
| 59 | // for completion up to |timeout_seconds|. If all actions complete within the |
| 60 | // timeout period, |done| is called with a value of Error::kSuccess. |
| 61 | // Otherwise, it is called with Error::kOperationTimeout. |
| 62 | void Run(int timeout_seconds, |
| 63 | const base::Callback<void(const Error &)> &done); |
| 64 | |
| 65 | private: |
| 66 | FRIEND_TEST(HookTableTest, ActionTimesOut); |
| 67 | FRIEND_TEST(HookTableTest, DelayedAction); |
| 68 | FRIEND_TEST(HookTableTest, MultipleActionsAllSucceed); |
| 69 | FRIEND_TEST(HookTableTest, MultipleActionsAndOneTimesOut); |
| 70 | |
| 71 | // The |timeout_seconds| passed to Run() is divided into |kPollIterations|, |
| 72 | // polled once iteration. |
| 73 | static const int kPollIterations; |
| 74 | |
| 75 | // For each action, there is a |start| and a |poll| callback, which are stored |
| 76 | // in this structure. |
| 77 | struct HookCallbacks { |
| 78 | HookCallbacks(const base::Closure &s, const base::Callback<bool()> &p) |
| 79 | : start(s), poll(p) {} |
| 80 | const base::Closure start; |
| 81 | const base::Callback<bool()> poll; |
| 82 | }; |
| 83 | |
| 84 | // Each action is stored in this table. The key is |name| passed to Add(). |
| 85 | typedef std::map<std::string, HookCallbacks> HookTableMap; |
| 86 | |
| 87 | // Calls all the |poll| callbacks in |hook_table_|. If all of them return |
| 88 | // true, then |done| is called with Error::kSuccess. Otherwise, it queues |
| 89 | // itself in the |event_dispatcher_| to be called again later. It repeats |
| 90 | // this process up to |kPollIterations|, after which if an action still has |
| 91 | // not completed, |done| is called with Error::kOperationTimeout. |
| 92 | void PollActions(int timeout_seconds, |
| 93 | const base::Callback<void(const Error &)> &done); |
| 94 | |
| 95 | // Each action is stored in this table. |
| 96 | HookTableMap hook_table_; |
| 97 | |
| 98 | // Used for polling actions that do not complete immediately. |
| 99 | EventDispatcher *const event_dispatcher_; |
| 100 | |
| 101 | // Counts the number of polling attempts. |
| 102 | int iteration_counter_; |
| 103 | |
| 104 | DISALLOW_COPY_AND_ASSIGN(HookTable); |
| 105 | }; |
| 106 | |
| 107 | } // namespace shill |
| 108 | |
| 109 | #endif // SHILL_HOOK_TABLE_H_ |