Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

SSLStream : Removed sync lock method#24352

Closed
Drawaes wants to merge 1 commit into
dotnet:masterfrom
Drawaes:RemoveDuplicateCode
Closed

SSLStream : Removed sync lock method#24352
Drawaes wants to merge 1 commit into
dotnet:masterfrom
Drawaes:RemoveDuplicateCode

Conversation

@Drawaes
Copy link
Copy Markdown

@Drawaes Drawaes commented Sep 30, 2017

Looking at the code, because this method is basically never actually blocked on (only if a renegotiation takes place during a write) there is no need for a second "sync" method.

Instead we can block on the Async method as it is controlled via a TCS anyway and doesn't go down to a "real blocking method" at the OS level so no extra threads should be generated from the original. This again simplifies the code maintenance going forward and refactoring SslState

@benaadams
Copy link
Copy Markdown
Member

@dotnet-bot Test Outerloop Windows x64 Debug Build
@dotnet-bot Test Outerloop UWP CoreCLR x64 Debug Build
@dotnet-bot Test Outerloop Linux x64 Release Build

@Drawaes
Copy link
Copy Markdown
Author

Drawaes commented Sep 30, 2017

All failures in the OuterLoop Linux are again with System.Drawing.

  1. Debian 90
  2. SLES 12
  3. Ubuntu 17.04

Tests that fail are in MonoTests.System.Drawing.Imaging.PngCodecTest, all failing so tests are

  1. Bitmap1bitFeatures
  2. Bitmap1bitPixels

And that repeated up to 4bit.

I would hazard a guess and say this is unrelated.

/cc @mellinoe @safern any ideas why System.Drawing seems to be failing in outerloop a lot?

@Drawaes
Copy link
Copy Markdown
Author

Drawaes commented Sep 30, 2017

As a double check there is no differences in benchmarks that I can see. (Other than Azure "wobble")

@stephentoub
Copy link
Copy Markdown
Member

As a double check there is no differences in benchmarks that I can see

The benchmark uses the synchronous methods?

@Drawaes
Copy link
Copy Markdown
Author

Drawaes commented Sep 30, 2017

No actually will make one that does. Getresult on completed task should pretty much be a no-op right? And as there isn't even a way to force renegotiation in .net ....

@stephentoub
Copy link
Copy Markdown
Member

Getresult on completed task should pretty much be a no-op right?

Yup (other than if it failed, in which case it'll throw).

@Drawaes
Copy link
Copy Markdown
Author

Drawaes commented Oct 1, 2017

Right finally forced myself to write some Sync networking code, the results I see are

44906.5 for the "new" version vs "44705.7" for the "old" version, basically I can't find much in it, maybe slightly towards the new version but I wouldn't put money on either on any day

public Task LockAsync()
{
_sslState.CheckEnqueueWrite();
_sslState.CheckEnqueueWriteAsync().GetAwaiter().GetResult();
Copy link
Copy Markdown
Member

@stephentoub stephentoub Oct 1, 2017

Choose a reason for hiding this comment

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

If this is just going to use GetAwaiter().GetResult() and then return CompletedTask, why not remove LockAsync from ISslWriteAdapter entirely and just have the current call sites to LockAsync use CheckEnqueueWriteAsync? Seems like the implementation here should really just be:

public Task LockAsync() => _sslState.CheckEnqueueWriteAsync();

which is the same as the SslWriteAsync implementation, in which case it shouldn't be necessary in the abstraction.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Because the call site is the same path for sync and async?

I see a single call to LockAsync:
https://github.com/Drawaes/corefx/blob/e415d485d542fe952d7a6bdd9257d5a8837448cc/src/System.Net.Security/src/System/Net/Security/SslStreamInternal.cs#L294

If CheckEnqueueWriteAsync actually completes synchronously, then it doesn't matter whether you call GetAwaiter().GetResult() or not, as the call site is going to get back an already completed task. If CheckEnqueueWriteAsync completes asynchronously, the call site is still capable of working with async operations, and we shouldn't be forcing it to block earlier than it otherwise needs to... the call site will treat it like any other incomplete async operation, and then we'll end up blocking at the top-most level if necessary: https://github.com/Drawaes/corefx/blob/e415d485d542fe952d7a6bdd9257d5a8837448cc/src/System.Net.Security/src/System/Net/Security/SslStreamInternal.cs#L114

That said, I'm still not entirely clear why it's ok to remove the sync path. Can you explain that in more detail? Are you saying the sync path today never actually blocks on anything?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Nope but I am saying that in the Sync and Async case they both block waiting effectively for a different thread to complete the handshake. So the flow should be the same, its not like calling an async underlying method normally that will cause another task to be generated.

Now originally I made the sync and async versions because of the Async path that I made in the SslStream. But I have now taken a second look and because of the combined path I thought this was a good way to kill off some more code with no effect.

I have been known to be wrong though, so I am interested if you think there is a subtle difference I am missing here, and if I am then I am happy to drop this PR. Its really about killing off as much unneeded complexity, especially around this locking stuff in here.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I am saying that in the Sync and Async case they both block waiting effectively for a different thread to complete the handshake. So the flow should be the same, its not like calling an async underlying method normally that will cause another task to be generated.

I'm looking at this code:
https://github.com/Drawaes/corefx/blob/e415d485d542fe952d7a6bdd9257d5a8837448cc/src/System.Net.Security/src/System/Net/Security/SslState.cs#L1324-L1333
This would seem to suggest that, today, a synchronous caller will be woken up directly by this code, whereas an asynchronous caller will be woken up asynchronously via a work item being queued. Doesn't that mean that with this PR, the synchronous caller will be blocked and the asynchronous work will need to queue another work item that'll need to run to unblock it?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

You might be right here, I won't close the PR for now so that the testing can be discussed below. But this might just be better refactored completely to remove the current stuff. I suspect a SemaphoreSlim with count = 1 would be ideal here.

@Drawaes
Copy link
Copy Markdown
Author

Drawaes commented Oct 1, 2017

Because the call site is the same path for sync and async?

{
// Proceed with write.
return;
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It looks like we don't have any tests that make it beyond this return, in either the sync or async versions. Is it possible to add some?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

In a word no, @bartonjs should be able to confirm. But from .net I am sure there is no way to trigger a renegotiation. The testing I have done has been pretty manual using OpenSSL, or my own code to trigger the situations. So it might be possible to write an outerloop style scripted test but that would be beyond my knowledge of how your environment works for outerloop stuff...

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Ok, thanks. It's good to hear you've tested it manually, thanks, but it's scary to me that we have all this code to support renegotiation and nothing actually testing it automatically. @bartonjs, @Priya91, @geoffkizer, @wfurt, anything we can do about that?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Yes I agree, if someone is happy to help me through how a scripted outloop test is created, I am more than happy to put something together that should test it, if help is wanted. (I also agree that its a bit worrying that there is zero test coverage for what is a complex bit of code).

#22998 (comment)

@Drawaes
Copy link
Copy Markdown
Author

Drawaes commented Oct 3, 2017

Closing this for now, as there is no test coverage in this area and the benefits don't justify the risks of changing untested code.

Have opened #24407 to ask for tests for this area

@Drawaes Drawaes closed this Oct 3, 2017
@karelz karelz added this to the 2.1.0 milestone Oct 28, 2017
@Drawaes Drawaes deleted the RemoveDuplicateCode branch November 3, 2017 22:17
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.

8 participants