Skip to content

Avoid false assert in AssertMemberExposure due to a race.#53044

Merged
AlekseyTs merged 2 commits intodotnet:mainfrom
AlekseyTs:Issue52368
Apr 30, 2021
Merged

Avoid false assert in AssertMemberExposure due to a race.#53044
AlekseyTs merged 2 commits intodotnet:mainfrom
AlekseyTs:Issue52368

Conversation

@AlekseyTs
Copy link
Contributor

Fixes #52368.

@AlekseyTs AlekseyTs requested a review from a team as a code owner April 30, 2021 00:23

RoslynDebug.AssertOrFailFast(false, "Premature symbol exposure.");

static bool isMemberInCompleteMemeberList(MembersAndInitializers? membersAndInitializers, Symbol member)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
static bool isMemberInCompleteMemeberList(MembersAndInitializers? membersAndInitializers, Symbol member)
static bool isMemberInCompleteMemberList(MembersAndInitializers? membersAndInitializers, Symbol member)

Copy link
Member

@333fred 333fred left a comment

Choose a reason for hiding this comment

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

LGTM (commit 1) other than the typo.

@AlekseyTs
Copy link
Contributor Author

@dotnet/roslyn-compiler For a second review.

else
{
// It looks like there was a race and we need to check _lazyMembersAndInitializers again
membersAndInitializers = Volatile.Read(ref _lazyMembersAndInitializers);
Copy link
Member

Choose a reason for hiding this comment

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

In order for this to be safe I believe we need to switch the following assignment to use a Volatile.Write call

http://sourceroslyn.io/#Microsoft.CodeAnalysis.CSharp/Symbols/Source/SourceMemberContainerSymbol.cs,1458

var alreadyKnown = Interlocked.CompareExchange(ref _lazyMembersAndInitializers, membersAndInitializers, null);
if (alreadyKnown != null)
{
    diagnostics.Free();
    return alreadyKnown;
}

AddDeclarationDiagnostics(diagnostics);
diagnostics.Free();

// SUGGESTION: make this a Volatile.Write
_lazyDeclaredMembersAndInitializers = null;

return membersAndInitializers!;

The CompareExchange(ref _lazyMembersAndInitializes, ...) uses a fully memory barrier but I believe that occurs before the operation not after. That means all of the writes after the operation can be viewed by other thread in any order. Hence it's possible for a different thread to observe _lazyDeclaredMembersAndInitializers = null before it sees the write to _lazyMembersAndInitiaizers. That is true even though you use Volatile.Read because that only affects reordering on the current thread.

Rule of thumb is that if you use Volatile.Read on one thread they need to be paired with Volatile.Write or Intelocked.Exchange on the other thread. Else you can get into ordering issues.

I'm not 100% confident in my assessment here, memory ordering is tricky. So possible someone comes back and corrects me here but would feel better with this change.

Copy link
Member

Choose a reason for hiding this comment

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

Followed up with runtime team. My reasoning is incorrect. The CompareExchange does a fence before / after hence the writes are ordered.

@AlekseyTs AlekseyTs merged commit 3b1a1a4 into dotnet:main Apr 30, 2021
@ghost ghost added this to the Next milestone Apr 30, 2021
333fred added a commit to 333fred/roslyn that referenced this pull request May 5, 2021
…into bind-strings

* upstream/features/interpolated-string: (912 commits)
  Update version for 16.11
  Avoid false assert in AssertMemberExposure due to a race. (dotnet#53044)
  Fix caret placement for argument completion snippets
  Explicitly define no PR triggers as they are on 'by default'
  Remove eng/docker/mono.sh script (dotnet#51833)
  Run LSP integration on CI builds only until they're stable
  Implement the right interface
  Disable CompileTimeSolutionProvider
  Avoid negative logic when checking whether a eol is present
  Update src/Analyzers/CSharp/CodeFixes/UseSimpleUsingStatement/UseSimpleUsingStatementCodeFixProvider.cs
  Apply suggestions
  Adapt codestyle
  Prepend first statement of using block with a newline, if opening bracket is on same line as using statement
  Fix EnC capabilties check for runtimes that aren't returning capabilities yet (dotnet#53000)
  Add ConfigureAwait calls to cocoa code.
  Remove CustomTags from CodeAction constructors
  Make CodeAction.CustomTags internal
  Update "contributing" doc with pointers on IOperation test hook (dotnet#52978)
  Cocoa part
  Switch to a simpler cancellation ownership model for the FAR window. (Cocoa still to come)
  ...
@Youssef1313 Youssef1313 mentioned this pull request Jun 25, 2021
@RikkiGibson RikkiGibson modified the milestones: Next, 17.0.P2 Jun 29, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AssertMemberExposure causing flaky test failures

4 participants