Skip to content

Commit 022046a

Browse files
committed
Merge pull request #445 from dsyme/fix-mem3
Fix caching, rewrite FCS reactor queue
2 parents 66573c6 + 46ec963 commit 022046a

File tree

13 files changed

+423
-422
lines changed

13 files changed

+423
-422
lines changed

RELEASE_NOTES.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
#### 1.4.1 -
2+
* Add pause before backgrounnd work starts. The FCS request queue must be empty for 1 second before work will start
3+
* Write trace information about the reactor queue to the event log
4+
* Rewrite reactor to consistently prioritize queued work
5+
* Implement cancellation for queued work if it is cancelled prior to being executed
6+
* Adjust caching to check cache correctly if there is a gap before the request is executed
7+
18
#### 1.4.0.9 -
29
* FSharpType.Format fix
310
* Disable maximum-memory trigger by default until use case ironed out

docs/content/queue.fsx

Lines changed: 30 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,22 @@ Compiler Services: Notes on the FSharpChecker operations queue
66
77
This is a design note on the FSharpChecker component and its operations queue. See also the notes on the [FSharpChecker caches](caches.html)
88
9-
FSharpChecker maintains an operations queue. Items from the FSharpChecker operations queue are currently processed
9+
FSharpChecker maintains an operations queue. Items from the FSharpChecker operations queue are processed
1010
sequentially and in order.
1111
12-
In addition to operations in the queue, there is also a low-priority, interleaved background operation
13-
to bring the checking of the checking of "the current background project" up-to-date. When the queue is empty
14-
this operation is conducted in small incremental fragments of parse/check work (cooperatively time-sliced to be approximately <50ms,
15-
see `maxTimeShareMilliseconds` in IncrementalBuild.fs). For a working definition of "current background project"
16-
you currently have to see the calls to `StartBackgroundCompile`
17-
in [service.fs](https://github.com/fsharp/FSharp.Compiler.Service/blob/master/src/fsharp/vs/service.fs),
18-
where an API call which in turn calls StartBackgroundCompile indicates that the current background project has been specified.
12+
The thread processing these requests can also run a low-priority, interleaved background operation when the
13+
queue is empty. This can be used to implicitly bring the background check of a project "up-to-date".
14+
When the operations queue has been empty for 1 second,
15+
this background work is run in small incremental fragments. This work is cooperatively time-sliced to be approximately <50ms, (see `maxTimeShareMilliseconds` in
16+
IncrementalBuild.fs). The project to be checked in the background is set implicitly
17+
by calls to ``CheckFileInProject`` and ``ParseAndCheckFileInProject``.
18+
To disable implicit background checking completely, set ``checker.ImplicitlyStartBackgroundWork`` to false.
19+
To change the time before background work starts, set ``checker.PauseBeforeBackgroundWork`` to the required number of milliseconds.
1920
20-
Many calls to the FSharpChecker API enqueue an operation in the FSharpChecker compiler queue. These correspond to the
21+
Most calls to the FSharpChecker API enqueue an operation in the FSharpChecker compiler queue. These correspond to the
2122
calls to EnqueueAndAwaitOpAsync in [service.fs](https://github.com/fsharp/FSharp.Compiler.Service/blob/master/src/fsharp/vs/service.fs).
2223
23-
* For example, calling `ParseAndCheckProject` enqueues a `ParseAndCheckProjectImpl` operation. The length of the
24+
* For example, calling `ParseAndCheckProject` enqueues a `ParseAndCheckProjectImpl` operation. The time taken for the
2425
operation will depend on how much work is required to bring the project analysis up-to-date.
2526
2627
* Likewise, calling any of `GetUsesOfSymbol`, `GetAllUsesOfAllSymbols`, `ParseFileInProject`,
@@ -30,30 +31,25 @@ calls to EnqueueAndAwaitOpAsync in [service.fs](https://github.com/fsharp/FSharp
3031
vary - many will be very fast - but they won't be processed until other operations already in the queue are complete.
3132
3233
Some operations do not enqueue anything on the FSharpChecker operations queue - notably any accesses to the Symbol APIs.
33-
These are multi-threaded access to the TAST data produced by other FSharpChecker operations.
34-
35-
In advanced interactive scenarios you may need to consider contributing design changes and extensions to how the FSharpChecker queue works,
36-
in order to better serve your needs. Some tools throw a lot of interactive work at the FSharpChecker operations queue.
37-
If you are writing such a component, we encourage you to get more involved in making carefully considered
38-
changes, cleanup and documentation to how the queue works, to ensure it is suitable for your needs.
39-
40-
For example, from the FCS point of view, it is plausible to consider changing of extending how the
41-
FSharpChecker operations queue works - the design of the queue and its processing is not sacred.
42-
In theory you could submit extensions and documentation that allow prioritization of operations, or interleaving of
43-
operations, or cancellation of operations, or more explicitness about the "current background project", and so on.
44-
45-
Finally, for those writing interactive editors which use FCS, in the current state of affairs you
46-
should be cautious about having any auto-operation (e.g. operations like "Find Unused Declarations"
47-
which run automatically and are unassociated with a user action, but not operations like "Find All References"
48-
which a user explicitly triggers) request a check of the entire project. That will cause very long operations
49-
and contention on the FSharpChecker operations queue. Any such very-long-running request should normally be associated
50-
with a user action, and preferably it should be cancellable.
51-
52-
Alternatively, it is reasonable to trigger these
53-
kinds of auto-operations when the ProjectChecked event is triggered on FSharpChecker. This event indicates the
54-
completion of interleaved checking of the "current background project" (specified above). Again, if
55-
the spec of "current background project" is not suitable or sufficiently controlled for your needs, please
56-
help to document it and adjust it.
34+
These use cross-threaded access to the TAST data produced by other FSharpChecker operations.
35+
36+
Some tools throw a lot of interactive work at the FSharpChecker operations queue.
37+
If you are writing such a component, consider running your project against a debug build
38+
of FSharp.Compiler.Service.dll to see the Trace.WriteInformation messages indicating the length of the
39+
operations queuea and the time to process requests.
40+
41+
For those writing interactive editors which use FCS, you
42+
should be cautious about operations that request a check of the entire project.
43+
For example, be careful about requesting the check of an entire project
44+
on operations like "Highlight Symbol" or "Find Unused Declarations"
45+
(which run automatically when the user opens a file or moves the cursor).
46+
as opposed to operations like "Find All References" (which a user explicitly triggers).
47+
Project checking can cause long and contention on the FSharpChecker operations queue.
48+
49+
Requests to FCS can be cancelled by cancelling the async operation. (Some requests also
50+
include additional callbacks which can be used to indicate a cancellation condition).
51+
This cancellation will be effective if the cancellation is performed before the operation
52+
is executed in the operations queue.
5753
5854
Summary
5955
-------

src/fsharp/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
<DefineConstants>$(DefineConstants);COMPILER</DefineConstants>
4040
<DefineConstants>$(DefineConstants);EXTENSIONTYPING</DefineConstants>
4141
<DefineConstants>$(DefineConstants);NO_STRONG_NAMES</DefineConstants>
42+
<DefineConstants>$(DefineConstants);TRACE</DefineConstants>
4243
<TargetFSharpCoreVersion>4.3.0.0</TargetFSharpCoreVersion>
4344
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\..\</SolutionDir>
4445
<TargetFrameworkProfile />

src/fsharp/fsc.fs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -615,15 +615,15 @@ type ILResource with
615615
| ILResourceLocation.Local b -> b()
616616
| _-> error(InternalError("Bytes",rangeStartup))
617617

618-
let EncodeInterfaceData(tcConfig:TcConfig,tcGlobals,exportRemapping,generatedCcu,outfile) =
618+
let EncodeInterfaceData(tcConfig:TcConfig,tcGlobals,exportRemapping,generatedCcu,outfile,isIncrementalBuild) =
619619
if GenerateInterfaceData(tcConfig) then
620620
if verbose then dprintfn "Generating interface data attribute...";
621621
let resource = WriteSignatureData (tcConfig,tcGlobals,exportRemapping,generatedCcu,outfile)
622622
if verbose then dprintf "Generated interface data attribute!\n";
623623
// REVIEW: need a better test for this
624624
let outFileNoExtension = Filename.chopExtension outfile
625625
let isCompilerServiceDll = outFileNoExtension.Contains("FSharp.LanguageService.Compiler")
626-
if tcConfig.useOptimizationDataFile || tcGlobals.compilingFslib || isCompilerServiceDll then
626+
if (tcConfig.useOptimizationDataFile || tcGlobals.compilingFslib || isCompilerServiceDll) && not isIncrementalBuild then
627627
let sigDataFileName = (Filename.chopExtension outfile)+".sigdata"
628628
File.WriteAllBytes(sigDataFileName,resource.Bytes);
629629
let sigAttr = mkSignatureDataVersionAttr tcGlobals (IL.parseILVersion Internal.Utilities.FSharpEnvironment.FSharpBinaryMetadataFormatRevision)
@@ -1917,7 +1917,7 @@ let main2(Args(tcConfig, tcImports, frameworkTcImports: TcImports, tcGlobals, er
19171917

19181918
let sigDataAttributes,sigDataResources =
19191919
try
1920-
EncodeInterfaceData(tcConfig, tcGlobals, exportRemapping, generatedCcu, outfile)
1920+
EncodeInterfaceData(tcConfig, tcGlobals, exportRemapping, generatedCcu, outfile, false)
19211921
with e ->
19221922
errorRecoveryNoRange e
19231923
SqmLoggerWithConfig tcConfig errorLogger.ErrorNumbers errorLogger.WarningNumbers

src/fsharp/fsc.fsi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ type ErrorLoggerProvider =
2121

2222
type SigningInfo = SigningInfo of (* delaysign:*) bool * (*signer:*) string option * (*container:*) string option
2323

24-
val EncodeInterfaceData: tcConfig:TcConfig * tcGlobals:TcGlobals * exportRemapping:Tastops.Remap * generatedCcu: Tast.CcuThunk * outfile: string -> ILAttribute list * ILResource list
24+
val EncodeInterfaceData: tcConfig:TcConfig * tcGlobals:TcGlobals * exportRemapping:Tastops.Remap * generatedCcu: Tast.CcuThunk * outfile: string * isIncrementalBuild: bool -> ILAttribute list * ILResource list
2525
val ValidateKeySigningAttributes : tcConfig:TcConfig * tcGlobals:TcGlobals * TypeChecker.TopAttribs -> SigningInfo
2626
val GetSigner : SigningInfo -> ILBinaryWriter.ILStrongNameSigner option
2727

src/fsharp/vs/IncrementalBuild.fs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1080,7 +1080,7 @@ module internal IncrementalFSharpBuild =
10801080
#if SILVERLIGHT
10811081
50L
10821082
#else
1083-
match System.Environment.GetEnvironmentVariable("mFSharp_MaxTimeShare") with
1083+
match System.Environment.GetEnvironmentVariable("FCS_MaxTimeShare") with
10841084
| null | "" -> 50L
10851085
| s -> int64 s
10861086
#endif
@@ -1500,7 +1500,7 @@ module internal IncrementalFSharpBuild =
15001500
let exportRemapping = MakeExportRemapping generatedCcu generatedCcu.Contents
15011501

15021502
let sigData =
1503-
let _sigDataAttributes,sigDataResources = Driver.EncodeInterfaceData(tcConfig,tcGlobals,exportRemapping,generatedCcu,outfile)
1503+
let _sigDataAttributes,sigDataResources = Driver.EncodeInterfaceData(tcConfig,tcGlobals,exportRemapping,generatedCcu,outfile,true)
15041504
[ for r in sigDataResources do
15051505
let ccuName = GetSignatureDataResourceName r
15061506
let bytes =

src/fsharp/vs/IncrementalBuild.fsi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ module internal IncrementalFSharpBuild =
107107
/// Whether there are any 'live' type providers that may need a refresh when a project is Cleaned
108108
member ThereAreLiveTypeProviders : bool
109109
#endif
110-
/// Perform one step in the F# build (type-checking only, the type check is not finalized)
110+
/// Perform one step in the F# build. Return true if the background work is finished.
111111
member Step : unit -> bool
112112

113113
/// Get the preceding typecheck state of a slot, without checking if it is up-to-date w.r.t.

0 commit comments

Comments
 (0)