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

Make Uri Thread-Safe #33042

Merged
merged 11 commits into from
May 9, 2020
Merged

Conversation

MihaZupan
Copy link
Member

@MihaZupan MihaZupan commented Mar 2, 2020

Fixes #19130, contributes to #24239 (I will update the docs after we ensure thread-safety across all members).

Instead of locking on a string (could have even been the same instance the user passed into the ctor) and on the _info, we can perform the necesarry synchronization around Flags by using Interlocked.

UriInfo and MoreInfo fields are updated without synhronization, meaning that we may perform some duplicated work but not corrupt the state, The fields are always assigned-only in one go / written to before we share the instance (for example, we will set some UriInfo.Offset fields before saving the local instance to the shared _info field),

@MihaZupan MihaZupan added this to the 5.0 milestone Mar 2, 2020
@MihaZupan MihaZupan requested review from stephentoub and a team March 2, 2020 12:54
@MihaZupan
Copy link
Member Author

Uri tests in CI are hitting this assert in threads.cpp. @jkotas can you comment on what that is about?

@jkotas
Copy link
Member

jkotas commented Mar 2, 2020

Uri tests in CI are hitting this assert in threads.cpp. @jkotas can you comment on what that is about?

This assert means that lock was taken when thread exited. Your change might have exposed a bug somewhere else.

@stephentoub
Copy link
Member

stephentoub commented Mar 2, 2020

This assert means that lock was taken when thread exited. Your change might have exposed a bug somewhere else.

Presumably it's because of this line in the test?
https://github.com/dotnet/runtime/pull/33042/files#diff-8b7a4bf942ba035dbe2451db20151f71R1005

@jkotas
Copy link
Member

jkotas commented Mar 2, 2020

Presumably it's because of this line in the test?

Yes, this change would trigger this assert. This assert is a left-over from SQL CLR days where it was important to avoid orphaned locks. I will look into deleting it.

@jkotas
Copy link
Member

jkotas commented Mar 2, 2020

. This assert is a left-over from SQL CLR days where it was important to avoid orphaned locks. I will look into deleting it.

#33048

@MihaZupan
Copy link
Member Author

The scope of this PR has changed to Uri thread safety as a whole, not just the lock-on-string behavior.

@MihaZupan MihaZupan changed the title Do not lock on string in Uri Make Uri Thread-Safe Mar 3, 2020
monojenkins pushed a commit to monojenkins/mono that referenced this pull request Mar 5, 2020
These APIs were introduced recently in dotnet/runtime#33042 and already used in several places e.g. in `Task`, `InterlockedBitVector32`, `SafeHandle`, `RegexCharClass`

It should emit `%reg = atomicrmw and (or)` in LLVM IR. (and unlike `Interlocked.Add` these API return old value, see dotnet/runtime#33102

Addresses dotnet/runtime#32239 for Mono.
EgorBo added a commit to mono/mono that referenced this pull request Mar 10, 2020
These APIs were introduced recently in dotnet/runtime#33042 and already used in several places e.g. in `Task`, `InterlockedBitVector32`, `SafeHandle`, `RegexCharClass`

It should emit `%reg = atomicrmw and (or)` in LLVM IR. (and unlike `Interlocked.Add` these API return old value, see dotnet/runtime#33102

Addresses dotnet/runtime#32239 for Mono.

Co-authored-by: EgorBo <EgorBo@users.noreply.github.com>
@MihaZupan MihaZupan closed this Mar 10, 2020
@MihaZupan
Copy link
Member Author

@stephentoub could you take a look at these changes?

The gist of this change is

  • Don't lock on the input string
  • Swap locks for Interlocked operations
  • Add some validation to prevent custom parsers from corrupting Uri instances
  • Remove a (very hard to hit) race condition where Uri.Equals could return the wrong result
  • Reduce race conditions around MoreInfo accesses, that would result in throwing away a value instead of caching it (not dangerous, just potentially duplicated work)

Copy link
Member

@stephentoub stephentoub left a comment

Choose a reason for hiding this comment

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

Looks pretty good. Have you measured perf impact of constructing and initializing Uris?

@MihaZupan
Copy link
Member Author

Have you measured perf impact of constructing and initializing Uris?

I'm seeing a ~3% time improvement on the following test cases

[Benchmark]
public int NewUri_Port() => new Uri("https://microsoft.com").Port;

[Benchmark]
public Uri NewUri_WithUnicode() => new Uri("https://microsoft.com/ä");

@stephentoub
Copy link
Member

Thanks.

Co-authored-by: Stephen Toub <stoub@microsoft.com>
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.

[Uri] FxCop: System.Uri locks on a string.
7 participants