mukesh agrawal | 46c27cc | 2013-07-10 16:39:10 -0700 | [diff] [blame] | 1 | // Copyright (c) 2013 The Chromium OS Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #ifndef SHILL_RESULT_AGGREGATOR_H_ |
| 6 | #define SHILL_RESULT_AGGREGATOR_H_ |
| 7 | |
Samuel Tan | 7c1aacb | 2014-09-15 17:13:40 -0700 | [diff] [blame] | 8 | #include <base/cancelable_callback.h> |
Ben Chan | cc67c52 | 2014-09-03 07:19:18 -0700 | [diff] [blame] | 9 | #include <base/macros.h> |
mukesh agrawal | 46c27cc | 2013-07-10 16:39:10 -0700 | [diff] [blame] | 10 | #include <base/memory/ref_counted.h> |
| 11 | |
| 12 | #include "shill/callbacks.h" |
| 13 | #include "shill/error.h" |
| 14 | |
| 15 | namespace shill { |
| 16 | |
Samuel Tan | 7c1aacb | 2014-09-15 17:13:40 -0700 | [diff] [blame] | 17 | class EventDispatcher; |
| 18 | |
mukesh agrawal | 46c27cc | 2013-07-10 16:39:10 -0700 | [diff] [blame] | 19 | // The ResultAggregator is used to aggregate the result of multiple |
| 20 | // asynchronous operations. To use: construct a ResultAggregator, and |
Samuel Tan | 7c1aacb | 2014-09-15 17:13:40 -0700 | [diff] [blame] | 21 | // Bind its ReportResult methods to some Callbacks. The ResultAggregator |
| 22 | // can also be constructed with an EventDispatcher pointer and timeout delay if |
| 23 | // we want to wait for a limited period of time for asynchronous operations |
| 24 | // to complete. |
mukesh agrawal | 46c27cc | 2013-07-10 16:39:10 -0700 | [diff] [blame] | 25 | // |
| 26 | // When the Callbacks are destroyed, they will drop their references |
Samuel Tan | fbe8d2b | 2014-09-15 20:23:59 -0700 | [diff] [blame] | 27 | // to the ResultAggregator. When all references to the ResultAggregator are |
| 28 | // destroyed, or if a timeout occurs, the ResultAggregator will invoke |
| 29 | // |callback_|. |callback_| will only be invoked exactly once by whichever of |
| 30 | // these two events occurs first. |
mukesh agrawal | 46c27cc | 2013-07-10 16:39:10 -0700 | [diff] [blame] | 31 | // |
Samuel Tan | fbe8d2b | 2014-09-15 20:23:59 -0700 | [diff] [blame] | 32 | // |callback_| will see Error type of Success if all Callbacks reported |
| 33 | // Success to ResultAggregator. If the timeout occurs, |callback_| will see |
| 34 | // Error type of OperationTimeout. Otherwise, |callback_| will see the first of |
| 35 | // the Errors reported to ResultAggregator. |
| 36 | // |
| 37 | // Note: If no callbacks invoked ReportResult and the ResultAggregator is |
| 38 | // destructed (before timing out), the ResultAggregator will be destructed |
| 39 | // silently and will not invoke |callback_|. This can cause unexpected |
| 40 | // behavior if the user expects |callback_| to be invoked after the |
| 41 | // result_aggregator goes out of scope. For example: |
| 42 | // |
| 43 | // void Manager::Foo() { |
| 44 | // auto result_aggregator(make_scoped_refptr(new ResultAggregator( |
| 45 | // Bind(&Manager::Func, AsWeakPtr()), dispatcher_, 1000))); |
| 46 | // if (condition) { |
| 47 | // LOG(ERROR) << "Failed!" |
| 48 | // return; |
| 49 | // } |
| 50 | // ResultCallback aggregator_callback( |
| 51 | // Bind(&ResultAggregator::ReportResult, result_aggregator)); |
| 52 | // devices_[0]->OnBeforeSuspend(aggregator_callback); |
| 53 | // } |
| 54 | // |
| 55 | // If |condition| is true and the function returns without passing the |
| 56 | // reference to |result_aggregator| to devices_[0], |result_aggregator| will |
| 57 | // be destructed upon returning from Manager::Foo and will never call |
| 58 | // Manager::Func(). This is problematic if the owner of |result_aggregator| |
| 59 | // expects Manager::Func to be called when |result_aggregator| goes out of |
| 60 | // scope. |
| 61 | // |
| 62 | // Another anomaly that can occur is it the ResultCallback that is being |
| 63 | // passed around is allowed to go out to scope without being run. If at least |
| 64 | // one object ran the ResultCallback, the ResultAggregator will invoke |
| 65 | // |callback_| upon going out of scope, even though there exists an object |
| 66 | // that was passed a ResultCallback but did not actually run it. This is |
| 67 | // incorrect behavior, as we assume that |callback_| will only be run if |
| 68 | // the ResultAggregator times out or if all objects that were passed the |
| 69 | // ResultCallback run it. |
| 70 | // |
| 71 | // In order to ensure that ResultAggregator behaves as it is meant to, follow |
| 72 | // these conventions when using it: |
| 73 | // 1) Always run any ResultCallback that is passed around before letting it |
| 74 | // go out of scope. |
| 75 | // 2) If the ResultAggregator will go out of scope without passing any |
| 76 | // ResultCallback objects (i.e. references to itself) to other objects, |
| 77 | // invoke the callback the ResultAggregator was constructed with directly |
| 78 | // before letting ResultAggregator go out of scope. |
| 79 | |
mukesh agrawal | 46c27cc | 2013-07-10 16:39:10 -0700 | [diff] [blame] | 80 | class ResultAggregator : public base::RefCounted<ResultAggregator> { |
| 81 | public: |
| 82 | explicit ResultAggregator(const ResultCallback &callback); |
Samuel Tan | 7c1aacb | 2014-09-15 17:13:40 -0700 | [diff] [blame] | 83 | ResultAggregator(const ResultCallback &callback, EventDispatcher *dispatcher, |
| 84 | int timeout_milliseconds); |
mukesh agrawal | 46c27cc | 2013-07-10 16:39:10 -0700 | [diff] [blame] | 85 | virtual ~ResultAggregator(); |
| 86 | |
| 87 | void ReportResult(const Error &error); |
| 88 | |
| 89 | private: |
Samuel Tan | 7c1aacb | 2014-09-15 17:13:40 -0700 | [diff] [blame] | 90 | // Callback for timeout registered with EventDispatcher. |
| 91 | void Timeout(); |
| 92 | |
| 93 | base::WeakPtrFactory<ResultAggregator> weak_ptr_factory_; |
mukesh agrawal | 46c27cc | 2013-07-10 16:39:10 -0700 | [diff] [blame] | 94 | const ResultCallback callback_; |
Samuel Tan | 7c1aacb | 2014-09-15 17:13:40 -0700 | [diff] [blame] | 95 | base::CancelableClosure timeout_callback_; |
mukesh agrawal | 46c27cc | 2013-07-10 16:39:10 -0700 | [diff] [blame] | 96 | bool got_result_; |
Samuel Tan | 7c1aacb | 2014-09-15 17:13:40 -0700 | [diff] [blame] | 97 | bool timed_out_; |
mukesh agrawal | 46c27cc | 2013-07-10 16:39:10 -0700 | [diff] [blame] | 98 | Error error_; |
| 99 | |
| 100 | DISALLOW_COPY_AND_ASSIGN(ResultAggregator); |
| 101 | }; |
| 102 | |
| 103 | } // namespace shill |
| 104 | |
| 105 | #endif // SHILL_RESULT_AGGREGATOR_H_ |