44module internal FSharp.Compiler.CompileOps
55
66open System
7+ open System.Collections .Concurrent
78open System.Collections .Generic
89open System.Diagnostics
910open System.IO
@@ -3938,6 +3939,13 @@ and [<Sealed>] TcImports(tcConfigP: TcConfigProvider, initialResolutions: TcAsse
39383939 let mutable dllTable : NameMap < ImportedBinary > = NameMap.empty
39393940 let mutable ccuInfos : ImportedAssembly list = []
39403941 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+
39413949 let disposeActions = ResizeArray()
39423950 let mutable disposed = false
39433951 let mutable ilGlobalsOpt = ilGlobalsOpt
@@ -3949,14 +3957,33 @@ and [<Sealed>] TcImports(tcConfigP: TcConfigProvider, initialResolutions: TcAsse
39493957#endif
39503958
39513959 let disposal = new TcImportsSafeDisposal( disposeActions, disposeTypeProviderActions, compilationThread)
3952-
3960+
39533961 let CheckDisposed () =
39543962 if disposed then assert false
39553963
39563964 let dispose () =
39573965 CheckDisposed()
39583966 ( disposal :> IDisposable) .Dispose()
39593967
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+
39603987 static let ccuHasType ( ccu : CcuThunk ) ( nsname : string list ) ( tname : string ) =
39613988 let matchNameSpace ( entityOpt : Entity option ) n =
39623989 match entityOpt with
@@ -3988,13 +4015,13 @@ and [<Sealed>] TcImports(tcConfigP: TcConfigProvider, initialResolutions: TcAsse
39884015 CheckDisposed()
39894016 tcImportsWeak
39904017#endif
3991-
4018+
39924019 member tcImports.RegisterCcu ccuInfo =
39934020 CheckDisposed()
39944021 ccuInfos <- ccuInfos ++ ccuInfo
39954022 // Assembly Ref Resolution: remove this use of ccu.AssemblyName
39964023 ccuTable <- NameMap.add ( ccuInfo.FSharpViewOfMetadata.AssemblyName) ccuInfo ccuTable
3997-
4024+
39984025 member tcImports.RegisterDll dllInfo =
39994026 CheckDisposed()
40004027 dllInfos <- dllInfos ++ dllInfo
@@ -4037,24 +4064,24 @@ and [<Sealed>] TcImports(tcConfigP: TcConfigProvider, initialResolutions: TcAsse
40374064 | Some res -> res
40384065 | None -> error( Error( FSComp.SR.buildCouldNotResolveAssembly assemblyName, m))
40394066
4040- member tcImports.GetImportedAssemblies () =
4067+ member tcImports.GetImportedAssemblies () =
40414068 CheckDisposed()
4042- match importsBase with
4069+ match importsBase with
40434070 | Some importsBase-> List.append ( importsBase.GetImportedAssemblies()) ccuInfos
4044- | None -> ccuInfos
4045-
4046- member tcImports.GetCcusExcludingBase () =
4071+ | None -> ccuInfos
4072+
4073+ member tcImports.GetCcusExcludingBase () =
40474074 CheckDisposed()
4048- ccuInfos |> List.map ( fun x -> x.FSharpViewOfMetadata)
4075+ ccuInfos |> List.map ( fun x -> x.FSharpViewOfMetadata)
40494076
4050- member tcImports.GetCcusInDeclOrder () =
4077+ member tcImports.GetCcusInDeclOrder () =
40514078 CheckDisposed()
40524079 List.map ( fun x -> x.FSharpViewOfMetadata) ( tcImports.GetImportedAssemblies())
4053-
4080+
40544081 // 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 ) =
40564083 CheckDisposed()
4057- let rec look ( t : TcImports ) =
4084+ let rec look ( t : TcImports ) =
40584085 match NameMap.tryFind assemblyName t.CcuTable with
40594086 | Some res -> Some res
40604087 | None ->
@@ -4069,9 +4096,8 @@ and [<Sealed>] TcImports(tcConfigP: TcConfigProvider, initialResolutions: TcAsse
40694096 match look tcImports with
40704097 | Some res -> ResolvedImportedAssembly res
40714098 | None -> UnresolvedImportedAssembly assemblyName
4072-
40734099
4074- member tcImports.FindCcu ( ctok , m , assemblyName , lookupOnly ) =
4100+ member tcImports.FindCcu ( ctok , m , assemblyName , lookupOnly ) =
40754101 CheckDisposed()
40764102 match tcImports.FindCcuInfo( ctok, m, assemblyName, lookupOnly) with
40774103 | ResolvedImportedAssembly importedAssembly -> ResolvedCcu( importedAssembly.FSharpViewOfMetadata)
@@ -4509,7 +4535,7 @@ and [<Sealed>] TcImports(tcConfigP: TcConfigProvider, initialResolutions: TcAsse
45094535#endif
45104536 FSharpOptimizationData = notlazy None }
45114537 tcImports.RegisterCcu ccuinfo
4512- let phase2 () =
4538+ let phase2 () =
45134539#if ! NO_ EXTENSIONTYPING
45144540 ccuinfo.TypeProviders <- tcImports.ImportTypeProviderExtensions ( ctok, tcConfig, filename, ilScopeRef, ilModule.ManifestOfAssembly.CustomAttrs.AsList, ccu.Contents, invalidateCcu, m)
45154541#endif
@@ -4569,11 +4595,17 @@ and [<Sealed>] TcImports(tcConfigP: TcConfigProvider, initialResolutions: TcAsse
45694595 | None ->
45704596 if verbose then dprintf " *** no optimization data for CCU %s , was DLL compiled with --no-optimization-data??\n " ccuName
45714597 None
4572- | Some info ->
4598+ | Some info ->
45734599 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 ()))
45774609
45784610 let ilg = defaultArg ilGlobalsOpt EcmaMscorlibILGlobals
45794611
@@ -4599,19 +4631,25 @@ and [<Sealed>] TcImports(tcConfigP: TcConfigProvider, initialResolutions: TcAsse
45994631 ()
46004632#endif
46014633 data, ccuinfo, phase2)
4602-
4634+
46034635 // Register all before relinking to cope with mutually-referential ccus
46044636 ccuRawDataAndInfos |> List.iter ( p23 >> tcImports.RegisterCcu)
4605- let phase2 () =
4637+ let phase2 () =
46064638 (* Relink *)
46074639 (* 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+ )
46094648#if ! NO_ EXTENSIONTYPING
46104649 ccuRawDataAndInfos |> List.iter ( fun ( _ , _ , phase2 ) -> phase2())
46114650#endif
4612- ccuRawDataAndInfos |> List.map p23 |> List.map ResolvedImportedAssembly
4651+ ccuRawDataAndInfos |> List.map p23 |> List.map ResolvedImportedAssembly
46134652 phase2
4614-
46154653
46164654 // NOTE: When used in the Language Service this can cause the transitive checking of projects. Hence it must be cancellable.
46174655 member tcImports.RegisterAndPrepareToImportReferencedDll ( ctok , r : AssemblyResolution ) : Cancellable < _ * ( unit -> AvailableImportedAssembly list )> =
@@ -4653,16 +4691,16 @@ and [<Sealed>] TcImports(tcConfigP: TcConfigProvider, initialResolutions: TcAsse
46534691 ILAssemblyRefs = assemblyData.ILAssemblyRefs }
46544692 tcImports.RegisterDll dllinfo
46554693 let ilg = defaultArg ilGlobalsOpt EcmaMscorlibILGlobals
4656- let phase2 =
4694+ let phase2 =
46574695 if assemblyData.HasAnyFSharpSignatureDataAttribute then
46584696 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)
46614699 else
4662- try
4700+ try
46634701 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
46664704 tcImports.PrepareToImportReferencedILAssembly ( ctok, m, filename, dllinfo)
46674705 return dllinfo, phase2
46684706 }
@@ -4683,6 +4721,7 @@ and [<Sealed>] TcImports(tcConfigP: TcConfigProvider, initialResolutions: TcAsse
46834721 })
46844722
46854723 let dllinfos , phase2s = results |> List.choose id |> List.unzip
4724+ fixupOrphanCcus()
46864725 let ccuinfos = ( List.collect ( fun phase2 -> phase2()) phase2s)
46874726 return dllinfos, ccuinfos
46884727 }
0 commit comments