Skip to content
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

Fix multithreaded _completion access #6068

Merged
merged 6 commits into from
Jul 17, 2020
Merged

Fix multithreaded _completion access #6068

merged 6 commits into from
Jul 17, 2020

Conversation

morganchen12
Copy link
Contributor

@morganchen12 morganchen12 commented Jul 17, 2020

Probably fixes #5979. Without a repro, who knows.

Fixes an issue where if an attempt to present a web URL via SFSafariViewController happened while there was already a URL being presented the error handler callback would be invoked on the wrong thread.
Also added some copies when moving blocks around.

Copy link
Member

@paulb777 paulb777 left a comment

Choose a reason for hiding this comment

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

Seems reasonable to me. @maksymmalyhin would you take a look?

FirebaseAuth/Sources/Utilities/FIRAuthURLPresenter.m Outdated Show resolved Hide resolved
@paulb777 paulb777 requested a review from maksymmalyhin July 17, 2020 17:03
@morganchen12 morganchen12 changed the title Morganchen/auth2 Jul 17, 2020
Copy link
Contributor

@maksymmalyhin maksymmalyhin left a comment

Choose a reason for hiding this comment

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

FIRAuthURLPresenter has a complex state that is mutated along the course of the operation. Making it thread-safe with simple locks may be a challenge. I think we may cover most of the case by the following approach:

  1. Perform all actions and mutations on a serial dispatch queues. Then we have a better chance to avoid inconsistent states and race conditions.
  2. (Optional) Consider additional synchronization on the caller side, e.g. use a separate instance of the presenter for each request and make sure there is a single disposable instance at the time.

Feel free to ask questions here or offline as for the details.

FirebaseAuth/Sources/Utilities/FIRAuthURLPresenter.m Outdated Show resolved Hide resolved
@@ -75,12 +95,15 @@ - (void)presentURL:(NSURL *)URL
completion:(FIRAuthURLPresentationCompletion)completion {
if (_isPresenting) {
Copy link
Contributor

Choose a reason for hiding this comment

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

If it is possible that presentURL:UIDelegate :callbackMatcher:completion: method may be called at the same time from another thread? If yes, it looks like we have another race condition here (and other places where _isPresenting is used).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. Looks like this class was originally written to serialize accesses to _isPresenting and _completion on the global auth work queue, which is a serial queue. I removed the locks and moved all accesses to this queue.

Copy link
Contributor

Choose a reason for hiding this comment

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

For some reason I cannot see the synchronization on the global auth worker queue in the PR. Is it outside the scope or haven't pushed it yet?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's out of scope of the PR. The serialization is pre-existing in the form of dispatches from the SFSafariViewControllerDelegate methods. The bug arose (I think) from calling the wrong completion closure off of the main queue, when it should have been called on the main queue.

Copy link
Contributor

Choose a reason for hiding this comment

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

Makes sense, thank you for the explanation! I think it may be useful to add this explanation to the PR description for the history.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

Copy link
Contributor

@maksymmalyhin maksymmalyhin left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Contributor

@renkelvin renkelvin left a comment

Choose a reason for hiding this comment

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

LGTM

FIRAuthURLPresentationCompletion completion = _completion;
_completion = nil;
FIRAuthURLPresentationCompletion completion = [_completion copy];
_completion = NULL;
Copy link
Contributor

Choose a reason for hiding this comment

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

I think nil should be used in objc, no?

Copy link
Contributor

Choose a reason for hiding this comment

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

nil and NULL will behave the same here, but NULL is more appropriate because _completion as a reference to a block which is technically a C pointer. (this article provides a summary for the difference)

@morganchen12 morganchen12 merged commit da034ba into master Jul 17, 2020
@morganchen12 morganchen12 deleted the morganchen/auth2 branch July 28, 2020 21:49
@firebase firebase locked and limited conversation to collaborators Aug 17, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.