1 // Copyright 2017 The Chromium 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 MOJO_PUBLIC_CPP_BINDINGS_CALLBACK_HELPERS_H_
6 #define MOJO_PUBLIC_CPP_BINDINGS_CALLBACK_HELPERS_H_
7
8 #include <memory>
9 #include <utility>
10
11 #include "base/bind.h"
12 #include "base/callback.h"
13 #include "base/macros.h"
14 #include "base/memory/ptr_util.h"
15
16 // This is a helper utility to wrap a base::OnceCallback such that if the
17 // callback is destructed before it has a chance to run (e.g. the callback is
18 // bound into a task and the task is dropped), it will be run the with
19 // default arguments passed into WrapCallbackWithDefaultInvokeIfNotRun.
20 // Alternatively, it will run the delete closure passed to
21 // WrapCallbackWithDropHandler.
22 //
23 // These helpers are intended for use on the client side of a mojo interface,
24 // where users want to know if their individual callback was dropped (e.g.
25 // due to connection error). This can save the burden of tracking pending
26 // mojo callbacks in a map so they can be cleaned up in the interface's
27 // connection error callback.
28 //
29 // Caveats:
30 // 1) The default form of the callback, called when the original was dropped
31 // before running, may not run on the thread you expected. If this is a problem
32 // for your code, DO NOT USE these helpers.
33 // 2) There is no type information that indicates the wrapped object has special
34 // destructor behavior. It is therefore not recommended to pass these wrapped
35 // callbacks into deep call graphs where code readers could be confused whether
36 // or not the Run() mehtod should be invoked.
37 //
38 // Example:
39 // foo->DoWorkAndReturnResult(
40 // WrapCallbackWithDefaultInvokeIfNotRun(
41 // base::BindOnce(&Foo::OnResult, this), false));
42 //
43 // If the callback is destructed without running, it'll be run with "false".
44 //
45 // foo->DoWorkAndReturnResult(
46 // WrapCallbackWithDropHandler(base::BindOnce(&Foo::OnResult, this),
47 // base::BindOnce(&Foo::LogError, this, WAS_DROPPED)));
48
49 namespace mojo {
50 namespace internal {
51
52 // First, tell the compiler CallbackWithDeleteHelper is a class template with
53 // one type parameter. Then define specializations where the type is a function
54 // returning void and taking zero or more arguments.
55 template <typename Signature>
56 class CallbackWithDeleteHelper;
57
58 // Only support callbacks that return void because otherwise it is odd to call
59 // the callback in the destructor and drop the return value immediately.
60 template <typename... Args>
61 class CallbackWithDeleteHelper<void(Args...)> {
62 public:
63 using CallbackType = base::OnceCallback<void(Args...)>;
64
65 // Bound arguments may be different to the callback signature when wrappers
66 // are used, e.g. in base::Owned and base::Unretained case, they are
67 // OwnedWrapper and UnretainedWrapper. Use BoundArgs to help handle this.
68 template <typename... BoundArgs>
CallbackWithDeleteHelper(CallbackType callback,BoundArgs &&...args)69 explicit CallbackWithDeleteHelper(CallbackType callback, BoundArgs&&... args)
70 : callback_(std::move(callback)) {
71 delete_callback_ =
72 base::BindOnce(&CallbackWithDeleteHelper::Run, base::Unretained(this),
73 std::forward<BoundArgs>(args)...);
74 }
75
76 // The first int param acts to disambiguate this constructor from the template
77 // constructor above. The precendent is C++'s own operator++(int) vs
78 // operator++() to distinguish post-increment and pre-increment.
CallbackWithDeleteHelper(int ignored,CallbackType callback,base::OnceClosure delete_callback)79 CallbackWithDeleteHelper(int ignored,
80 CallbackType callback,
81 base::OnceClosure delete_callback)
82 : callback_(std::move(callback)),
83 delete_callback_(std::move(delete_callback)) {}
84
~CallbackWithDeleteHelper()85 ~CallbackWithDeleteHelper() {
86 if (delete_callback_)
87 std::move(delete_callback_).Run();
88 }
89
Run(Args...args)90 void Run(Args... args) {
91 delete_callback_.Reset();
92 std::move(callback_).Run(std::forward<Args>(args)...);
93 }
94
95 private:
96 CallbackType callback_;
97 base::OnceClosure delete_callback_;
98
99 DISALLOW_COPY_AND_ASSIGN(CallbackWithDeleteHelper);
100 };
101
102 } // namespace internal
103
104 template <typename T, typename... Args>
WrapCallbackWithDropHandler(base::OnceCallback<T> cb,base::OnceClosure delete_cb)105 inline base::OnceCallback<T> WrapCallbackWithDropHandler(
106 base::OnceCallback<T> cb,
107 base::OnceClosure delete_cb) {
108 return base::BindOnce(&internal::CallbackWithDeleteHelper<T>::Run,
109 std::make_unique<internal::CallbackWithDeleteHelper<T>>(
110 0, std::move(cb), std::move(delete_cb)));
111 }
112
113 template <typename T, typename... Args>
WrapCallbackWithDefaultInvokeIfNotRun(base::OnceCallback<T> cb,Args &&...args)114 inline base::OnceCallback<T> WrapCallbackWithDefaultInvokeIfNotRun(
115 base::OnceCallback<T> cb,
116 Args&&... args) {
117 return base::BindOnce(&internal::CallbackWithDeleteHelper<T>::Run,
118 std::make_unique<internal::CallbackWithDeleteHelper<T>>(
119 std::move(cb), std::forward<Args>(args)...));
120 }
121
122 } // namespace mojo
123
124 #endif // MOJO_PUBLIC_CPP_BINDINGS_CALLBACK_HELPERS_H_
125