Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incremental builder with async lazy evaluation / Reactor removal #11573

Merged
merged 146 commits into from
Jun 7, 2021

Conversation

TIHan
Copy link
Contributor

@TIHan TIHan commented May 15, 2021

The basic idea behind this is to make each file in the dependency graph within incremental builder be asynchronously lazily evaluated; meaning many calls to the same evaluation of a file will not block threads.

  • Removes the reactor thread entirely.
  • Removes implicit background building.
  • Removes the eventually builder.
  • Adds BuildGraph.

Majority of these changes do not change the signature of the public APIs, but may change the behavior slightly due to removing the implicit background building; it's more predictable that way.

This should also resolve:
#11569

Copy link
Contributor

@dsyme dsyme left a comment

Choose a reason for hiding this comment

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

Notes from deep review and co-working with @TIHan

static member AwaitTask(task: Task) =
Node(wrapThreadStaticInfo(Async.AwaitTask task))

static member AwaitWaitHandle(waitHandle: WaitHandle) =
Copy link
Contributor

Choose a reason for hiding this comment

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

This is only for test purposes - label it as such or rename to AwaitWaitHandleForTesting


static member RunImmediate : computation: NodeCode<'T> * ?ct: CancellationToken -> 'T

static member StartAsTask : computation: NodeCode<'T> * ?ct: CancellationToken -> Task<'T>
Copy link
Contributor

Choose a reason for hiding this comment

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

Rename to StartAsTask_ForTesting

[<AbstractClass;Sealed>]
type NodeCode =

static member RunImmediate : computation: NodeCode<'T> * ?ct: CancellationToken -> 'T
Copy link
Contributor

Choose a reason for hiding this comment

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

At each callsite ct is None.

My preference: remove the cancellation token argument and rename to RunImmediateWithoutCancellation

Certainly make the ct argument non-optional as it's relaly important to know where we have non-cancellable code


static member Sequential : computations: NodeCode<'T> seq -> NodeCode<'T []>

static member AwaitWaitHandle : waitHandle: WaitHandle -> NodeCode<bool>
Copy link
Contributor

Choose a reason for hiding this comment

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

Rename to AwaitWaitHandle_ForTesting


member GetValue: unit -> NodeCode<'T>

member TryGetValue: unit -> 'T voption
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe rename to TryPeekValue, since no compute is done

/// By default, 'retryCompute' is 'true'.
new : computation: NodeCode<'T> -> GraphNode<'T>

member GetValue: unit -> NodeCode<'T>
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe make the computational aspect of this clearer in the name, e.g. ComputeValue or GetOrComputeValue

