Skip to content

Commit d48369f

Browse files
dsymeDon Syme
andauthored
Use RunImmediate for better debug stacks (#11788)
* Use RunImmediate for better debug stacks * fix build Co-authored-by: Don Syme <donsyme@fastmail.com>
1 parent dfeb8a9 commit d48369f

File tree

30 files changed

+394
-311
lines changed

30 files changed

+394
-311
lines changed

src/fsharp/absil/illib.fs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ open System.Collections.Concurrent
88
open System.Diagnostics
99
open System.IO
1010
open System.Threading
11+
open System.Threading.Tasks
1112
open System.Runtime.CompilerServices
1213

1314
[<AutoOpen>]
@@ -86,6 +87,19 @@ module internal PervasiveAutoOpens =
8687

8788
let notFound() = raise (KeyNotFoundException())
8889

90+
type Async with
91+
static member RunImmediate (computation: Async<'T>, ?cancellationToken ) =
92+
let cancellationToken = defaultArg cancellationToken Async.DefaultCancellationToken
93+
let ts = TaskCompletionSource<'T>()
94+
let task = ts.Task
95+
Async.StartWithContinuations(
96+
computation,
97+
(fun k -> ts.SetResult k),
98+
(fun exn -> ts.SetException exn),
99+
(fun _ -> ts.SetCanceled()),
100+
cancellationToken)
101+
task.Result
102+
89103
[<Struct>]
90104
/// An efficient lazy for inline storage in a class type. Results in fewer thunks.
91105
type InlineDelayInit<'T when 'T : not struct> =

src/fsharp/absil/illib.fsi

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ module internal PervasiveAutoOpens =
4747

4848
member inline EndsWithOrdinal: value:string -> bool
4949

50+
type Async with
51+
/// Runs the computation synchronously, always starting on the current thread.
52+
static member RunImmediate: computation: Async<'T> * ?cancellationToken: CancellationToken -> 'T
53+
5054
val foldOn: p:('a -> 'b) -> f:('c -> 'b -> 'd) -> z:'c -> x:'a -> 'd
5155

5256
val notFound: unit -> 'a

src/fsharp/service/service.fs

Lines changed: 23 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -955,31 +955,25 @@ type BackgroundCompiler(
955955

956956
member _.ProjectChecked = projectChecked.Publish
957957

958-
member _.ClearCachesAsync (_userOpName) =
959-
async {
960-
return
961-
lock gate (fun () ->
962-
parseCacheLock.AcquireLock (fun ltok ->
963-
checkFileInProjectCache.Clear(ltok)
964-
parseFileCache.Clear(ltok))
965-
incrementalBuildersCache.Clear(AnyCallerThread)
966-
frameworkTcImportsCache.Clear()
967-
scriptClosureCache.Clear (AnyCallerThread)
968-
)
969-
}
958+
member _.ClearCaches() =
959+
lock gate (fun () ->
960+
parseCacheLock.AcquireLock (fun ltok ->
961+
checkFileInProjectCache.Clear(ltok)
962+
parseFileCache.Clear(ltok))
963+
incrementalBuildersCache.Clear(AnyCallerThread)
964+
frameworkTcImportsCache.Clear()
965+
scriptClosureCache.Clear (AnyCallerThread)
966+
)
970967

971-
member _.DownsizeCaches(_userOpName) =
972-
async {
973-
return
974-
lock gate (fun () ->
975-
parseCacheLock.AcquireLock (fun ltok ->
976-
checkFileInProjectCache.Resize(ltok, newKeepStrongly=1)
977-
parseFileCache.Resize(ltok, newKeepStrongly=1))
978-
incrementalBuildersCache.Resize(AnyCallerThread, newKeepStrongly=1, newKeepMax=1)
979-
frameworkTcImportsCache.Downsize()
980-
scriptClosureCache.Resize(AnyCallerThread,newKeepStrongly=1, newKeepMax=1)
981-
)
982-
}
968+
member _.DownsizeCaches() =
969+
lock gate (fun () ->
970+
parseCacheLock.AcquireLock (fun ltok ->
971+
checkFileInProjectCache.Resize(ltok, newKeepStrongly=1)
972+
parseFileCache.Resize(ltok, newKeepStrongly=1))
973+
incrementalBuildersCache.Resize(AnyCallerThread, newKeepStrongly=1, newKeepMax=1)
974+
frameworkTcImportsCache.Downsize()
975+
scriptClosureCache.Resize(AnyCallerThread,newKeepStrongly=1, newKeepMax=1)
976+
)
983977

984978
member _.FrameworkImportsCache = frameworkTcImportsCache
985979

@@ -1195,29 +1189,24 @@ type FSharpChecker(legacyReferenceResolver,
11951189
member ic.InvalidateAll() =
11961190
ic.ClearCaches()
11971191

1198-
member _.ClearCachesAsync(?userOpName: string) =
1192+
member ic.ClearCaches() =
11991193
let utok = AnyCallerThread
1200-
let userOpName = defaultArg userOpName "Unknown"
12011194
braceMatchCache.Clear(utok)
1202-
backgroundCompiler.ClearCachesAsync(userOpName)
1203-
1204-
member ic.ClearCaches(?userOpName) =
1205-
ic.ClearCachesAsync(?userOpName=userOpName) |> Async.Start // this cache clearance is not synchronous, it will happen when the background op gets run
1195+
backgroundCompiler.ClearCaches()
12061196

12071197
member _.CheckMaxMemoryReached() =
12081198
if not maxMemoryReached && System.GC.GetTotalMemory(false) > int64 maxMB * 1024L * 1024L then
12091199
Trace.TraceWarning("!!!!!!!! MAX MEMORY REACHED, DOWNSIZING F# COMPILER CACHES !!!!!!!!!!!!!!!")
12101200
// If the maxMB limit is reached, drastic action is taken
12111201
// - reduce strong cache sizes to a minimum
1212-
let userOpName = "MaxMemoryReached"
12131202
maxMemoryReached <- true
12141203
braceMatchCache.Resize(AnyCallerThread, newKeepStrongly=10)
1215-
backgroundCompiler.DownsizeCaches(userOpName) |> Async.RunSynchronously
1204+
backgroundCompiler.DownsizeCaches()
12161205
maxMemEvent.Trigger( () )
12171206

12181207
// This is for unit testing only
12191208
member ic.ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients() =
1220-
ic.ClearCachesAsync() |> Async.RunSynchronously
1209+
ic.ClearCaches()
12211210
System.GC.Collect()
12221211
System.GC.WaitForPendingFinalizers()
12231212
FxResolver.ClearStaticCaches()
@@ -1229,7 +1218,7 @@ type FSharpChecker(legacyReferenceResolver,
12291218
backgroundCompiler.InvalidateConfiguration(options, userOpName)
12301219

12311220
/// Clear the internal cache of the given projects.
1232-
member _.ClearCache(options: FSharpProjectOptions seq, ?userOpName: string) =
1221+
member _.ClearCache(options: seq<FSharpProjectOptions>, ?userOpName: string) =
12331222
let userOpName = defaultArg userOpName "Unknown"
12341223
backgroundCompiler.ClearCache(options, userOpName)
12351224

tests/FSharp.Compiler.UnitTests/BuildGraphTests.fs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ module BuildGraphTests =
7171

7272
let work = Async.Parallel(Array.init requests (fun _ -> graphNode.GetOrComputeValue() |> Async.AwaitNodeCode))
7373

74-
Async.RunSynchronously(work)
74+
Async.RunImmediate(work)
7575
|> ignore
7676

7777
Assert.shouldBe 1 computationCount
@@ -84,7 +84,7 @@ module BuildGraphTests =
8484

8585
let work = Async.Parallel(Array.init requests (fun _ -> graphNode.GetOrComputeValue() |> Async.AwaitNodeCode))
8686

87-
let result = Async.RunSynchronously(work)
87+
let result = Async.RunImmediate(work)
8888

8989
Assert.shouldNotBeEmpty result
9090
Assert.shouldBe requests result.Length
@@ -116,7 +116,7 @@ module BuildGraphTests =
116116

117117
Assert.shouldBeTrue weak.IsAlive
118118

119-
Async.RunSynchronously(Async.Parallel(Array.init requests (fun _ -> graphNode.GetOrComputeValue() |> Async.AwaitNodeCode)))
119+
Async.RunImmediate(Async.Parallel(Array.init requests (fun _ -> graphNode.GetOrComputeValue() |> Async.AwaitNodeCode)))
120120
|> ignore
121121

122122
GC.Collect(2, GCCollectionMode.Forced, true)

tests/FSharp.Test.Utilities/CompilerAssert.fs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ type CompilerAssert private () =
132132
options
133133
|> Array.append defaultProjectOptions.OtherOptions
134134
|> Array.append [| "fsc.dll"; inputFilePath; "-o:" + outputFilePath; (if isExe then "--target:exe" else "--target:library"); "--nowin32manifest" |]
135-
let errors, _ = checker.Compile args |> Async.RunSynchronously
135+
let errors, _ = checker.Compile args |> Async.RunImmediate
136136
errors, outputFilePath
137137

138138
static let compileAux isExe options source f : unit =
@@ -397,7 +397,7 @@ type CompilerAssert private () =
397397

398398
let parseResults =
399399
checker.ParseFile("test.fs", SourceText.ofString source, parseOptions)
400-
|> Async.RunSynchronously
400+
|> Async.RunImmediate
401401

402402
Assert.IsEmpty(parseResults.Diagnostics, sprintf "Parse errors: %A" parseResults.Diagnostics)
403403

@@ -410,7 +410,7 @@ type CompilerAssert private () =
410410

411411
let compileErrors, statusCode =
412412
checker.Compile([parseResults.ParseTree], "test", outputFilePath, dependencies, executable = isExe, noframework = true)
413-
|> Async.RunSynchronously
413+
|> Async.RunImmediate
414414

415415
Assert.IsEmpty(compileErrors, sprintf "Compile errors: %A" compileErrors)
416416
Assert.AreEqual(0, statusCode, sprintf "Nonzero status code: %d" statusCode)
@@ -421,7 +421,7 @@ type CompilerAssert private () =
421421
let parseOptions = { FSharpParsingOptions.Default with SourceFiles = [|"test.fs"|] }
422422
let parseResults =
423423
checker.ParseFile("test.fs", SourceText.ofString source, parseOptions)
424-
|> Async.RunSynchronously
424+
|> Async.RunImmediate
425425

426426
Assert.IsEmpty(parseResults.Diagnostics, sprintf "Parse errors: %A" parseResults.Diagnostics)
427427

@@ -434,15 +434,15 @@ type CompilerAssert private () =
434434

435435
let compileErrors, statusCode, assembly =
436436
checker.CompileToDynamicAssembly([parseResults.ParseTree], assemblyName, dependencies, None, noframework = true)
437-
|> Async.RunSynchronously
437+
|> Async.RunImmediate
438438

439439
Assert.IsEmpty(compileErrors, sprintf "Compile errors: %A" compileErrors)
440440
Assert.AreEqual(0, statusCode, sprintf "Nonzero status code: %d" statusCode)
441441
Assert.IsTrue(assembly.IsSome, "no assembly returned")
442442
Option.get assembly
443443

444444
static member Pass (source: string) =
445-
let parseResults, fileAnswer = checker.ParseAndCheckFileInProject("test.fs", 0, SourceText.ofString source, defaultProjectOptions) |> Async.RunSynchronously
445+
let parseResults, fileAnswer = checker.ParseAndCheckFileInProject("test.fs", 0, SourceText.ofString source, defaultProjectOptions) |> Async.RunImmediate
446446

447447
Assert.IsEmpty(parseResults.Diagnostics, sprintf "Parse errors: %A" parseResults.Diagnostics)
448448

@@ -455,7 +455,7 @@ type CompilerAssert private () =
455455
static member PassWithOptions options (source: string) =
456456
let options = { defaultProjectOptions with OtherOptions = Array.append options defaultProjectOptions.OtherOptions}
457457

458-
let parseResults, fileAnswer = checker.ParseAndCheckFileInProject("test.fs", 0, SourceText.ofString source, options) |> Async.RunSynchronously
458+
let parseResults, fileAnswer = checker.ParseAndCheckFileInProject("test.fs", 0, SourceText.ofString source, options) |> Async.RunImmediate
459459

460460
Assert.IsEmpty(parseResults.Diagnostics, sprintf "Parse errors: %A" parseResults.Diagnostics)
461461

@@ -473,7 +473,7 @@ type CompilerAssert private () =
473473
0,
474474
SourceText.ofString (File.ReadAllText absoluteSourceFile),
475475
{ defaultProjectOptions with OtherOptions = Array.append options defaultProjectOptions.OtherOptions; SourceFiles = [|sourceFile|] })
476-
|> Async.RunSynchronously
476+
|> Async.RunImmediate
477477

478478
Assert.IsEmpty(parseResults.Diagnostics, sprintf "Parse errors: %A" parseResults.Diagnostics)
479479

@@ -503,7 +503,7 @@ type CompilerAssert private () =
503503
0,
504504
SourceText.ofString source,
505505
{ defaultProjectOptions with OtherOptions = Array.append options defaultProjectOptions.OtherOptions; SourceFiles = [|name|] })
506-
|> Async.RunSynchronously
506+
|> Async.RunImmediate
507507

508508
if parseResults.Diagnostics.Length > 0 then
509509
parseResults.Diagnostics
@@ -523,7 +523,7 @@ type CompilerAssert private () =
523523
0,
524524
SourceText.ofString source,
525525
{ defaultProjectOptions with OtherOptions = Array.append options defaultProjectOptions.OtherOptions})
526-
|> Async.RunSynchronously
526+
|> Async.RunImmediate
527527

528528
if parseResults.Diagnostics.Length > 0 then
529529
parseResults.Diagnostics
@@ -543,7 +543,7 @@ type CompilerAssert private () =
543543
0,
544544
SourceText.ofString source,
545545
{ defaultProjectOptions with OtherOptions = Array.append options defaultProjectOptions.OtherOptions})
546-
|> Async.RunSynchronously
546+
|> Async.RunImmediate
547547

548548
match fileAnswer with
549549
| FSharpCheckFileAnswer.Aborted _ -> Assert.Fail("Type Checker Aborted"); failwith "Type Checker Aborted"
@@ -565,7 +565,7 @@ type CompilerAssert private () =
565565
0,
566566
SourceText.ofString source,
567567
{ defaultProjectOptions with OtherOptions = Array.append options defaultProjectOptions.OtherOptions})
568-
|> Async.RunSynchronously
568+
|> Async.RunImmediate
569569

570570
if parseResults.Diagnostics.Length > 0 then
571571
parseResults.Diagnostics
@@ -669,7 +669,7 @@ type CompilerAssert private () =
669669
static member Parse (source: string) =
670670
let sourceFileName = "test.fs"
671671
let parsingOptions = { FSharpParsingOptions.Default with SourceFiles = [| sourceFileName |] }
672-
checker.ParseFile(sourceFileName, SourceText.ofString source, parsingOptions) |> Async.RunSynchronously
672+
checker.ParseFile(sourceFileName, SourceText.ofString source, parsingOptions) |> Async.RunImmediate
673673

674674
static member ParseWithErrors (source: string) expectedParseErrors =
675675
let parseResults = CompilerAssert.Parse source

tests/FSharp.Test.Utilities/Utilities.fs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ open System
66
open System.IO
77
open System.Reflection
88
open System.Collections.Immutable
9+
open System.Diagnostics
10+
open System.Threading.Tasks
911
open Microsoft.CodeAnalysis
1012
open Microsoft.CodeAnalysis.CSharp
11-
open System.Diagnostics
1213
open FSharp.Test.Utilities
1314
open TestFramework
1415
open NUnit.Framework
@@ -17,6 +18,19 @@ open NUnit.Framework
1718

1819
module Utilities =
1920

21+
type Async with
22+
static member RunImmediate (computation: Async<'T>, ?cancellationToken ) =
23+
let cancellationToken = defaultArg cancellationToken Async.DefaultCancellationToken
24+
let ts = TaskCompletionSource<'T>()
25+
let task = ts.Task
26+
Async.StartWithContinuations(
27+
computation,
28+
(fun k -> ts.SetResult k),
29+
(fun exn -> ts.SetException exn),
30+
(fun _ -> ts.SetCanceled()),
31+
cancellationToken)
32+
task.Result
33+
2034
[<RequireQualifiedAccess>]
2135
type TargetFramework =
2236
| NetStandard20

0 commit comments

Comments
 (0)