internal override void ForceComplete(SourceLocation locationOpt, CancellationToken cancellationToken) { while (true) { cancellationToken.ThrowIfCancellationRequested(); var incompletePart = _state.NextIncompletePart; switch (incompletePart) { case CompletionPart.Attributes: GetAttributes(); break; case CompletionPart.TypeParameterConstraints: var constraintTypes = this.ConstraintTypesNoUseSiteDiagnostics; // Nested type parameter references might not be valid in error scenarios. //Debug.Assert(this.ContainingSymbol.IsContainingSymbolOfAllTypeParameters(this.ConstraintTypes)); //Debug.Assert(this.ContainingSymbol.IsContainingSymbolOfAllTypeParameters(ImmutableArray<TypeSymbol>.CreateFrom(this.Interfaces))); Debug.Assert(this.ContainingSymbol.IsContainingSymbolOfAllTypeParameters(this.EffectiveBaseClassNoUseSiteDiagnostics)); Debug.Assert(this.ContainingSymbol.IsContainingSymbolOfAllTypeParameters(this.DeducedBaseTypeNoUseSiteDiagnostics)); break; case CompletionPart.None: return; default: // any other values are completion parts intended for other kinds of symbols _state.NotePartComplete(CompletionPart.All & ~CompletionPart.TypeParameterSymbolAll); break; } _state.SpinWaitComplete(incompletePart, cancellationToken); } }
// 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); }
internal override void ForceComplete(SourceLocation locationOpt, CancellationToken cancellationToken) { if (!_state.HasComplete(CompletionPart.Attributes)) { _ = GetAttributes(); // Consider the following items: // 1. It is possible for parallel calls to GetAttributes to exist // 2. GetAttributes will return when the attributes are available, not when the part is noted // as complete. // 3. The thread which actually completes the attributes is the one which must set the CompletionParts.Attributes // value. // 4. This call cannot correctly return until this part is set. // // That is why it is necessary to check this value again. // // Note: #2 above is common practice amongst all of the symbols. // // Note: #3 above is an invariant that has existed in the code for some time. It's not clear if this invariant // is 100% correct. After inspection though it seems likely to be correct as the code is asserting that // SymbolDeclaredEvent is raised before CompletionPart.Attributes is noted as completed. Also this is a common // pattern amongst the GetAttributes implementations. _state.SpinWaitComplete(CompletionPart.Attributes, cancellationToken); } _state.NotePartComplete(CompletionPart.All); }
internal override void ForceComplete(SourceLocation locationOpt, CancellationToken cancellationToken) { while (true) { cancellationToken.ThrowIfCancellationRequested(); var incompletePart = _state.NextIncompletePart; switch (incompletePart) { case CompletionPart.Attributes: GetAttributes(); break; case CompletionPart.StartValidatingReferencedAssemblies: { BindingDiagnosticBag diagnostics = null; if (AnyReferencedAssembliesAreLinked) { diagnostics = BindingDiagnosticBag.GetInstance(); ValidateLinkedAssemblies(diagnostics, cancellationToken); } if (_state.NotePartComplete(CompletionPart.StartValidatingReferencedAssemblies)) { if (diagnostics != null) { _assemblySymbol.AddDeclarationDiagnostics(diagnostics); } _state.NotePartComplete(CompletionPart.FinishValidatingReferencedAssemblies); } if (diagnostics != null) { diagnostics.Free(); } } break; case CompletionPart.FinishValidatingReferencedAssemblies: // some other thread has started validating references (otherwise we would be in the case above) so // we just wait for it to both finish and report the diagnostics. Debug.Assert(_state.HasComplete(CompletionPart.StartValidatingReferencedAssemblies)); _state.SpinWaitComplete(CompletionPart.FinishValidatingReferencedAssemblies, cancellationToken); break; case CompletionPart.MembersCompleted: this.GlobalNamespace.ForceComplete(locationOpt, cancellationToken); if (this.GlobalNamespace.HasComplete(CompletionPart.MembersCompleted)) { _state.NotePartComplete(CompletionPart.MembersCompleted); } else { Debug.Assert(locationOpt != null, "If no location was specified, then the namespace members should be completed"); return; } break; case CompletionPart.None: return; default: // any other values are completion parts intended for other kinds of symbols _state.NotePartComplete(incompletePart); break; } _state.SpinWaitComplete(incompletePart, cancellationToken); } }