// basesBeingResolved is only used to break circular references. internal NamespaceOrTypeSymbol GetAliasTarget(ConsList <TypeSymbol>?basesBeingResolved) { if (!_state.HasComplete(CompletionPart.AliasTarget)) { // the target is not yet bound. If it is an ordinary alias, bind the target // symbol. If it is an extern alias then find the target in the list of metadata references. var newDiagnostics = DiagnosticBag.GetInstance(); NamespaceOrTypeSymbol symbol = this.IsExtern ? ResolveExternAliasTarget(newDiagnostics) : ResolveAliasTarget(_binder, _aliasTargetName, newDiagnostics, basesBeingResolved); if ((object?)Interlocked.CompareExchange(ref _aliasTarget, symbol, null) == null) { // Note: It's important that we don't call newDiagnosticsToReadOnlyAndFree here. That call // can force the prompt evaluation of lazy initialized diagnostics. That in turn can // call back into GetAliasTarget on the same thread resulting in a dead lock scenario. bool won = Interlocked.Exchange(ref _aliasTargetDiagnostics, newDiagnostics) == null; Debug.Assert(won, "Only one thread can win the alias target CompareExchange"); _state.NotePartComplete(CompletionPart.AliasTarget); // we do not clear this.aliasTargetName, as another thread might be about to use it for ResolveAliasTarget(...) } else { newDiagnostics.Free(); // Wait for diagnostics to have been reported if another thread resolves the alias _state.SpinWaitComplete(CompletionPart.AliasTarget, default(CancellationToken)); } } return(_aliasTarget !); }
// basesBeingResolved is only used to break circular references. internal NamespaceOrTypeSymbol GetAliasTarget(ConsList <Symbol> basesBeingResolved) { if (!_state.HasComplete(CompletionPart.AliasTarget)) { // the target is not yet bound. If it is an ordinary alias, bind the target // symbol. If it is an extern alias then find the target in the list of metadata references. var newDiagnostics = DiagnosticBag.GetInstance(); NamespaceOrTypeSymbol symbol = this.IsExtern ? ResolveExternAliasTarget(newDiagnostics) : ResolveAliasTarget(_binder, _aliasTargetName, newDiagnostics, basesBeingResolved); if ((object)Interlocked.CompareExchange(ref _aliasTarget, symbol, null) == null) { bool won = ImmutableInterlocked.InterlockedInitialize(ref _aliasTargetDiagnostics, newDiagnostics.ToReadOnlyAndFree()); Debug.Assert(won, "Only one thread can win the alias target CompareExchange"); _state.NotePartComplete(CompletionPart.AliasTarget); // we do not clear this.aliasTargetName, as another thread might be about to use it for ResolveAliasTarget(...) } else { newDiagnostics.Free(); // Wait for diagnostics to have been reported if another thread resolves the alias _state.SpinWaitComplete(CompletionPart.AliasTarget, default(CancellationToken)); } } return(_aliasTarget); }
protected void LazyMethodChecks() { if (!state.HasComplete(CompletionPart.FinishMethodChecks)) { // TODO: if this lock ever encloses a potential call to Debugger.NotifyOfCrossThreadDependency, // then we should call DebuggerUtilities.CallBeforeAcquiringLock() (see method comment for more // details). object lockObject = MethodChecksLockObject; Debug.Assert(lockObject != null); lock (lockObject) { if (state.NotePartComplete(CompletionPart.StartMethodChecks)) { // By setting StartMethodChecks, we've committed to doing the checks and setting // FinishMethodChecks. So there is no cancellation supported between one and the other. var diagnostics = BindingDiagnosticBag.GetInstance(); try { MethodChecks(diagnostics); AddDeclarationDiagnostics(diagnostics); } finally { state.NotePartComplete(CompletionPart.FinishMethodChecks); diagnostics.Free(); } } else { // Either (1) this thread is in the process of completing the method, // or (2) some other thread has beat us to the punch and completed the method. // We can distinguish the two cases here by checking for the FinishMethodChecks // part to be complete, which would only occur if another thread completed this // method. // // The other case, in which this thread is in the process of completing the method, // requires that we return here even though the checks are not complete. That's because // methods are processed by first populating the return type and parameters by binding // the syntax from source. Those values are visible to the same thread for the purpose // of computing which methods are implemented and overridden. But then those values // may be rewritten (by the same thread) to copy down custom modifiers. In order to // allow the same thread to see the return type and parameters from the syntax (though // they do not yet take on their final values), we return here. // Due to the fact that LazyMethodChecks is potentially reentrant, we must use a // reentrant lock to avoid deadlock and cannot assert that at this point method checks // have completed (state.HasComplete(CompletionPart.FinishMethodChecks)). } } } }
internal sealed override bool HasComplete(CompletionPart part) { return(_state.HasComplete(part)); }
internal sealed override bool HasComplete(CompletionPart part) => state.HasComplete(part);