4
4
module internal FSharp.Compiler.CompileOps
5
5
6
6
open System
7
+ open System.Collections .Concurrent
7
8
open System.Collections .Generic
8
9
open System.Diagnostics
9
10
open System.IO
@@ -3938,6 +3939,13 @@ and [<Sealed>] TcImports(tcConfigP: TcConfigProvider, initialResolutions: TcAsse
3938
3939
let mutable dllTable : NameMap < ImportedBinary > = NameMap.empty
3939
3940
let mutable ccuInfos : ImportedAssembly list = []
3940
3941
let mutable ccuTable : NameMap < ImportedAssembly > = NameMap.empty
3942
+
3943
+ /// ccuThunks is a ConcurrentDictionary thus threadsafe
3944
+ /// the key is a ccuThunk object, the value is a (unit->unit) func that when executed
3945
+ /// the func is used to fix up the func and operates on data captured at the time the func is created.
3946
+ /// func() is captured during phase2() of RegisterAndPrepareToImportReferencedDll(..) and PrepareToImportReferencedFSharpAssembly ( .. )
3947
+ let mutable ccuThunks = new ConcurrentDictionary< CcuThunk, ( unit -> unit)>()
3948
+
3941
3949
let disposeActions = ResizeArray()
3942
3950
let mutable disposed = false
3943
3951
let mutable ilGlobalsOpt = ilGlobalsOpt
@@ -3949,14 +3957,33 @@ and [<Sealed>] TcImports(tcConfigP: TcConfigProvider, initialResolutions: TcAsse
3949
3957
#endif
3950
3958
3951
3959
let disposal = new TcImportsSafeDisposal( disposeActions, disposeTypeProviderActions, compilationThread)
3952
-
3960
+
3953
3961
let CheckDisposed () =
3954
3962
if disposed then assert false
3955
3963
3956
3964
let dispose () =
3957
3965
CheckDisposed()
3958
3966
( disposal :> IDisposable) .Dispose()
3959
3967
3968
+ // This is used to fixe up unresolved ccuThunks that were created during assembly import.
3969
+ // the ccuThunks dictionary is a ConcurrentDictionary and thus threadsafe.
3970
+ // Algorithm:
3971
+ // Get a snapshot of the current unFixedUp ccuThunks.
3972
+ // for each of those thunks, remove them from the dictionary, so any parallel threads can't do this work
3973
+ // If it successfully removed it from the dictionary then do the fixup
3974
+ // If the thunk remains unresolved add it back to the ccuThunks dictionary for further processing
3975
+ // If not then move on to the next thunk
3976
+ let fixupOrphanCcus () =
3977
+ let keys = ccuThunks.Keys
3978
+ for ccuThunk in keys do
3979
+ match ccuThunks.TryRemove( ccuThunk) with
3980
+ | true , func ->
3981
+ if ccuThunk.IsUnresolvedReference then
3982
+ func()
3983
+ if ccuThunk.IsUnresolvedReference then
3984
+ ccuThunks.TryAdd( ccuThunk, func) |> ignore
3985
+ | _ -> ()
3986
+
3960
3987
static let ccuHasType ( ccu : CcuThunk ) ( nsname : string list ) ( tname : string ) =
3961
3988
let matchNameSpace ( entityOpt : Entity option ) n =
3962
3989
match entityOpt with
@@ -3988,13 +4015,13 @@ and [<Sealed>] TcImports(tcConfigP: TcConfigProvider, initialResolutions: TcAsse
3988
4015
CheckDisposed()
3989
4016
tcImportsWeak
3990
4017
#endif
3991
-
4018
+
3992
4019
member tcImports.RegisterCcu ccuInfo =
3993
4020
CheckDisposed()
3994
4021
ccuInfos <- ccuInfos ++ ccuInfo
3995
4022
// Assembly Ref Resolution: remove this use of ccu.AssemblyName
3996
4023
ccuTable <- NameMap.add ( ccuInfo.FSharpViewOfMetadata.AssemblyName) ccuInfo ccuTable
3997
-
4024
+
3998
4025
member tcImports.RegisterDll dllInfo =
3999
4026
CheckDisposed()
4000
4027
dllInfos <- dllInfos ++ dllInfo
@@ -4037,24 +4064,24 @@ and [<Sealed>] TcImports(tcConfigP: TcConfigProvider, initialResolutions: TcAsse
4037
4064
| Some res -> res
4038
4065
| None -> error( Error( FSComp.SR.buildCouldNotResolveAssembly assemblyName, m))
4039
4066
4040
- member tcImports.GetImportedAssemblies () =
4067
+ member tcImports.GetImportedAssemblies () =
4041
4068
CheckDisposed()
4042
- match importsBase with
4069
+ match importsBase with
4043
4070
| Some importsBase-> List.append ( importsBase.GetImportedAssemblies()) ccuInfos
4044
- | None -> ccuInfos
4045
-
4046
- member tcImports.GetCcusExcludingBase () =
4071
+ | None -> ccuInfos
4072
+
4073
+ member tcImports.GetCcusExcludingBase () =
4047
4074
CheckDisposed()
4048
- ccuInfos |> List.map ( fun x -> x.FSharpViewOfMetadata)
4075
+ ccuInfos |> List.map ( fun x -> x.FSharpViewOfMetadata)
4049
4076
4050
- member tcImports.GetCcusInDeclOrder () =
4077
+ member tcImports.GetCcusInDeclOrder () =
4051
4078
CheckDisposed()
4052
4079
List.map ( fun x -> x.FSharpViewOfMetadata) ( tcImports.GetImportedAssemblies())
4053
-
4080
+
4054
4081
// This is the main "assembly reference --> assembly" resolution routine.
4055
- member tcImports.FindCcuInfo ( ctok , m , assemblyName , lookupOnly ) =
4082
+ member tcImports.FindCcuInfo ( ctok , m , assemblyName , lookupOnly ) =
4056
4083
CheckDisposed()
4057
- let rec look ( t : TcImports ) =
4084
+ let rec look ( t : TcImports ) =
4058
4085
match NameMap.tryFind assemblyName t.CcuTable with
4059
4086
| Some res -> Some res
4060
4087
| None ->
@@ -4069,9 +4096,8 @@ and [<Sealed>] TcImports(tcConfigP: TcConfigProvider, initialResolutions: TcAsse
4069
4096
match look tcImports with
4070
4097
| Some res -> ResolvedImportedAssembly res
4071
4098
| None -> UnresolvedImportedAssembly assemblyName
4072
-
4073
4099
4074
- member tcImports.FindCcu ( ctok , m , assemblyName , lookupOnly ) =
4100
+ member tcImports.FindCcu ( ctok , m , assemblyName , lookupOnly ) =
4075
4101
CheckDisposed()
4076
4102
match tcImports.FindCcuInfo( ctok, m, assemblyName, lookupOnly) with
4077
4103
| ResolvedImportedAssembly importedAssembly -> ResolvedCcu( importedAssembly.FSharpViewOfMetadata)
@@ -4509,7 +4535,7 @@ and [<Sealed>] TcImports(tcConfigP: TcConfigProvider, initialResolutions: TcAsse
4509
4535
#endif
4510
4536
FSharpOptimizationData = notlazy None }
4511
4537
tcImports.RegisterCcu ccuinfo
4512
- let phase2 () =
4538
+ let phase2 () =
4513
4539
#if ! NO_ EXTENSIONTYPING
4514
4540
ccuinfo.TypeProviders <- tcImports.ImportTypeProviderExtensions ( ctok, tcConfig, filename, ilScopeRef, ilModule.ManifestOfAssembly.CustomAttrs.AsList, ccu.Contents, invalidateCcu, m)
4515
4541
#endif
@@ -4569,11 +4595,17 @@ and [<Sealed>] TcImports(tcConfigP: TcConfigProvider, initialResolutions: TcAsse
4569
4595
| None ->
4570
4596
if verbose then dprintf " *** no optimization data for CCU %s , was DLL compiled with --no-optimization-data??\n " ccuName
4571
4597
None
4572
- | Some info ->
4598
+ | Some info ->
4573
4599
let data = GetOptimizationData ( filename, ilScopeRef, ilModule.TryGetILModuleDef(), info)
4574
- let res = data.OptionalFixup( fun nm -> availableToOptionalCcu( tcImports.FindCcu( ctok, m, nm, lookupOnly= false )))
4575
- if verbose then dprintf " found optimization data for CCU %s \n " ccuName
4576
- Some res)
4600
+ let fixupThunk () = data.OptionalFixup( fun nm -> availableToOptionalCcu( tcImports.FindCcu( ctok, m, nm, lookupOnly= false )))
4601
+
4602
+ // Make a note of all ccuThunks that may still need to be fixed up when other dlls are loaded
4603
+ for ccuThunk in data.FixupThunks do
4604
+ if ccuThunk.IsUnresolvedReference then
4605
+ ccuThunks.TryAdd( ccuThunk, fun () -> fixupThunk () |> ignore) |> ignore
4606
+
4607
+ if verbose then dprintf " found optimization data for CCU %s \n " ccuName
4608
+ Some ( fixupThunk ()))
4577
4609
4578
4610
let ilg = defaultArg ilGlobalsOpt EcmaMscorlibILGlobals
4579
4611
@@ -4599,19 +4631,25 @@ and [<Sealed>] TcImports(tcConfigP: TcConfigProvider, initialResolutions: TcAsse
4599
4631
()
4600
4632
#endif
4601
4633
data, ccuinfo, phase2)
4602
-
4634
+
4603
4635
// Register all before relinking to cope with mutually-referential ccus
4604
4636
ccuRawDataAndInfos |> List.iter ( p23 >> tcImports.RegisterCcu)
4605
- let phase2 () =
4637
+ let phase2 () =
4606
4638
(* Relink *)
4607
4639
(* dprintf "Phase2: %s\n" filename; REMOVE DIAGNOSTICS *)
4608
- ccuRawDataAndInfos |> List.iter ( fun ( data , _ , _ ) -> data.OptionalFixup( fun nm -> availableToOptionalCcu( tcImports.FindCcu( ctok, m, nm, lookupOnly= false ))) |> ignore)
4640
+ ccuRawDataAndInfos
4641
+ |> List.iter ( fun ( data , _ , _ ) ->
4642
+ let fixupThunk () = data.OptionalFixup( fun nm -> availableToOptionalCcu( tcImports.FindCcu( ctok, m, nm, lookupOnly= false ))) |> ignore
4643
+ fixupThunk()
4644
+ for ccuThunk in data.FixupThunks do
4645
+ if ccuThunk.IsUnresolvedReference then
4646
+ ccuThunks.TryAdd( ccuThunk, fixupThunk) |> ignore
4647
+ )
4609
4648
#if ! NO_ EXTENSIONTYPING
4610
4649
ccuRawDataAndInfos |> List.iter ( fun ( _ , _ , phase2 ) -> phase2())
4611
4650
#endif
4612
- ccuRawDataAndInfos |> List.map p23 |> List.map ResolvedImportedAssembly
4651
+ ccuRawDataAndInfos |> List.map p23 |> List.map ResolvedImportedAssembly
4613
4652
phase2
4614
-
4615
4653
4616
4654
// NOTE: When used in the Language Service this can cause the transitive checking of projects. Hence it must be cancellable.
4617
4655
member tcImports.RegisterAndPrepareToImportReferencedDll ( ctok , r : AssemblyResolution ) : Cancellable < _ * ( unit -> AvailableImportedAssembly list )> =
@@ -4653,16 +4691,16 @@ and [<Sealed>] TcImports(tcConfigP: TcConfigProvider, initialResolutions: TcAsse
4653
4691
ILAssemblyRefs = assemblyData.ILAssemblyRefs }
4654
4692
tcImports.RegisterDll dllinfo
4655
4693
let ilg = defaultArg ilGlobalsOpt EcmaMscorlibILGlobals
4656
- let phase2 =
4694
+ let phase2 =
4657
4695
if assemblyData.HasAnyFSharpSignatureDataAttribute then
4658
4696
if not ( assemblyData.HasMatchingFSharpSignatureDataAttribute ilg) then
4659
- errorR( Error( FSComp.SR.buildDifferentVersionMustRecompile filename, m))
4660
- tcImports.PrepareToImportReferencedILAssembly ( ctok, m, filename, dllinfo)
4697
+ errorR( Error( FSComp.SR.buildDifferentVersionMustRecompile filename, m))
4698
+ tcImports.PrepareToImportReferencedILAssembly ( ctok, m, filename, dllinfo)
4661
4699
else
4662
- try
4700
+ try
4663
4701
tcImports.PrepareToImportReferencedFSharpAssembly ( ctok, m, filename, dllinfo)
4664
- with e -> error( Error( FSComp.SR.buildErrorOpeningBinaryFile( filename, e.Message), m))
4665
- else
4702
+ with e -> error( Error( FSComp.SR.buildErrorOpeningBinaryFile( filename, e.Message), m))
4703
+ else
4666
4704
tcImports.PrepareToImportReferencedILAssembly ( ctok, m, filename, dllinfo)
4667
4705
return dllinfo, phase2
4668
4706
}
@@ -4683,6 +4721,7 @@ and [<Sealed>] TcImports(tcConfigP: TcConfigProvider, initialResolutions: TcAsse
4683
4721
})
4684
4722
4685
4723
let dllinfos , phase2s = results |> List.choose id |> List.unzip
4724
+ fixupOrphanCcus()
4686
4725
let ccuinfos = ( List.collect ( fun phase2 -> phase2()) phase2s)
4687
4726
return dllinfos, ccuinfos
4688
4727
}
0 commit comments