Skip to content

Commit b3c3c2a

Browse files
committed
Merge remote-tracking branch 'majocha/asynclocal' into transparent-compiler-tests-asynclocal
2 parents 4223425 + d62ec6e commit b3c3c2a

File tree

32 files changed

+513
-789
lines changed

32 files changed

+513
-789
lines changed

docs/release-notes/.FSharp.Compiler.Service/8.0.300.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,4 @@
2727
* Reduce allocations in compiler checking via `ValueOption` usage ([PR #16323](https://github.com/dotnet/fsharp/pull/16323), [PR #16567](https://github.com/dotnet/fsharp/pull/16567))
2828
* Reverted [#16348](https://github.com/dotnet/fsharp/pull/16348) `ThreadStatic` `CancellationToken` changes to improve test stability and prevent potential unwanted cancellations. ([PR #16536](https://github.com/dotnet/fsharp/pull/16536))
2929
* Refactored parenthesization API. ([PR #16461])(https://github.com/dotnet/fsharp/pull/16461))
30+
* Replaced `ThreadStatic` diagnostics globals with `AsyncLocal` for better stability of Transparent Compiler. Removed `NodeCode` in favor of unflavored Async ([PR #16645](https://github.com/dotnet/fsharp/pull/16645))

docs/release-notes/.VisualStudio/17.10.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@
66
### Changed
77

88
* Use refactored parenthesization API in unnecessary parentheses code fix. ([PR #16461])(https://github.com/dotnet/fsharp/pull/16461))
9+
* Removed `NodeCode` in favor of unflavored Async with AsyncLocal state ([PR #16645](https://github.com/dotnet/fsharp/pull/16645))

src/Compiler/Driver/CompilerConfig.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ and IProjectReference =
252252
abstract FileName: string
253253

254254
/// Evaluate raw contents of the assembly file generated by the project
255-
abstract EvaluateRawContents: unit -> NodeCode<ProjectAssemblyDataResult>
255+
abstract EvaluateRawContents: unit -> Async<ProjectAssemblyDataResult>
256256

257257
/// Get the logical timestamp that would be the timestamp of the assembly file generated by the project
258258
///

src/Compiler/Driver/CompilerConfig.fsi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ and IProjectReference =
8686
/// Evaluate raw contents of the assembly file generated by the project.
8787
/// 'None' may be returned if an in-memory view of the contents is, for some reason,
8888
/// not available. In this case the on-disk view of the contents will be preferred.
89-
abstract EvaluateRawContents: unit -> NodeCode<ProjectAssemblyDataResult>
89+
abstract EvaluateRawContents: unit -> Async<ProjectAssemblyDataResult>
9090

9191
/// Get the logical timestamp that would be the timestamp of the assembly file generated by the project.
9292
///

src/Compiler/Driver/CompilerImports.fs

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1735,7 +1735,7 @@ and [<Sealed>] TcImports
17351735
m
17361736
) =
17371737

1738-
let startingErrorCount = DiagnosticsThreadStatics.DiagnosticsLogger.ErrorCount
1738+
let startingErrorCount = DiagnosticsAsyncState.DiagnosticsLogger.ErrorCount
17391739

17401740
// Find assembly level TypeProviderAssemblyAttributes. These will point to the assemblies that
17411741
// have class which implement ITypeProvider and which have TypeProviderAttribute on them.
@@ -1936,7 +1936,7 @@ and [<Sealed>] TcImports
19361936
with RecoverableException e ->
19371937
errorRecovery e m
19381938

1939-
if startingErrorCount < DiagnosticsThreadStatics.DiagnosticsLogger.ErrorCount then
1939+
if startingErrorCount < DiagnosticsAsyncState.DiagnosticsLogger.ErrorCount then
19401940
error (Error(FSComp.SR.etOneOrMoreErrorsSeenDuringExtensionTypeSetting (), m))
19411941

19421942
providers
@@ -2158,14 +2158,14 @@ and [<Sealed>] TcImports
21582158
(
21592159
ctok,
21602160
r: AssemblyResolution
2161-
) : NodeCode<(_ * (unit -> AvailableImportedAssembly list)) option> =
2162-
node {
2161+
) : Async<(_ * (unit -> AvailableImportedAssembly list)) option> =
2162+
async {
21632163
CheckDisposed()
21642164
let m = r.originalReference.Range
21652165
let fileName = r.resolvedPath
21662166

21672167
let! contentsOpt =
2168-
node {
2168+
async {
21692169
match r.ProjectReference with
21702170
| Some ilb -> return! ilb.EvaluateRawContents()
21712171
| None -> return ProjectAssemblyDataResult.Unavailable true
@@ -2228,27 +2228,29 @@ and [<Sealed>] TcImports
22282228

22292229
// NOTE: When used in the Language Service this can cause the transitive checking of projects. Hence it must be cancellable.
22302230
member tcImports.RegisterAndImportReferencedAssemblies(ctok, nms: AssemblyResolution list) =
2231-
node {
2231+
async {
22322232
CheckDisposed()
22332233

22342234
let tcConfig = tcConfigP.Get ctok
22352235

22362236
let runMethod =
22372237
match tcConfig.parallelReferenceResolution with
2238-
| ParallelReferenceResolution.On -> NodeCode.Parallel
2239-
| ParallelReferenceResolution.Off -> NodeCode.Sequential
2240-
2241-
let! results =
2242-
nms
2243-
|> List.map (fun nm ->
2244-
node {
2245-
try
2246-
return! tcImports.TryRegisterAndPrepareToImportReferencedDll(ctok, nm)
2247-
with e ->
2248-
errorR (Error(FSComp.SR.buildProblemReadingAssembly (nm.resolvedPath, e.Message), nm.originalReference.Range))
2249-
return None
2250-
})
2251-
|> runMethod
2238+
| ParallelReferenceResolution.On -> Async.Parallel
2239+
| ParallelReferenceResolution.Off -> Async.SequentialImmediate
2240+
2241+
let! results = async {
2242+
use captureTasks = new CaptureDiagnosticsConcurrently(DiagnosticsAsyncState.DiagnosticsLogger)
2243+
return! nms |> List.map (fun nm ->
2244+
async {
2245+
use _ = UseDiagnosticsLogger captureTasks.LoggerForTask
2246+
try
2247+
return! tcImports.TryRegisterAndPrepareToImportReferencedDll(ctok, nm)
2248+
with e ->
2249+
errorR (Error(FSComp.SR.buildProblemReadingAssembly (nm.resolvedPath, e.Message), nm.originalReference.Range))
2250+
return None
2251+
})
2252+
|> runMethod
2253+
}
22522254

22532255
let _dllinfos, phase2s = results |> Array.choose id |> List.ofArray |> List.unzip
22542256
fixupOrphanCcus ()
@@ -2282,7 +2284,7 @@ and [<Sealed>] TcImports
22822284
ReportWarnings warns
22832285

22842286
tcImports.RegisterAndImportReferencedAssemblies(ctok, res)
2285-
|> NodeCode.RunImmediateWithoutCancellation
2287+
|> Async.RunImmediateWithoutCancellation
22862288
|> ignore
22872289

22882290
true
@@ -2383,7 +2385,7 @@ and [<Sealed>] TcImports
23832385
// we dispose TcImports is because we need to dispose type providers, and type providers are never included in the framework DLL set.
23842386
// If a framework set ever includes type providers, you will not have to worry about explicitly calling Dispose as the Finalizer will handle it.
23852387
static member BuildFrameworkTcImports(tcConfigP: TcConfigProvider, frameworkDLLs, nonFrameworkDLLs) =
2386-
node {
2388+
async {
23872389
let ctok = CompilationThreadToken()
23882390
let tcConfig = tcConfigP.Get ctok
23892391

@@ -2460,7 +2462,7 @@ and [<Sealed>] TcImports
24602462
resolvedAssemblies |> List.choose tryFindEquivPrimaryAssembly
24612463

24622464
let! fslibCcu, fsharpCoreAssemblyScopeRef =
2463-
node {
2465+
async {
24642466
if tcConfig.compilingFSharpCore then
24652467
// When compiling FSharp.Core.dll, the fslibCcu reference to FSharp.Core.dll is a delayed ccu thunk fixed up during type checking
24662468
return CcuThunk.CreateDelayed getFSharpCoreLibraryName, ILScopeRef.Local
@@ -2553,7 +2555,7 @@ and [<Sealed>] TcImports
25532555
dependencyProvider
25542556
) =
25552557

2556-
node {
2558+
async {
25572559
let ctok = CompilationThreadToken()
25582560
let tcConfig = tcConfigP.Get ctok
25592561

@@ -2571,7 +2573,7 @@ and [<Sealed>] TcImports
25712573
}
25722574

25732575
static member BuildTcImports(tcConfigP: TcConfigProvider, dependencyProvider) =
2574-
node {
2576+
async {
25752577
let ctok = CompilationThreadToken()
25762578
let tcConfig = tcConfigP.Get ctok
25772579

@@ -2603,7 +2605,7 @@ let RequireReferences (ctok, tcImports: TcImports, tcEnv, thisAssemblyName, reso
26032605

26042606
let ccuinfos =
26052607
tcImports.RegisterAndImportReferencedAssemblies(ctok, resolutions)
2606-
|> NodeCode.RunImmediateWithoutCancellation
2608+
|> Async.RunImmediateWithoutCancellation
26072609

26082610
let asms =
26092611
ccuinfos

src/Compiler/Driver/CompilerImports.fsi

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -199,14 +199,14 @@ type TcImports =
199199
member internal Base: TcImports option
200200

201201
static member BuildFrameworkTcImports:
202-
TcConfigProvider * AssemblyResolution list * AssemblyResolution list -> NodeCode<TcGlobals * TcImports>
202+
TcConfigProvider * AssemblyResolution list * AssemblyResolution list -> Async<TcGlobals * TcImports>
203203

204204
static member BuildNonFrameworkTcImports:
205205
TcConfigProvider * TcImports * AssemblyResolution list * UnresolvedAssemblyReference list * DependencyProvider ->
206-
NodeCode<TcImports>
206+
Async<TcImports>
207207

208208
static member BuildTcImports:
209-
tcConfigP: TcConfigProvider * dependencyProvider: DependencyProvider -> NodeCode<TcGlobals * TcImports>
209+
tcConfigP: TcConfigProvider * dependencyProvider: DependencyProvider -> Async<TcGlobals * TcImports>
210210

211211
/// Process a group of #r in F# Interactive.
212212
/// Adds the reference to the tcImports and add the ccu to the type checking environment.

src/Compiler/Driver/ParseAndCheckInputs.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1828,7 +1828,7 @@ let CheckMultipleInputsUsingGraphMode
18281828
|> Graph.writeMermaidToFile graphFile)
18291829

18301830
let _ = ctok // TODO Use it
1831-
let diagnosticsLogger = DiagnosticsThreadStatics.DiagnosticsLogger
1831+
let diagnosticsLogger = DiagnosticsAsyncState.DiagnosticsLogger
18321832

18331833
// In the first linear part of parallel checking, we use a 'checkForErrors' that checks either for errors
18341834
// somewhere in the files processed prior to each one, or in the processing of this particular file.

src/Compiler/Driver/fsc.fs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -614,7 +614,7 @@ let main1
614614
// Import basic assemblies
615615
let tcGlobals, frameworkTcImports =
616616
TcImports.BuildFrameworkTcImports(foundationalTcConfigP, sysRes, otherRes)
617-
|> NodeCode.RunImmediateWithoutCancellation
617+
|> Async.RunImmediateWithoutCancellation
618618

619619
let ilSourceDocs =
620620
[
@@ -663,7 +663,7 @@ let main1
663663

664664
let tcImports =
665665
TcImports.BuildNonFrameworkTcImports(tcConfigP, frameworkTcImports, otherRes, knownUnresolved, dependencyProvider)
666-
|> NodeCode.RunImmediateWithoutCancellation
666+
|> Async.RunImmediateWithoutCancellation
667667

668668
// register tcImports to be disposed in future
669669
disposables.Register tcImports

src/Compiler/Facilities/AsyncMemoize.fs

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,11 @@ type internal MemoizeReply<'TValue> =
5050
| New of CancellationToken
5151
| Existing of Task<'TValue>
5252

53-
type internal MemoizeRequest<'TValue> = GetOrCompute of NodeCode<'TValue> * CancellationToken
53+
type internal MemoizeRequest<'TValue> = GetOrCompute of Async<'TValue> * CancellationToken
5454

5555
[<DebuggerDisplay("{DebuggerDisplay}")>]
5656
type internal Job<'TValue> =
57-
| Running of TaskCompletionSource<'TValue> * CancellationTokenSource * NodeCode<'TValue> * DateTime * ResizeArray<DiagnosticsLogger>
57+
| Running of TaskCompletionSource<'TValue> * CancellationTokenSource * Async<'TValue> * DateTime * ResizeArray<DiagnosticsLogger>
5858
| Completed of 'TValue * (PhasedDiagnostic * FSharpDiagnosticSeverity) list
5959
| Canceled of DateTime
6060
| Failed of DateTime * exn // TODO: probably we don't need to keep this
@@ -354,15 +354,12 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T
354354
log (Restarted, key)
355355
Interlocked.Increment &restarted |> ignore
356356
System.Diagnostics.Trace.TraceInformation $"{name} Restarted {key.Label}"
357-
let currentLogger = DiagnosticsThreadStatics.DiagnosticsLogger
358-
DiagnosticsThreadStatics.DiagnosticsLogger <- cachingLogger
359-
360-
try
361-
let! result = computation |> Async.AwaitNodeCode
362-
post (key, (JobCompleted(result, cachingLogger.CapturedDiagnostics)))
363-
return ()
364-
finally
365-
DiagnosticsThreadStatics.DiagnosticsLogger <- currentLogger
357+
use _ = UseDiagnosticsLogger cachingLogger
358+
359+
let! result = computation
360+
post (key, (JobCompleted(result, cachingLogger.CapturedDiagnostics)))
361+
return ()
362+
366363
with
367364
| TaskCancelled _ ->
368365
Interlocked.Increment &cancel_exception_subsequent |> ignore
@@ -481,14 +478,22 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T
481478
Version = key.GetVersion()
482479
}
483480

484-
node {
485-
let! ct = NodeCode.CancellationToken
481+
let callerDiagnosticLogger =
482+
if DiagnosticsAsyncState.DiagnosticsLogger = UninitializedDiagnosticsLogger then
483+
// TODO: Telemetry?
484+
DiagnosticsAsyncState.DiagnosticsLogger <- DiscardErrorsLogger
485+
486+
DiagnosticsAsyncState.DiagnosticsLogger
487+
488+
// Preserve outer diagnostics scope in case the computation doesn't.
489+
let computation = computation |> Async.CompilationScope
486490

487-
let callerDiagnosticLogger = DiagnosticsThreadStatics.DiagnosticsLogger
491+
async {
492+
let! ct = Async.CancellationToken
488493

489494
match!
490495
processRequest post (key, GetOrCompute(computation, ct)) callerDiagnosticLogger
491-
|> NodeCode.AwaitTask
496+
|> Async.AwaitTask
492497
with
493498
| New internalCt ->
494499

@@ -500,21 +505,17 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T
500505
Async.StartAsTask(
501506
async {
502507
// TODO: Should unify starting and restarting
503-
let currentLogger = DiagnosticsThreadStatics.DiagnosticsLogger
504-
DiagnosticsThreadStatics.DiagnosticsLogger <- cachingLogger
508+
use _ = UseDiagnosticsLogger cachingLogger
505509

506510
log (Started, key)
507511

508-
try
509-
let! result = computation |> Async.AwaitNodeCode
510-
post (key, (JobCompleted(result, cachingLogger.CapturedDiagnostics)))
511-
return result
512-
finally
513-
DiagnosticsThreadStatics.DiagnosticsLogger <- currentLogger
512+
let! result = computation
513+
post (key, (JobCompleted(result, cachingLogger.CapturedDiagnostics)))
514+
return result
514515
},
515516
cancellationToken = linkedCtSource.Token
516517
)
517-
|> NodeCode.AwaitTask
518+
|> Async.AwaitTask
518519
with
519520
| TaskCancelled ex ->
520521
// TODO: do we need to do anything else here? Presumably it should be done by the registration on
@@ -530,7 +531,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T
530531
post (key, (JobFailed(ex, cachingLogger.CapturedDiagnostics)))
531532
return raise ex
532533

533-
| Existing job -> return! job |> NodeCode.AwaitTask
534+
| Existing job -> return! job |> Async.AwaitTask
534535

535536
}
536537

src/Compiler/Facilities/AsyncMemoize.fsi

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,9 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T
6565

6666
member Clear: predicate: ('TKey -> bool) -> unit
6767

68-
member Get: key: ICacheKey<'TKey, 'TVersion> * computation: NodeCode<'TValue> -> NodeCode<'TValue>
68+
member Get: key: ICacheKey<'TKey, 'TVersion> * computation: Async<'TValue> -> Async<'TValue>
6969

70-
member Get': key: 'TKey * computation: NodeCode<'TValue> -> NodeCode<'TValue>
70+
member Get': key: 'TKey * computation: Async<'TValue> -> Async<'TValue>
7171

7272
member Event: IEvent<JobEvent * (string * 'TKey * 'TVersion)>
7373

0 commit comments

Comments
 (0)