Skip to content

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

Merged
merged 50 commits into from
Mar 26, 2019
Merged

Port FSTAsyncQueryListener to C++ #2620

merged 50 commits into from
Mar 26, 2019

Conversation

wilhuff
Copy link
Contributor

@wilhuff wilhuff commented Mar 24, 2019

There are a few things I'm trying to do with this PR:

In particular I wanted EventListener and AsyncEventListener 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 the AsyncEventListener was retained indirectly through the closure it passed into the QueryListener.

I've added an EventListener<T>::Create that creates an EventListener 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 to std::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 allow std::promise of any ARC managed values.

@wilhuff wilhuff force-pushed the wilhuff/async-listener branch from 56bdef0 to d52ef3b Compare March 24, 2019 17:37
Copy link
Contributor

@var-const var-const left a 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);
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resetting the local variable seems extraneous?

Copy link
Contributor Author

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.

@@ -65,14 +65,14 @@
RaiseInitialEvent(snapshot);
}
} else if (ShouldRaiseEvent(snapshot)) {
listener_(snapshot);
listener_->OnEvent(snapshot);
Copy link
Contributor

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)?

Copy link
Contributor Author

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::moved below. The listener gets a copy.

}
}

void Release(ListenerRegistration&& registration) {
Copy link
Contributor

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?

Copy link
Contributor Author

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)?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good.

@var-const var-const assigned wilhuff and unassigned var-const Mar 25, 2019
@wilhuff wilhuff changed the base branch from wilhuff/listener-registration to master March 26, 2019 16:31

template <typename T>
void AsyncEventListener<T>::Mute() {
muted_.store(true);
Copy link
Contributor

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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good.

@wilhuff wilhuff merged commit ba332be into master Mar 26, 2019
@wilhuff wilhuff deleted the wilhuff/async-listener branch March 26, 2019 18:54
Corrob pushed a commit that referenced this pull request Apr 25, 2019
* Test that shows a leak.

* Add EventListener to match Android.

* Use AsyncEventListener in FSTQueryListenerTests

* Remove FSTAsyncQueryListener

* Use EventListener throughout
@firebase firebase locked and limited conversation to collaborators Oct 19, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants