-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Port FSTAsyncQueryListener to C++ #2620
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
... and use it
These are implicitly deleted because DocumentSet is a member and isn't default constructible.
56bdef0
to
d52ef3b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
until we can use C++14
Any thoughts on when that might happen?
|
||
template <typename T> | ||
void AsyncEventListener<T>::Mute() { | ||
muted_.store(true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Optional: unless it's a stylistic preference, I think you can just use the regular assignment syntax muted_ = true
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had used load
/store
initially because I dislike hiding important operations behind operators. However, I can see the appeal. I've changed it because you pointed it out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand the rationale, though in this case an argument can be made that any operation on an atomic variable is atomic, so as long as the reader of the code keeps the type in mind, there should be no ambiguity.
if (async_listener) { | ||
async_listener->Mute(); | ||
async_listener_.reset(); | ||
async_listener.reset(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
reset
ting the local variable seems extraneous?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reseting this local variable has the effect of making the QueryListener the only thing keeping the AsyncEventListener alive, so that it can be destroyed inline with QueryListener.
However, now that I think about it, there's no great way to guarantee that the QueryListener will be destroyed on our queue because the query_listener local retains it. This means that the various EventListener destructors will run on this thread.
In the end I think that's not as bad a thing as I had thought it would be. The QueryListener and the AsyncEventListener are created on the user's thread so it's fine for them to be destroyed there.
Secondarily, now that you've prompted about it, I think that trying to manage which thread destroys an object by carefully managing the lifetime of shared_ptrs is a losing game. If we really care we need to make this contractual, i.e. that the pointed to object, in its destructor submits the real destructor to an executor. That's beyond the scope of this change though, so I'm not doing anything about it here.
So, I've removed the extra resets.
Firestore/core/src/firebase/firestore/api/listener_registration.mm
Outdated
Show resolved
Hide resolved
Firestore/core/src/firebase/firestore/api/listener_registration.mm
Outdated
Show resolved
Hide resolved
@@ -65,14 +65,14 @@ | |||
RaiseInitialEvent(snapshot); | |||
} | |||
} else if (ShouldRaiseEvent(snapshot)) { | |||
listener_(snapshot); | |||
listener_->OnEvent(snapshot); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: can this be std::move(snapshot)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. The snapshot is retained by the query listener, it is std::move
d below. The listener gets a copy.
} | ||
} | ||
|
||
void Release(ListenerRegistration&& registration) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Optional: maybe something like Unblock
or Proceed
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about Resolve
(in the spirit of promises being resolved or rejected)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good.
|
||
template <typename T> | ||
void AsyncEventListener<T>::Mute() { | ||
muted_.store(true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand the rationale, though in this case an argument can be made that any operation on an atomic variable is atomic, so as long as the reader of the code keeps the type in mind, there should be no ambiguity.
} | ||
} | ||
|
||
void Release(ListenerRegistration&& registration) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good.
* Test that shows a leak. * Add EventListener to match Android. * Use AsyncEventListener in FSTQueryListenerTests * Remove FSTAsyncQueryListener * Use EventListener throughout
There are a few things I'm trying to do with this PR:
std::unique_ptr
where possibleIn particular I wanted
EventListener
andAsyncEventListener
to implement the same interface to make this more like the Android port. In addition to being conceptually simpler this also greatly simplifies the ownership model because previously theAsyncEventListener
was retained indirectly through the closure it passed into theQueryListener
.I've added an
EventListener<T>::Create
that creates anEventListener
from a lambda, which should leave many cases just as expressive as before.Unfortunately, until we can use C++14, there's no generalized capture, so in some cases I've elected to explicitly spell out a class that implements
EventListener
in order to be able tostd::move
things into its closure. In another case this allows me to share the contents of the closure with the caller in a way that works functionally like Objective-C__block
scoped variables.This goes the rest of the way to addressing #2564, as far as the cycle in
getDocumentsWithSource:completion:
is concerned. The test in 786e2f1 shows the leak before and it's gone after.Note the DIFFBASE on #2619. This change depends upon
ListenerRegistration
existing as a C++ type, because clang does not allowstd::promise
of any ARC managed values.