Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/fsharp/absil/illib.fs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ open System.Collections.Concurrent
open System.Diagnostics
open System.IO
open System.Threading
open System.Threading.Tasks
open System.Runtime.CompilerServices

[<AutoOpen>]
Expand Down Expand Up @@ -86,6 +87,19 @@ module internal PervasiveAutoOpens =

let notFound() = raise (KeyNotFoundException())

type Async with
static member RunImmediate (computation: Async<'T>, ?cancellationToken ) =
let cancellationToken = defaultArg cancellationToken Async.DefaultCancellationToken
let ts = TaskCompletionSource<'T>()
let task = ts.Task
Async.StartWithContinuations(
computation,
(fun k -> ts.SetResult k),
(fun exn -> ts.SetException exn),
(fun _ -> ts.SetCanceled()),
cancellationToken)
task.Result

[<Struct>]
/// An efficient lazy for inline storage in a class type. Results in fewer thunks.
type InlineDelayInit<'T when 'T : not struct> =
Expand Down
4 changes: 4 additions & 0 deletions src/fsharp/absil/illib.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ module internal PervasiveAutoOpens =

member inline EndsWithOrdinal: value:string -> bool

type Async with
/// Runs the computation synchronously, always starting on the current thread.
static member RunImmediate: computation: Async<'T> * ?cancellationToken: CancellationToken -> 'T

val foldOn: p:('a -> 'b) -> f:('c -> 'b -> 'd) -> z:'c -> x:'a -> 'd

val notFound: unit -> 'a
Expand Down
57 changes: 23 additions & 34 deletions src/fsharp/service/service.fs
Original file line number Diff line number Diff line change
Expand Up @@ -955,31 +955,25 @@ type BackgroundCompiler(

member _.ProjectChecked = projectChecked.Publish

member _.ClearCachesAsync (_userOpName) =
async {
return
lock gate (fun () ->
parseCacheLock.AcquireLock (fun ltok ->
checkFileInProjectCache.Clear(ltok)
parseFileCache.Clear(ltok))
incrementalBuildersCache.Clear(AnyCallerThread)
frameworkTcImportsCache.Clear()
scriptClosureCache.Clear (AnyCallerThread)
)
}
member _.ClearCaches() =
lock gate (fun () ->
parseCacheLock.AcquireLock (fun ltok ->
checkFileInProjectCache.Clear(ltok)
parseFileCache.Clear(ltok))
incrementalBuildersCache.Clear(AnyCallerThread)
frameworkTcImportsCache.Clear()
scriptClosureCache.Clear (AnyCallerThread)
)

member _.DownsizeCaches(_userOpName) =
async {
return
lock gate (fun () ->
parseCacheLock.AcquireLock (fun ltok ->
checkFileInProjectCache.Resize(ltok, newKeepStrongly=1)
parseFileCache.Resize(ltok, newKeepStrongly=1))
incrementalBuildersCache.Resize(AnyCallerThread, newKeepStrongly=1, newKeepMax=1)
frameworkTcImportsCache.Downsize()
scriptClosureCache.Resize(AnyCallerThread,newKeepStrongly=1, newKeepMax=1)
)
}
member _.DownsizeCaches() =
lock gate (fun () ->
parseCacheLock.AcquireLock (fun ltok ->
checkFileInProjectCache.Resize(ltok, newKeepStrongly=1)
parseFileCache.Resize(ltok, newKeepStrongly=1))
incrementalBuildersCache.Resize(AnyCallerThread, newKeepStrongly=1, newKeepMax=1)
frameworkTcImportsCache.Downsize()
scriptClosureCache.Resize(AnyCallerThread,newKeepStrongly=1, newKeepMax=1)
)

member _.FrameworkImportsCache = frameworkTcImportsCache

Expand Down Expand Up @@ -1195,29 +1189,24 @@ type FSharpChecker(legacyReferenceResolver,
member ic.InvalidateAll() =
ic.ClearCaches()

member _.ClearCachesAsync(?userOpName: string) =
member ic.ClearCaches() =
let utok = AnyCallerThread
let userOpName = defaultArg userOpName "Unknown"
braceMatchCache.Clear(utok)
backgroundCompiler.ClearCachesAsync(userOpName)

member ic.ClearCaches(?userOpName) =
ic.ClearCachesAsync(?userOpName=userOpName) |> Async.Start // this cache clearance is not synchronous, it will happen when the background op gets run
backgroundCompiler.ClearCaches()

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

// This is for unit testing only
member ic.ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients() =
ic.ClearCachesAsync() |> Async.RunSynchronously
ic.ClearCaches()
System.GC.Collect()
System.GC.WaitForPendingFinalizers()
FxResolver.ClearStaticCaches()
Expand All @@ -1229,7 +1218,7 @@ type FSharpChecker(legacyReferenceResolver,
backgroundCompiler.InvalidateConfiguration(options, userOpName)

/// Clear the internal cache of the given projects.
member _.ClearCache(options: FSharpProjectOptions seq, ?userOpName: string) =
member _.ClearCache(options: seq<FSharpProjectOptions>, ?userOpName: string) =
let userOpName = defaultArg userOpName "Unknown"
backgroundCompiler.ClearCache(options, userOpName)

Expand Down
6 changes: 3 additions & 3 deletions tests/FSharp.Compiler.UnitTests/BuildGraphTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ module BuildGraphTests =

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

Async.RunSynchronously(work)
Async.RunImmediate(work)
|> ignore

Assert.shouldBe 1 computationCount
Expand All @@ -84,7 +84,7 @@ module BuildGraphTests =

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

let result = Async.RunSynchronously(work)
let result = Async.RunImmediate(work)

Assert.shouldNotBeEmpty result
Assert.shouldBe requests result.Length
Expand Down Expand Up @@ -116,7 +116,7 @@ module BuildGraphTests =

Assert.shouldBeTrue weak.IsAlive

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

GC.Collect(2, GCCollectionMode.Forced, true)
Expand Down
26 changes: 13 additions & 13 deletions tests/FSharp.Test.Utilities/CompilerAssert.fs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ type CompilerAssert private () =
options
|> Array.append defaultProjectOptions.OtherOptions
|> Array.append [| "fsc.dll"; inputFilePath; "-o:" + outputFilePath; (if isExe then "--target:exe" else "--target:library"); "--nowin32manifest" |]
let errors, _ = checker.Compile args |> Async.RunSynchronously
let errors, _ = checker.Compile args |> Async.RunImmediate
errors, outputFilePath

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

let parseResults =
checker.ParseFile("test.fs", SourceText.ofString source, parseOptions)
|> Async.RunSynchronously
|> Async.RunImmediate

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

if parseResults.Diagnostics.Length > 0 then
parseResults.Diagnostics
Expand All @@ -523,7 +523,7 @@ type CompilerAssert private () =
0,
SourceText.ofString source,
{ defaultProjectOptions with OtherOptions = Array.append options defaultProjectOptions.OtherOptions})
|> Async.RunSynchronously
|> Async.RunImmediate

if parseResults.Diagnostics.Length > 0 then
parseResults.Diagnostics
Expand All @@ -543,7 +543,7 @@ type CompilerAssert private () =
0,
SourceText.ofString source,
{ defaultProjectOptions with OtherOptions = Array.append options defaultProjectOptions.OtherOptions})
|> Async.RunSynchronously
|> Async.RunImmediate

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

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

static member ParseWithErrors (source: string) expectedParseErrors =
let parseResults = CompilerAssert.Parse source
Expand Down
16 changes: 15 additions & 1 deletion tests/FSharp.Test.Utilities/Utilities.fs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ open System
open System.IO
open System.Reflection
open System.Collections.Immutable
open System.Diagnostics
open System.Threading.Tasks
open Microsoft.CodeAnalysis
open Microsoft.CodeAnalysis.CSharp
open System.Diagnostics
open FSharp.Test.Utilities
open TestFramework
open NUnit.Framework
Expand All @@ -17,6 +18,19 @@ open NUnit.Framework

module Utilities =

type Async with
static member RunImmediate (computation: Async<'T>, ?cancellationToken ) =
Copy link
Member

Choose a reason for hiding this comment

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

Shall we consolidate this one with the default one?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The default one is not public (not added to FSharp.Core)

let cancellationToken = defaultArg cancellationToken Async.DefaultCancellationToken
let ts = TaskCompletionSource<'T>()
let task = ts.Task
Async.StartWithContinuations(
computation,
(fun k -> ts.SetResult k),
(fun exn -> ts.SetException exn),
(fun _ -> ts.SetCanceled()),
cancellationToken)
task.Result

[<RequireQualifiedAccess>]
type TargetFramework =
| NetStandard20
Expand Down
Loading