blob: fff52b1485b60629bbce86ecf542e9a1665fee11 [file] [log] [blame]
// 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
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
// 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 {
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 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(); }
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),
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().
using HookTableMap = std::map<std::string, HookAction>;
// 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_;
} // namespace shill