RequireCompilationThread ctok // we don't want to do assembly resolution concurrently, we assume MSBuild doesn't handle this
TcConfig.TryResolveLibsUsingMSBuildRules (tcConfig, assemblyList, rangeStartup, ResolveAssemblyReferenceMode.ReportErrors)
// we don't want to do assembly resolution concurrently, we assume MSBuild doesn't handle this
lock assemblyResolutionGate (fun () ->
Copy link
Contributor

Choose a reason for hiding this comment

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

Here the ultimate need for a lock is the call to tcConfig.legacyReferenceResolver.Impl.Resolve

See comment above. Agreed this looks like a reasonable place to acquire AssemblyResolutionLockToken


static member BuildFrameworkTcImports (tcConfigP: TcConfigProvider, frameworkDLLs, nonFrameworkDLLs) =
node {
let ctok = CompilationThreadToken()
Copy link
Contributor

Choose a reason for hiding this comment

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

Change to AssumeCompilationThreadWithoutEvidence()

@@ -257,7 +257,7 @@ type public FSharpChecker =
/// <param name="filename">The filename for the file.</param>
/// <param name="options">The options for the project or script, used to determine active --define conditionals and other options relevant to parsing.</param>
/// <param name="userOpName">An optional string used for tracing compiler operations associated with this request.</param>
member GetBackgroundCheckResultsForFileInProject: filename: string * options: FSharpProjectOptions * ?userOpName: string -> Async<FSharpParseFileResults * FSharpCheckFileResults>
member GetBackgroundCheckResultsForFileInProject: filename: string * options: FSharpProjectOptions * ?userOpName: string -> Async<(FSharpParseFileResults * FSharpCheckFileResults) option>
Copy link
Contributor

Choose a reason for hiding this comment

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

We discussed this - no need to catch OperationCanceledException, no need to return options

@TIHan
Copy link
Contributor Author

TIHan commented Jun 7, 2021

Great, it passes. :)

Copy link
Contributor

@dsyme dsyme left a comment

Choose a reason for hiding this comment

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

This is great, I did a careful third online review with @TIHan and we added documentation and made some fixes. There are things to be done as follow up

  1. Using the "save" trigger for scripts is not enough - we should request possible reanalysis each time the script comes into focus, otherwise red-squigglies will not be cleared after background file changes unless the user actually edits and saves. Have sent a repro to Will, he'll do this separately

  2. When it's in main we can do InlineIfLambda on the 'Cancellable' methods like 'bind'

  3. Check some FCS callstacks in the debugger (post hoc is ok)

  4. Document the API changes in the FCS release notes, particularly the removal of the background build

@TIHan TIHan merged commit 200af47 into dotnet:main Jun 7, 2021
@TIHan TIHan mentioned this pull request Aug 16, 2021
10 tasks
nosami pushed a commit to xamarin/visualfsharp that referenced this pull request Jan 26, 2022
…net#11573)

* Adding AsyncLazy

* Trying to get rid of cancellable and eventually use in incrementalbuilder

* remove a lot of use of eventually and cancellable in incremental build

* trying

* More refactoring

* drastrically less use of ctok

* fixing build

* Using reactor for foreground checking

* more cleanup

* more cleanup

* Properly set a background build

* fixing build

* fixing build

* trying to fix it

* Added Seq.foldAsync

* Changes to return 'option' on some service APIs

* fixing tests

* fixing surface area test

* reseting lazy async

* minor name change

* Trying to cleanup more incremental builder stuff

* fixing a few bugs

* trying to fix tests

* More cleanup

* fixing tests

* Fixing build

* comment updates and adding a finalize

* Minor comment update

* Using run synchronously so it can perform cancellation

* cleanup

* need to reply

* Reverting back to the previous AsyncLazy behavior

* Cleaning up asynclazy

* More comment updates

* Remove open

* Adding constraint

* Allow setting cultureinfo for async lazy

* Minor update

* re-using compilation globals scope correctly

* Added foregroundCheckFileCacheAgent

* Fixing deadlock

* Minor fix

* An incremental build will not invalidate itself if a reference has changed

* Added 'autoInvalidateConfiguration' option to FSharpChecker. Added 'IsProjectInvalidated' as a service API.

* Fixing builder

* Fixing up-to-date check

* Update surface area. Renamed IsProjectInvalidated to IsProjectReferencesInvalidated

* fixing build

* Trying to fix tests

* skip test

* minor cleanup

* Updating surface area. Minor cleanup

* Update surface area

* Use the parseLock instead of a mailbox

* Added AsyncLazy.fs and AsyncLazyTests.fs. Combined AsyncLazyWeak and AsyncLazy into one

* More cleanup

* Trying to minimize diff

* Trying to minimize diff

* Removing ctok in more places

* Updating baseline. Removing Reactor.fs.

* Added a comment

* More cleanup

* Fixing build

* Fixing build

* Fixing build

* Updated baseline

* Remove lock

* Fixing tests

* Trying to fix tests

* Fixing tests

* Revert that

* Fixing tests

* Fixing

* Minor format change

* Fixing tests

* no gc.collect

* Trying to fix tests

* Trying to fix tests

* Using logical

* trying to fix tests

* updating test sdk

* Fixing tests. Reverting back test sdk

* Fixing tests

* Fixing tests

* Fixing test

* TcImports uses Async instead of Cancellable now

* FrameworkImportCache is now will ensure it only computes once for key

* Removing some dependency on ctok

* Removed lock in compiler assert

* Added AsyncErrorLogger

* More work on error logger

* more error logger work

* Fixed build

* Should be partly working

* Cleaning up AsyncErrorLogger

* minor change

* minor fix for debug

* Using assemblyResolutionGate to lock

* fixing async lazy tests

* do not make these type provider tests parallel

* do not make these type provider tests parallel

* make more tests non-parallelizable

* Fixing test

* Use getOrCreateBuilder

* Make type provider tests non-parallel

* Allow cancellation exception to be thrown when evaluating raw contents

* Refactored AsyncLazy. Added GraphNode and LazyGraphNode.

* Fixing GraphNode

* Fixing tests. Adding stack trace info.

* fixing build

* using string

* Reactoring build graph

* More refactoring

* Fixing build

* Removing another mutable state

* Bigger buffer time in tests

* Ported AsyncLazy from Roslyn

* Fixing build

* Removed RequestCount

* Removed Roslyn AsyncLazy

* Skipping tests temporarily until linux passes. GraphNode now will retry by default.

* slowly re-enable tests

* Re-enable build graph tests

* Remove Thread.Sleep in tests. Add timeout exceptions

* TcImports locking, use lock/token methodology, remove Enventually, renaming from code review

* fix build

* fix build

* Fixing build

* Removed API changes that are returning 'option' in FCS

* Allow single background check for a script

* Fixing build

* Fixing build

* Better handling of scripts

* fix taking semaphore atomically

* comment

* Check for task completed

* Using explicit continuation options

* use underscore

Co-authored-by: Don Syme <donsyme@fastmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants