117 lines
4.7 KiB
C++
117 lines
4.7 KiB
C++
//
|
|
// Copyright (C) 2013 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_RESULT_AGGREGATOR_H_
|
|
#define SHILL_RESULT_AGGREGATOR_H_
|
|
|
|
#include <base/cancelable_callback.h>
|
|
#include <base/macros.h>
|
|
#include <base/memory/ref_counted.h>
|
|
|
|
#include "shill/callbacks.h"
|
|
#include "shill/error.h"
|
|
|
|
namespace shill {
|
|
|
|
class EventDispatcher;
|
|
|
|
// The ResultAggregator is used to aggregate the result of multiple
|
|
// asynchronous operations. To use: construct a ResultAggregator, and
|
|
// Bind its ReportResult methods to some Callbacks. The ResultAggregator
|
|
// can also be constructed with an EventDispatcher pointer and timeout delay if
|
|
// we want to wait for a limited period of time for asynchronous operations
|
|
// to complete.
|
|
//
|
|
// When the Callbacks are destroyed, they will drop their references
|
|
// to the ResultAggregator. When all references to the ResultAggregator are
|
|
// destroyed, or if a timeout occurs, the ResultAggregator will invoke
|
|
// |callback_|. |callback_| will only be invoked exactly once by whichever of
|
|
// these two events occurs first.
|
|
//
|
|
// |callback_| will see Error type of Success if all Callbacks reported
|
|
// Success to ResultAggregator. If the timeout occurs, |callback_| will see
|
|
// Error type of OperationTimeout. Otherwise, |callback_| will see the first of
|
|
// the Errors reported to ResultAggregator.
|
|
//
|
|
// Note: If no callbacks invoked ReportResult and the ResultAggregator is
|
|
// destructed (before timing out), the ResultAggregator will be destructed
|
|
// silently and will not invoke |callback_|. This can cause unexpected
|
|
// behavior if the user expects |callback_| to be invoked after the
|
|
// result_aggregator goes out of scope. For example:
|
|
//
|
|
// void Manager::Foo() {
|
|
// auto result_aggregator(make_scoped_refptr(new ResultAggregator(
|
|
// Bind(&Manager::Func, AsWeakPtr()), dispatcher_, 1000)));
|
|
// if (condition) {
|
|
// LOG(ERROR) << "Failed!"
|
|
// return;
|
|
// }
|
|
// ResultCallback aggregator_callback(
|
|
// Bind(&ResultAggregator::ReportResult, result_aggregator));
|
|
// devices_[0]->OnBeforeSuspend(aggregator_callback);
|
|
// }
|
|
//
|
|
// If |condition| is true and the function returns without passing the
|
|
// reference to |result_aggregator| to devices_[0], |result_aggregator| will
|
|
// be destructed upon returning from Manager::Foo and will never call
|
|
// Manager::Func(). This is problematic if the owner of |result_aggregator|
|
|
// expects Manager::Func to be called when |result_aggregator| goes out of
|
|
// scope.
|
|
//
|
|
// Another anomaly that can occur is it the ResultCallback that is being
|
|
// passed around is allowed to go out to scope without being run. If at least
|
|
// one object ran the ResultCallback, the ResultAggregator will invoke
|
|
// |callback_| upon going out of scope, even though there exists an object
|
|
// that was passed a ResultCallback but did not actually run it. This is
|
|
// incorrect behavior, as we assume that |callback_| will only be run if
|
|
// the ResultAggregator times out or if all objects that were passed the
|
|
// ResultCallback run it.
|
|
//
|
|
// In order to ensure that ResultAggregator behaves as it is meant to, follow
|
|
// these conventions when using it:
|
|
// 1) Always run any ResultCallback that is passed around before letting it
|
|
// go out of scope.
|
|
// 2) If the ResultAggregator will go out of scope without passing any
|
|
// ResultCallback objects (i.e. references to itself) to other objects,
|
|
// invoke the callback the ResultAggregator was constructed with directly
|
|
// before letting ResultAggregator go out of scope.
|
|
|
|
class ResultAggregator : public base::RefCounted<ResultAggregator> {
|
|
public:
|
|
explicit ResultAggregator(const ResultCallback& callback);
|
|
ResultAggregator(const ResultCallback& callback, EventDispatcher* dispatcher,
|
|
int timeout_milliseconds);
|
|
virtual ~ResultAggregator();
|
|
|
|
void ReportResult(const Error& error);
|
|
|
|
private:
|
|
// Callback for timeout registered with EventDispatcher.
|
|
void Timeout();
|
|
|
|
base::WeakPtrFactory<ResultAggregator> weak_ptr_factory_;
|
|
const ResultCallback callback_;
|
|
base::CancelableClosure timeout_callback_;
|
|
bool got_result_;
|
|
bool timed_out_;
|
|
Error error_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(ResultAggregator);
|
|
};
|
|
|
|
} // namespace shill
|
|
|
|
#endif // SHILL_RESULT_AGGREGATOR_H_
|