Skip to content

Commit b044ca9

Browse files
authored
Service: wrap creating reference from module reader into cancellable (#16338)
1 parent 1c761ea commit b044ca9

File tree

2 files changed

+57
-22
lines changed

2 files changed

+57
-22
lines changed

src/Compiler/Service/service.fs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,12 +279,13 @@ type BackgroundCompiler
279279
| FSharpReferencedProject.ILModuleReference(nm, getStamp, getReader) ->
280280
{ new IProjectReference with
281281
member x.EvaluateRawContents() =
282-
node {
282+
cancellable {
283283
let ilReader = getReader ()
284284
let ilModuleDef, ilAsmRefs = ilReader.ILModuleDef, ilReader.ILAssemblyRefs
285285
let data = RawFSharpAssemblyData(ilModuleDef, ilAsmRefs) :> IRawFSharpAssemblyData
286286
return ProjectAssemblyDataResult.Available data
287287
}
288+
|> NodeCode.FromCancellable
288289

289290
member x.TryGetLogicalTimeStamp _ = getStamp () |> Some
290291
member x.FileName = nm

tests/service/ModuleReaderCancellationTests.fs

Lines changed: 55 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -58,27 +58,36 @@ module ModuleReader =
5858
securityDecls, customAttrs)
5959

6060

61-
type ModuleReader(name, typeDefs) =
61+
62+
type ModuleReader(name, typeDefs, cancelOnModuleAccess) =
6263
let assemblyName = $"{name}.dll"
63-
let moduleName = name
64-
let isDll = true
65-
66-
let ilModuleDef =
67-
mkILSimpleModule
68-
assemblyName moduleName isDll
69-
ModuleReader.subsystemVersion
70-
ModuleReader.useHighEntropyVA
71-
typeDefs
72-
None None
73-
ModuleReader.flags
74-
ModuleReader.exportedTypes
75-
""
64+
65+
let mkModuleDef =
66+
let mkModuleDef () =
67+
let assemblyName = $"{name}.dll"
68+
let moduleName = name
69+
let isDll = true
70+
71+
mkILSimpleModule
72+
assemblyName moduleName isDll
73+
ModuleReader.subsystemVersion
74+
ModuleReader.useHighEntropyVA
75+
typeDefs
76+
None None
77+
ModuleReader.flags
78+
ModuleReader.exportedTypes
79+
""
80+
81+
if cancelOnModuleAccess then
82+
runCancelFirstTime mkModuleDef
83+
else
84+
mkModuleDef
7685

7786
member val Timestamp = DateTime.UtcNow
7887
member val Path = Path.Combine(Path.GetTempPath(), assemblyName)
7988

8089
interface ILModuleReader with
81-
member x.ILModuleDef = ilModuleDef
90+
member x.ILModuleDef = mkModuleDef ()
8291
member x.ILAssemblyRefs = []
8392
member x.Dispose() = ()
8493

@@ -126,8 +135,8 @@ let createPreTypeDefs typeData =
126135
|> Array.ofList
127136
|> Array.map (fun data -> PreTypeDef data :> ILPreTypeDef)
128137

129-
let referenceReaderProject getPreTypeDefs options =
130-
let reader = new ModuleReader("Reference", mkILTypeDefsComputed getPreTypeDefs)
138+
let referenceReaderProject getPreTypeDefs (cancelOnModuleAccess: bool) options =
139+
let reader = new ModuleReader("Reference", mkILTypeDefsComputed getPreTypeDefs, cancelOnModuleAccess)
131140

132141
let project = FSharpReferencedProject.ILModuleReference(
133142
reader.Path, (fun _ -> reader.Timestamp), (fun _ -> reader)
@@ -171,7 +180,7 @@ let ``Type defs 01 - assembly import`` () =
171180
let getPreTypeDefs typeData = runCancelFirstTime (fun _ -> createPreTypeDefs typeData)
172181
let typeDefs = getPreTypeDefs [ { Name = "T"; Namespace = []; HasCtor = false; CancelOnImport = false } ]
173182
let path, options = mkTestFileAndOptions source [||]
174-
let options = referenceReaderProject typeDefs options
183+
let options = referenceReaderProject typeDefs false options
175184

176185
// First request, should be cancelled inside getPreTypeDefs
177186
// The cancellation happens in side CombineImportedAssembliesTask, so background builder node fails to be evaluated
@@ -196,7 +205,7 @@ let ``Type defs 02 - assembly import`` () =
196205

197206
let typeDefs = fun _ -> createPreTypeDefs [ { Name = "T"; Namespace = ["Ns"]; HasCtor = false; CancelOnImport = true } ]
198207
let path, options = mkTestFileAndOptions source [||]
199-
let options = referenceReaderProject typeDefs options
208+
let options = referenceReaderProject typeDefs false options
200209

201210
parseAndCheck path source options |> ignore
202211
wasCancelled |> shouldEqual false
@@ -214,7 +223,7 @@ let ``Type defs 03 - type import`` () =
214223

215224
let typeDefs = fun _ -> createPreTypeDefs [ { Name = "T"; Namespace = ["Ns1"; "Ns2"]; HasCtor = false; CancelOnImport = true } ]
216225
let path, options = mkTestFileAndOptions source [||]
217-
let options = referenceReaderProject typeDefs options
226+
let options = referenceReaderProject typeDefs false options
218227

219228
// First request, should be cancelled inside GetTypeDef
220229
// This shouldn't be cached due to InterruptibleLazy
@@ -239,7 +248,7 @@ let ``Type defs 04 - ctor import`` () =
239248

240249
let typeDefs = fun _ -> createPreTypeDefs [ { Name = "T"; Namespace = []; HasCtor = true; CancelOnImport = false } ]
241250
let path, options = mkTestFileAndOptions source [||]
242-
let options = referenceReaderProject typeDefs options
251+
let options = referenceReaderProject typeDefs false options
243252

244253
// First request, should be cancelled inside ILMethodDefs
245254
// This shouldn't be cached due to InterruptibleLazy
@@ -253,3 +262,28 @@ let ``Type defs 04 - ctor import`` () =
253262
results.Diagnostics |> Array.isEmpty |> shouldEqual true
254263

255264
| None -> failwith "Expecting results"
265+
266+
[<Test>]
267+
let ``Module def 01 - assembly import`` () =
268+
let source = source1
269+
270+
let getPreTypeDefs typeData = fun _ -> createPreTypeDefs typeData
271+
let typeDefs = getPreTypeDefs [ { Name = "T"; Namespace = []; HasCtor = false; CancelOnImport = false } ]
272+
let path, options = mkTestFileAndOptions source [||]
273+
let options = referenceReaderProject typeDefs true options
274+
275+
// First request, should be cancelled inside getPreTypeDefs
276+
// The cancellation happens in side CombineImportedAssembliesTask, so background builder node fails to be evaluated
277+
parseAndCheck path source options |> ignore
278+
wasCancelled |> shouldEqual true
279+
280+
// Second request, should succeed, with complete analysis
281+
match parseAndCheck path source options with
282+
| Some results ->
283+
wasCancelled |> shouldEqual false
284+
285+
results.Diagnostics
286+
|> Array.map _.Message
287+
|> shouldEqual [| "No constructors are available for the type 'T'" |]
288+
289+
| None -> failwith "Expecting results"

0 commit comments

Comments
 (0)