shill: HookTable collects async actions to be executed later
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
HookTable use the Add() function to provide a closure for starting
an action and a callback for pollings 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.
BUG=chromium-os:22408
TEST=new unittests; ran all unittests.
Change-Id: Ia42816a2a2b3c252108665ec64bc3c68f886463b
Reviewed-on: https://gerrit.chromium.org/gerrit/22862
Commit-Ready: Gary Morain <gmorain@chromium.org>
Reviewed-by: Gary Morain <gmorain@chromium.org>
Tested-by: Gary Morain <gmorain@chromium.org>
diff --git a/hook_table.cc b/hook_table.cc
new file mode 100644
index 0000000..53dbc29
--- /dev/null
+++ b/hook_table.cc
@@ -0,0 +1,78 @@
+// 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.
+
+#include "shill/hook_table.h"
+
+#include <string>
+
+#include <base/bind.h>
+#include <base/callback.h>
+
+#include "shill/error.h"
+#include "shill/event_dispatcher.h"
+
+using base::Bind;
+using base::Closure;
+using base::ConstRef;
+using base::Unretained;
+using std::string;
+
+namespace shill {
+
+const int HookTable::kPollIterations = 10;
+
+HookTable::HookTable(EventDispatcher *event_dispatcher)
+ : event_dispatcher_(event_dispatcher),
+ iteration_counter_(0) {}
+
+void HookTable::Add(const string &name, const base::Closure &start,
+ const base::Callback<bool()> &poll) {
+ hook_table_.insert(
+ HookTableMap::value_type(name, HookCallbacks(start, poll)));
+}
+
+void HookTable::Run(int timeout_seconds,
+ const base::Callback<void(const Error &)> &done) {
+ // Run all the start actions in the hook table.
+ for (HookTableMap::const_iterator it = hook_table_.begin();
+ it != hook_table_.end(); ++it) {
+ it->second.start.Run();
+ }
+
+ // Start polling for completion.
+ iteration_counter_ = 0;
+ PollActions(timeout_seconds, done);
+}
+
+void HookTable::PollActions(int timeout_seconds,
+ const base::Callback<void(const Error &)> &done) {
+ // Call all the poll functions in the hook table.
+ bool all_done = true;
+ for (HookTableMap::const_iterator it = hook_table_.begin();
+ it != hook_table_.end(); ++it) {
+ if (!it->second.poll.Run()) {
+ all_done = false;
+ }
+ }
+
+ if (all_done) {
+ done.Run(Error(Error::kSuccess));
+ return;
+ }
+
+ if (iteration_counter_ >= kPollIterations) {
+ done.Run(Error(Error::kOperationTimeout));
+ return;
+ }
+
+ // Some actions have not yet completed. Queue this function to poll again
+ // later.
+ Closure poll_actions_cb = Bind(&HookTable::PollActions, Unretained(this),
+ timeout_seconds, ConstRef(done));
+ const uint64 delay_ms = (timeout_seconds * 1000) / kPollIterations;
+ event_dispatcher_->PostDelayedTask(poll_actions_cb, delay_ms);
+ iteration_counter_++;
+}
+
+} // namespace shill