Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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 race condition that caused LaunchAsync to never resolve for chrome #2214
Fix race condition that caused LaunchAsync to never resolve for chrome #2214
Changes from 10 commits
ca95043
5a5391b
f58262f
aabb86e
deb351d
84c3dd5
dda9812
91c5e2b
7e65695
0d3a600
16b9283
d7d6617
e8199a6
f22b62a
1bbb87f
File filter
Filter by extension
Conversations
Jump to
There are no files selected for viewing
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.
Is there a benefit of moving the call to
Target.setDiscoverTargets
inInitializeAsync
instead of having it in the constructor as it is in puppeteer?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 didn't fully understand this, so I'll have to run more tests to get a better grip at it. But the test PuppeteerConnectTests.ShouldSupportTargetFilter was failing randomly. This test connects to a browser that already has 3 tabs open.
What I found was that the messages of the targets were being processed concurrently, even before the event handlers in
AttachAsync
were added. Again, ultimately this is due to the multi-threaded .NET vs single-threaded JS difference, as in JS the constructor and the event handlers would be added without any concurrent code.I will get back to you with a more thorough explanation.
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 agree with the addition of
_targetDiscoveryCompletionSource
to ensure it has completed before anything else tries to consume_targetsIdsForInit
whichStoreExistingTargetsForInit
populates.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.
Another idea, if we move
Target.setDiscoverTargets
toInitializeAsync
anyways, can we use modern async/await syntax and avoid_targetDiscoveryCompletionSource
or am I missing some concurrency again?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.
@jnyrup I have reverted these changes and couldn't reproduce any failure in 100 executions of PuppeteerConnectTests.ShouldSupportTargetFilter. Let's see how it behaves in AppVeyor.
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.
Turns out awaiting for
_targetDiscoveryCompletionSource
is needed before callingFinishInitializationIfReady()
, otherwiseFinishInitializationIfReady
could execute before any targets have been added to the_targetsIdsForInit
collection and the initialization would be considered completed.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'm fine with how it's done now 👍
... but if we moved
_connection.SendAsync("Target.setDiscoverTargets"
toInitializeAsync
again and awaited it, like in my example above, I'm failing to see how we can reachFinishInitializationIfReady
beforesetDiscoverTargetsTask
has completed, which should remove the need for_targetDiscoveryCompletionSource
.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.
@jnyrup had a closer look at your code above.
The change seems fine, and it simplifies a few things - for example it uses a try/catch which is more natural than checking
t.IsFaulted
. It's also closer to how FirefoxTargetManager works.However, the
_targetDiscoveryCompletionSource
is still needed, to signal other threads that may be processing a message and executingOnAttachedToTarget
.It also has the downside of diverging from upstream.
I can commit these changes if you agree it's a better solution.
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.
Ahh, it's those events messing with my brain again...
I now (again) see why
_targetDiscoveryCompletionSource
still is necessary.I'll let kblok decide whether to call
_connection.SendAsync("Target.setDiscoverTargets"
in the constructor ofInitializeAsync
.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 think that in this PR,
_targetDiscoveryCompletionSource
is more important than the location ofTarget.setDiscoverTargets
. I would leaveTarget.setDiscoverTargets
in the constructor as we have in upstream.We are making some important changes (good changes). But it would be nice to see if we do need to move it to
InitializeAsync
in isolation. If we see that we need it, we can move it in a new PR.This PR is looking great. I would merge it after the new
WithTimeout
s. Thank you @leonardo-fernandes, @jnyrup for the hard work here.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'm noticing that we now only execute
((Target)Target).CloseTask
ifClient.Connection.SendAsync("Target.closeTarget"
completes successfully.Should we still execute
((Target)Target).CloseTask
ifClient.Connection.SendAsync("Target.closeTarget"
throws an exception?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.
@jnyrup this matches upstream
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.
That's correct, I based it on upstream.
If
Target.closeTarget
fails, it's very likely thatTargetManager_TargetGone
will not be invoked, and the await ofCloseTask
would block the code indefinitely.