3
3
4
4
#include < utility>
5
5
6
+ #include " app/src/cleanup_notifier.h"
6
7
#include " app/src/include/firebase/future.h"
7
8
#include " app/src/reference_counted_future_impl.h"
8
9
#include " firestore/src/ios/hard_assert_ios.h"
@@ -27,10 +28,70 @@ template <typename ResultT>
27
28
class Promise {
28
29
public:
29
30
// Creates a future backed by `LastResults` cache.
30
- Promise (ReferenceCountedFutureImpl* future_api, int identifier)
31
- : future_api_{NOT_NULL (future_api)},
31
+ Promise (CleanupNotifier* cleanup, ReferenceCountedFutureImpl* future_api,
32
+ int identifier)
33
+ : cleanup_{NOT_NULL (cleanup)},
34
+ future_api_{NOT_NULL (future_api)},
32
35
identifier_{identifier},
33
- handle_{future_api->SafeAlloc <ResultT>(identifier)} {}
36
+ handle_{future_api->SafeAlloc <ResultT>(identifier)} {
37
+ RegisterForCleanup ();
38
+ }
39
+
40
+ ~Promise () { UnregisterForCleanup (); }
41
+
42
+ Promise (const Promise& rhs)
43
+ : cleanup_{rhs.cleanup_ },
44
+ future_api_{rhs.future_api_ },
45
+ identifier_{rhs.identifier_ },
46
+ handle_{rhs.handle_ } {
47
+ RegisterForCleanup ();
48
+ }
49
+
50
+ Promise (Promise&& rhs) noexcept
51
+ : cleanup_{rhs.cleanup_ },
52
+ future_api_{rhs.future_api_ },
53
+ identifier_{rhs.identifier_ },
54
+ handle_{rhs.handle_ } {
55
+ rhs.UnregisterForCleanup ();
56
+ rhs.cleanup_ = nullptr ;
57
+ rhs.future_api_ = nullptr ;
58
+ rhs.identifier_ = 0 ;
59
+ rhs.handle_ = {};
60
+
61
+ RegisterForCleanup ();
62
+ }
63
+
64
+ Promise& operator =(const Promise& rhs) {
65
+ UnregisterForCleanup ();
66
+
67
+ cleanup_ = rhs.cleanup_ ;
68
+ future_api_ = rhs.future_api_ ;
69
+ identifier_ = rhs.identifier_ ;
70
+ handle_ = rhs.handle_ ;
71
+
72
+ RegisterForCleanup ();
73
+
74
+ return *this ;
75
+ }
76
+
77
+ Promise& operator =(Promise&& rhs) noexcept {
78
+ rhs.UnregisterForCleanup ();
79
+ UnregisterForCleanup ();
80
+
81
+ cleanup_ = rhs.cleanup_ ;
82
+ future_api_ = rhs.future_api_ ;
83
+ identifier_ = rhs.identifier_ ;
84
+ handle_ = rhs.handle_ ;
85
+
86
+ RegisterForCleanup ();
87
+
88
+ rhs.cleanup_ = nullptr ;
89
+ rhs.future_api_ = nullptr ;
90
+ rhs.identifier_ = 0 ;
91
+ rhs.handle_ = {};
92
+
93
+ return *this ;
94
+ }
34
95
35
96
// This is only a template function to enable SFINAE. The `Promise` will have
36
97
// either `SetValue(ResultT)` or `SetValue()` defined, based on whether
@@ -43,6 +104,9 @@ class Promise {
43
104
template <typename DummyT = ResultT,
44
105
typename = absl::enable_if_t <!std::is_void<DummyT>::value>>
45
106
void SetValue (DummyT result) {
107
+ if (IsCleanedUp ()) {
108
+ return ;
109
+ }
46
110
future_api_->Complete (handle_, NoError (), /* error_message=*/ " " ,
47
111
[&](ResultT* value) {
48
112
// Future API doesn't support moving the value, use
@@ -54,24 +118,60 @@ class Promise {
54
118
template <typename DummyT = ResultT,
55
119
typename = absl::enable_if_t <std::is_void<DummyT>::value>>
56
120
void SetValue () {
121
+ if (IsCleanedUp ()) {
122
+ return ;
123
+ }
57
124
future_api_->Complete (handle_, NoError ());
58
125
}
59
126
60
127
void SetError (const util::Status& status) {
61
128
HARD_ASSERT_IOS (
62
129
!status.ok (),
63
130
" To fulfill a promise with 'ok' status, use Promise::SetValue." );
131
+ if (IsCleanedUp ()) {
132
+ return ;
133
+ }
134
+
64
135
future_api_->Complete (handle_, status.code (),
65
136
status.error_message ().c_str ());
66
137
}
67
138
68
139
Future<ResultT> future () {
140
+ if (IsCleanedUp ()) {
141
+ return Future<ResultT>{};
142
+ }
143
+
69
144
return Future<ResultT>{future_api_, handle_.get ()};
70
145
}
71
146
72
147
private:
148
+ Promise () = default ;
149
+
73
150
int NoError () const { return static_cast <int >(Error::Ok); }
74
151
152
+ // Note: `CleanupFn` is not used because `Promise` is a header-only class, to
153
+ // avoid a circular dependency between headers.
154
+ void RegisterForCleanup () {
155
+ if (IsCleanedUp ()) {
156
+ return ;
157
+ }
158
+
159
+ cleanup_->RegisterObject (this , [](void * raw_this) {
160
+ auto * this_ptr = static_cast <Promise*>(raw_this);
161
+ *this_ptr = {};
162
+ });
163
+ }
164
+
165
+ void UnregisterForCleanup () {
166
+ if (IsCleanedUp ()) {
167
+ return ;
168
+ }
169
+ cleanup_->UnregisterObject (this );
170
+ }
171
+
172
+ bool IsCleanedUp () const { return cleanup_ == nullptr ; }
173
+
174
+ CleanupNotifier* cleanup_ = nullptr ;
75
175
ReferenceCountedFutureImpl* future_api_ = nullptr ;
76
176
int identifier_ = 0 ;
77
177
SafeFutureHandle<ResultT> handle_;
0 commit comments