33
44#include < utility>
55
6+ #include " app/src/cleanup_notifier.h"
67#include " app/src/include/firebase/future.h"
78#include " app/src/reference_counted_future_impl.h"
89#include " firestore/src/ios/hard_assert_ios.h"
@@ -27,10 +28,70 @@ template <typename ResultT>
2728class Promise {
2829 public:
2930 // 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)},
3235 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+ }
3495
3596 // This is only a template function to enable SFINAE. The `Promise` will have
3697 // either `SetValue(ResultT)` or `SetValue()` defined, based on whether
@@ -43,6 +104,9 @@ class Promise {
43104 template <typename DummyT = ResultT,
44105 typename = absl::enable_if_t <!std::is_void<DummyT>::value>>
45106 void SetValue (DummyT result) {
107+ if (IsCleanedUp ()) {
108+ return ;
109+ }
46110 future_api_->Complete (handle_, NoError (), /* error_message=*/ " " ,
47111 [&](ResultT* value) {
48112 // Future API doesn't support moving the value, use
@@ -54,24 +118,60 @@ class Promise {
54118 template <typename DummyT = ResultT,
55119 typename = absl::enable_if_t <std::is_void<DummyT>::value>>
56120 void SetValue () {
121+ if (IsCleanedUp ()) {
122+ return ;
123+ }
57124 future_api_->Complete (handle_, NoError ());
58125 }
59126
60127 void SetError (const util::Status& status) {
61128 HARD_ASSERT_IOS (
62129 !status.ok (),
63130 " To fulfill a promise with 'ok' status, use Promise::SetValue." );
131+ if (IsCleanedUp ()) {
132+ return ;
133+ }
134+
64135 future_api_->Complete (handle_, status.code (),
65136 status.error_message ().c_str ());
66137 }
67138
68139 Future<ResultT> future () {
140+ if (IsCleanedUp ()) {
141+ return Future<ResultT>{};
142+ }
143+
69144 return Future<ResultT>{future_api_, handle_.get ()};
70145 }
71146
72147 private:
148+ Promise () = default ;
149+
73150 int NoError () const { return static_cast <int >(Error::Ok); }
74151
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 ;
75175 ReferenceCountedFutureImpl* future_api_ = nullptr ;
76176 int identifier_ = 0 ;
77177 SafeFutureHandle<ResultT> handle_;
0 commit comments