1 //
2 // Copyright (C) 2013 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 
17 #ifndef SHILL_RESULT_AGGREGATOR_H_
18 #define SHILL_RESULT_AGGREGATOR_H_
19 
20 #include <base/cancelable_callback.h>
21 #include <base/macros.h>
22 #include <base/memory/ref_counted.h>
23 
24 #include "shill/callbacks.h"
25 #include "shill/error.h"
26 
27 namespace shill {
28 
29 class EventDispatcher;
30 
31 // The ResultAggregator is used to aggregate the result of multiple
32 // asynchronous operations. To use: construct a ResultAggregator, and
33 // Bind its ReportResult methods to some Callbacks. The ResultAggregator
34 // can also be constructed with an EventDispatcher pointer and timeout delay if
35 // we want to wait for a limited period of time for asynchronous operations
36 // to complete.
37 //
38 // When the Callbacks are destroyed, they will drop their references
39 // to the ResultAggregator. When all references to the ResultAggregator are
40 // destroyed, or if a timeout occurs, the ResultAggregator will invoke
41 // |callback_|. |callback_| will only be invoked exactly once by whichever of
42 // these two events occurs first.
43 //
44 // |callback_| will see Error type of Success if all Callbacks reported
45 // Success to ResultAggregator. If the timeout occurs, |callback_| will see
46 // Error type of OperationTimeout. Otherwise, |callback_| will see the first of
47 // the Errors reported to ResultAggregator.
48 //
49 // Note: If no callbacks invoked ReportResult and the ResultAggregator is
50 // destructed (before timing out), the ResultAggregator will be destructed
51 // silently and will not invoke |callback_|. This can cause unexpected
52 // behavior if the user expects |callback_| to be invoked after the
53 // result_aggregator goes out of scope. For example:
54 //
55 // void Manager::Foo() {
56 //   auto result_aggregator(make_scoped_refptr(new ResultAggregator(
57 //       Bind(&Manager::Func, AsWeakPtr()), dispatcher_, 1000)));
58 //   if (condition) {
59 //     LOG(ERROR) << "Failed!"
60 //     return;
61 //   }
62 //   ResultCallback aggregator_callback(
63 //       Bind(&ResultAggregator::ReportResult, result_aggregator));
64 //   devices_[0]->OnBeforeSuspend(aggregator_callback);
65 // }
66 //
67 // If |condition| is true and the function returns without passing the
68 // reference to |result_aggregator| to devices_[0], |result_aggregator| will
69 // be destructed upon returning from Manager::Foo and will never call
70 // Manager::Func(). This is problematic if the owner of |result_aggregator|
71 // expects Manager::Func to be called when |result_aggregator| goes out of
72 // scope.
73 //
74 // Another anomaly that can occur is it the ResultCallback that is being
75 // passed around is allowed to go out to scope without being run. If at least
76 // one object ran the ResultCallback, the ResultAggregator will invoke
77 // |callback_| upon going out of scope, even though there exists an object
78 // that was passed a ResultCallback but did not actually run it. This is
79 // incorrect behavior, as we assume that |callback_| will only be run if
80 // the ResultAggregator times out or if all objects that were passed the
81 // ResultCallback run it.
82 //
83 // In order to ensure that ResultAggregator behaves as it is meant to, follow
84 // these conventions when using it:
85 //   1) Always run any ResultCallback that is passed around before letting it
86 //      go out of scope.
87 //   2) If the ResultAggregator will go out of scope without passing any
88 //      ResultCallback objects (i.e. references to itself) to other objects,
89 //      invoke the callback the ResultAggregator was constructed with directly
90 //      before letting ResultAggregator go out of scope.
91 
92 class ResultAggregator : public base::RefCounted<ResultAggregator> {
93  public:
94   explicit ResultAggregator(const ResultCallback& callback);
95   ResultAggregator(const ResultCallback& callback, EventDispatcher* dispatcher,
96                    int timeout_milliseconds);
97   virtual ~ResultAggregator();
98 
99   void ReportResult(const Error& error);
100 
101  private:
102   // Callback for timeout registered with EventDispatcher.
103   void Timeout();
104 
105   base::WeakPtrFactory<ResultAggregator> weak_ptr_factory_;
106   const ResultCallback callback_;
107   base::CancelableClosure timeout_callback_;
108   bool got_result_;
109   bool timed_out_;
110   Error error_;
111 
112   DISALLOW_COPY_AND_ASSIGN(ResultAggregator);
113 };
114 
115 }  // namespace shill
116 
117 #endif  // SHILL_RESULT_AGGREGATOR_H_
118