blob: 237f13247d635031b50d25b3060b1882f4976c6d [file] [log] [blame]
// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SHILL_HOOK_TABLE_H_
#define SHILL_HOOK_TABLE_H_
// HookTable provides a facility for starting a set of generic actions and
// polling 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 and a callback for polling its
// completion. When an event occurs, the Run() function is called, which starts
// each action and polls for completion. 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_cb = Bind(&MyService::Disconnect, &my_service);
// Callback poll_cb = Bind(&MyService::IsConnected, &my_service);
// hook_table_.Add("MyService", start_cb, poll_cb);
//
// The code that catches an event runs the actions of the hook table like this:
//
// Callback<void(const Error &)> done_cb =
// Bind(Manager::OnDisconnect, &manager);
// hook_table_.Run(kTimeout, done_cb);
//
// 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/basictypes.h>
#include <base/callback.h>
#include <gtest/gtest_prod.h>
namespace shill {
class Error;
class EventDispatcher;
class HookTable {
public:
explicit HookTable(EventDispatcher *event_dispatcher);
// Adds a closure to the hook table. |name| should be unique; otherwise, a
// previous closure by the same name will NOT be replaced. |start| will be
// called when Run() is called. Run() will poll to see if the action has
// completed by calling |poll|, which should return true when the action has
// completed.
void Add(const std::string &name, const base::Closure &start,
const base::Callback<bool()> &poll);
// Runs the actions that have been added to the HookTable via Add(). It polls
// for completion up to |timeout_seconds|. If all actions complete within the
// timeout period, |done| is called with a value of Error::kSuccess.
// Otherwise, it is called with Error::kOperationTimeout.
void Run(int timeout_seconds,
const base::Callback<void(const Error &)> &done);
private:
FRIEND_TEST(HookTableTest, ActionTimesOut);
FRIEND_TEST(HookTableTest, DelayedAction);
FRIEND_TEST(HookTableTest, MultipleActionsAllSucceed);
FRIEND_TEST(HookTableTest, MultipleActionsAndOneTimesOut);
// The |timeout_seconds| passed to Run() is divided into |kPollIterations|,
// polled once iteration.
static const int kPollIterations;
// For each action, there is a |start| and a |poll| callback, which are stored
// in this structure.
struct HookCallbacks {
HookCallbacks(const base::Closure &s, const base::Callback<bool()> &p)
: start(s), poll(p) {}
const base::Closure start;
const base::Callback<bool()> poll;
};
// Each action is stored in this table. The key is |name| passed to Add().
typedef std::map<std::string, HookCallbacks> HookTableMap;
// Calls all the |poll| callbacks in |hook_table_|. If all of them return
// true, then |done| is called with Error::kSuccess. Otherwise, it queues
// itself in the |event_dispatcher_| to be called again later. It repeats
// this process up to |kPollIterations|, after which if an action still has
// not completed, |done| is called with Error::kOperationTimeout.
void PollActions(int timeout_seconds,
const base::Callback<void(const Error &)> &done);
// Each action is stored in this table.
HookTableMap hook_table_;
// Used for polling actions that do not complete immediately.
EventDispatcher *const event_dispatcher_;
// Counts the number of polling attempts.
int iteration_counter_;
DISALLOW_COPY_AND_ASSIGN(HookTable);
};
} // namespace shill
#endif // SHILL_HOOK_TABLE_H_