summaryrefslogtreecommitdiff
path: root/hook_table.h
blob: 4ee477e398eecaeacb30fbd7ccfc54b34235e1f1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//
// Copyright (C) 2012 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#ifndef SHILL_HOOK_TABLE_H_
#define SHILL_HOOK_TABLE_H_

// HookTable provides a facility for starting a set of generic actions and
// reporting for their completion.  For example, on shutdown, each service gets
// disconnected.  A disconnect action may be instantaneous or it may require
// some time to complete.  Users of this facility use the Add() function to
// provide a closure for starting an action. Users report the completion of an
// action.  When an event occurs, the Run() function is called, which starts
// each action and sets a timer.  Upon completion or timeout, Run() calls a
// user-supplied callback to notify the caller of the state of actions.
//
// Usage example.  Add an action to a hook table like this:
//
//   HookTable hook_table_(&event_dispatcher);
//   Closure start_callback = Bind(&MyService::Disconnect, &my_service);
//   hook_table_.Add("MyService", start_callback);
//
// The code that catches an event runs the actions of the hook table like this:
//
//   ResultCallback done_callback = Bind(Manager::OnDisconnect, &manager);
//   hook_table_.Run(kTimeout, done_callback);
//
// When |my_service| has completed its disconnect process,
// Manager::OnDisconnect() gets called with Error::kSuccess.  If |my_service|
// does not finish its disconnect processing before kTimeout, then it gets
// called with kOperationTimeout.

#include <map>
#include <string>

#include <base/cancelable_callback.h>
#include <base/macros.h>
#include <gtest/gtest_prod.h>

#include "shill/callbacks.h"

namespace shill {
class EventDispatcher;
class Error;

class HookTable {
 public:
  explicit HookTable(EventDispatcher* event_dispatcher);
  ~HookTable();

  // Adds a closure to the hook table.  |name| should be unique; otherwise, a
  // previous closure by the same name will be replaced.  |start| will be called
  // when Run() is called.
  void Add(const std::string& name, const base::Closure& start);

  // Users call this function to report the completion of an action |name|.
  void ActionComplete(const std::string& name);

  // Removes the action associated with |name| from the hook table.  If |name|
  // does not exist, the hook table is unchanged.
  void Remove(const std::string& name);

  // Runs the actions that have been added to the HookTable via Add().  It
  // starts a timer for completion in |timeout_ms|.  If all actions complete
  // successfully within the timeout period, |done| is called with a value of
  // Error::kSuccess.  Otherwise, it is called with Error::kOperationTimeout.
  void Run(int timeout_ms, const ResultCallback& done);

  bool IsEmpty() const { return hook_table_.empty(); }

 private:
  friend class HookTableTest;

  // For each action, there is a |start| callback which is stored in this
  // structure.
  struct HookAction {
    explicit HookAction(const base::Closure& start_callback)
        : start_callback(start_callback),
          started(false),
          completed(false) {}
    const base::Closure start_callback;
    bool started;
    bool completed;
  };

  // Each action is stored in this table.  The key is |name| passed to Add().
  typedef std::map<std::string, HookAction> HookTableMap;

  // Returns true if all started actions have completed; false otherwise.  If no
  // actions have started, returns true.
  bool AllActionsComplete() const;

  // This function runs if all the actions do not complete before the timeout
  // period.  It invokes the user-supplied callback to Run() with an error value
  // kOperationTimeout.
  void ActionsTimedOut();

  // Each action is stored in this table.
  HookTableMap hook_table_;

  // This is the user-supplied callback to Run().
  ResultCallback done_callback_;

  // This callback is created in Run() and is queued to the event dispatcher to
  // run after a timeout period.  If all the actions complete before the
  // timeout, then this callback is canceled.
  base::CancelableClosure timeout_callback_;

  // Used for setting a timeout action to run in case all the actions do not
  // complete in time.
  EventDispatcher* const event_dispatcher_;

  DISALLOW_COPY_AND_ASSIGN(HookTable);
};

}  // namespace shill

#endif  // SHILL_HOOK_TABLE_H_