Skip to content

Commit

Permalink
IL: make ILTypeDefs, ILMethodDefs thread-safe (#16147)
Browse files Browse the repository at this point in the history
* IL: make ILTypeDefs, ILMethodDefs thread-safe
  • Loading branch information
auduchinok authored Oct 26, 2023
1 parent be6352d commit 5ee5e58
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 65 deletions.
71 changes: 32 additions & 39 deletions src/Compiler/AbstractIL/il.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2142,39 +2142,35 @@ type ILMethodDef
type MethodDefMap = Map<string, ILMethodDef list>

[<Sealed>]
type ILMethodDefs(f: unit -> ILMethodDef[]) =
type ILMethodDefs(f) =
inherit DelayInitArrayMap<ILMethodDef, string, ILMethodDef list>(f)

let mutable array = InlineDelayInit<_>(f)
override this.CreateDictionary(arr) =
let t = Dictionary(arr.Length)

let mutable dict =
InlineDelayInit<_>(fun () ->
let arr = array.Value
let t = Dictionary<_, _>()
for i = arr.Length - 1 downto 0 do
let y = arr[i]
let key = y.Name

for i = arr.Length - 1 downto 0 do
let y = arr[i]
let key = y.Name
match t.TryGetValue key with
| true, m -> t[key] <- y :: m
| _ -> t[key] <- [ y ]

match t.TryGetValue key with
| true, m -> t[key] <- y :: m
| _ -> t[key] <- [ y ]

t)
t

interface IEnumerable with
member x.GetEnumerator() =
((x :> IEnumerable<ILMethodDef>).GetEnumerator() :> IEnumerator)

interface IEnumerable<ILMethodDef> with
member x.GetEnumerator() =
(array.Value :> IEnumerable<ILMethodDef>).GetEnumerator()

member x.AsArray() = array.Value
(x.GetArray() :> IEnumerable<ILMethodDef>).GetEnumerator()

member x.AsList() = array.Value |> Array.toList
member x.AsArray() = x.GetArray()
member x.AsList() = x.GetArray() |> Array.toList

member x.FindByName nm =
match dict.Value.TryGetValue nm with
match x.GetDictionary().TryGetValue nm with
| true, m -> m
| _ -> []

Expand Down Expand Up @@ -2830,43 +2826,40 @@ type ILTypeDef
override x.ToString() = "type " + x.Name

and [<Sealed>] ILTypeDefs(f: unit -> ILPreTypeDef[]) =
inherit DelayInitArrayMap<ILPreTypeDef, string list * string, ILPreTypeDef>(f)

let mutable array = InlineDelayInit<_>(f)
override this.CreateDictionary(arr) =
let t = Dictionary(arr.Length, HashIdentity.Structural)

let mutable dict =
InlineDelayInit<_>(fun () ->
let arr = array.Value
let t = Dictionary<_, _>(HashIdentity.Structural)
for pre in arr do
let key = pre.Namespace, pre.Name
t[key] <- pre

for pre in arr do
let key = pre.Namespace, pre.Name
t[key] <- pre
ReadOnlyDictionary t

ReadOnlyDictionary t)
member x.AsArray() =
[| for pre in x.GetArray() -> pre.GetTypeDef() |]

member _.AsArray() =
[| for pre in array.Value -> pre.GetTypeDef() |]

member _.AsList() =
[ for pre in array.Value -> pre.GetTypeDef() ]
member x.AsList() =
[ for pre in x.GetArray() -> pre.GetTypeDef() ]

interface IEnumerable with
member x.GetEnumerator() =
((x :> IEnumerable<ILTypeDef>).GetEnumerator() :> IEnumerator)

interface IEnumerable<ILTypeDef> with
member x.GetEnumerator() =
(seq { for pre in array.Value -> pre.GetTypeDef() }).GetEnumerator()
(seq { for pre in x.GetArray() -> pre.GetTypeDef() }).GetEnumerator()

member _.AsArrayOfPreTypeDefs() = array.Value
member x.AsArrayOfPreTypeDefs() = x.GetArray()

member _.FindByName nm =
member x.FindByName nm =
let ns, n = splitILTypeName nm
dict.Value[ (ns, n) ].GetTypeDef()
x.GetDictionary().[(ns, n)].GetTypeDef()

member _.ExistsByName nm =
member x.ExistsByName nm =
let ns, n = splitILTypeName nm
dict.Value.ContainsKey((ns, n))
x.GetDictionary().ContainsKey((ns, n))

and [<NoEquality; NoComparison>] ILPreTypeDef =
abstract Namespace: string list
Expand Down
8 changes: 6 additions & 2 deletions src/Compiler/AbstractIL/il.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ module rec FSharp.Compiler.AbstractIL.IL
open FSharp.Compiler.IO
open System.Collections.Generic
open System.Reflection
open Internal.Utilities.Library

/// Represents the target primary assembly
[<RequireQualifiedAccess>]
Expand Down Expand Up @@ -1179,8 +1180,9 @@ type ILMethodDef =
/// Tables of methods. Logically equivalent to a list of methods but
/// the table is kept in a form optimized for looking up methods by
/// name and arity.
[<NoEquality; NoComparison; Sealed>]
[<NoEquality; NoComparison; Class; Sealed>]
type ILMethodDefs =
inherit DelayInitArrayMap<ILMethodDef, string, ILMethodDef list>

interface IEnumerable<ILMethodDef>

Expand Down Expand Up @@ -1458,8 +1460,10 @@ type ILTypeDefKind =
| Delegate

/// Tables of named type definitions.
[<NoEquality; NoComparison; Sealed>]
[<NoEquality; NoComparison; Class; Sealed>]
type ILTypeDefs =
inherit DelayInitArrayMap<ILPreTypeDef, string list * string, ILPreTypeDef>

interface IEnumerable<ILTypeDef>

member internal AsArray: unit -> ILTypeDef[]
Expand Down
58 changes: 42 additions & 16 deletions src/Compiler/Utilities/illib.fs
Original file line number Diff line number Diff line change
Expand Up @@ -115,25 +115,51 @@ module internal PervasiveAutoOpens =

task.Result

/// An efficient lazy for inline storage in a class type. Results in fewer thunks.
[<Struct>]
type InlineDelayInit<'T when 'T: not struct> =
new(f: unit -> 'T) =
{
store = Unchecked.defaultof<'T>
func = Func<_>(f)
}
[<AbstractClass>]
type DelayInitArrayMap<'T, 'TDictKey, 'TDictValue>(f: unit -> 'T[]) =
let syncObj = obj ()

let mutable arrayStore = null
let mutable dictStore = null

let mutable func = f

member this.GetArray() =
match arrayStore with
| NonNull value -> value
| _ ->
Monitor.Enter(syncObj)

try
match arrayStore with
| NonNull value -> value
| _ ->

arrayStore <- func ()

val mutable store: 'T
val mutable func: Func<'T> MaybeNull
func <- Unchecked.defaultof<_>
arrayStore
finally
Monitor.Exit(syncObj)

member x.Value =
match x.func with
| null -> x.store
member this.GetDictionary() =
match dictStore with
| NonNull value -> value
| _ ->
let res = LazyInitializer.EnsureInitialized(&x.store, x.func)
x.func <- null
res
let array = this.GetArray()
Monitor.Enter(syncObj)

try
match dictStore with
| NonNull value -> value
| _ ->

dictStore <- this.CreateDictionary(array)
dictStore
finally
Monitor.Exit(syncObj)

abstract CreateDictionary: 'T[] -> IDictionary<'TDictKey, 'TDictValue>

//-------------------------------------------------------------------------
// Library: projections
Expand Down
13 changes: 7 additions & 6 deletions src/Compiler/Utilities/illib.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,14 @@ module internal PervasiveAutoOpens =

val notFound: unit -> 'a

[<Struct>]
type internal InlineDelayInit<'T when 'T: not struct> =
[<AbstractClass>]
type DelayInitArrayMap<'T, 'TDictKey, 'TDictValue> =
new: f: (unit -> 'T[]) -> DelayInitArrayMap<'T, 'TDictKey, 'TDictValue>

member GetArray: unit -> 'T[]
member GetDictionary: unit -> IDictionary<'TDictKey, 'TDictValue>

new: f: (unit -> 'T) -> InlineDelayInit<'T>
val mutable store: 'T
val mutable func: Func<'T>
member Value: 'T
abstract CreateDictionary: 'T[] -> IDictionary<'TDictKey, 'TDictValue>

module internal Order =

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,7 @@ FSharp.Compiler.AbstractIL.IL+ILMethodDefs: ILMethodDef[] AsArray()
FSharp.Compiler.AbstractIL.IL+ILMethodDefs: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.AbstractIL.IL+ILMethodDef] AsList()
FSharp.Compiler.AbstractIL.IL+ILMethodDefs: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.AbstractIL.IL+ILMethodDef] FindByName(System.String)
FSharp.Compiler.AbstractIL.IL+ILMethodDefs: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.AbstractIL.IL+ILMethodDef] TryFindInstanceByNameAndCallingSignature(System.String, ILCallingSignature)
FSharp.Compiler.AbstractIL.IL+ILMethodDefs: System.Collections.Generic.IDictionary`2[System.String,Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.AbstractIL.IL+ILMethodDef]] CreateDictionary(ILMethodDef[])
FSharp.Compiler.AbstractIL.IL+ILMethodImplDef: Boolean Equals(ILMethodImplDef)
FSharp.Compiler.AbstractIL.IL+ILMethodImplDef: Boolean Equals(System.Object)
FSharp.Compiler.AbstractIL.IL+ILMethodImplDef: Boolean Equals(System.Object, System.Collections.IEqualityComparer)
Expand Down Expand Up @@ -1619,6 +1620,7 @@ FSharp.Compiler.AbstractIL.IL+ILTypeDefLayout: Int32 GetHashCode(System.Collecti
FSharp.Compiler.AbstractIL.IL+ILTypeDefLayout: Int32 Tag
FSharp.Compiler.AbstractIL.IL+ILTypeDefLayout: Int32 get_Tag()
FSharp.Compiler.AbstractIL.IL+ILTypeDefLayout: System.String ToString()
FSharp.Compiler.AbstractIL.IL+ILTypeDefs: System.Collections.Generic.IDictionary`2[System.Tuple`2[Microsoft.FSharp.Collections.FSharpList`1[System.String],System.String],FSharp.Compiler.AbstractIL.IL+ILPreTypeDef] CreateDictionary(ILPreTypeDef[])
FSharp.Compiler.AbstractIL.IL+ILTypeInit+Tags: Int32 BeforeField
FSharp.Compiler.AbstractIL.IL+ILTypeInit+Tags: Int32 OnAny
FSharp.Compiler.AbstractIL.IL+ILTypeInit: Boolean Equals(ILTypeInit)
Expand Down Expand Up @@ -11710,4 +11712,8 @@ FSharp.Compiler.Xml.XmlDoc: System.String GetXmlText()
FSharp.Compiler.Xml.XmlDoc: System.String[] GetElaboratedXmlLines()
FSharp.Compiler.Xml.XmlDoc: System.String[] UnprocessedLines
FSharp.Compiler.Xml.XmlDoc: System.String[] get_UnprocessedLines()
FSharp.Compiler.Xml.XmlDoc: Void .ctor(System.String[], FSharp.Compiler.Text.Range)
FSharp.Compiler.Xml.XmlDoc: Void .ctor(System.String[], FSharp.Compiler.Text.Range)
Internal.Utilities.Library.DelayInitArrayMap`3[T,TDictKey,TDictValue]: System.Collections.Generic.IDictionary`2[TDictKey,TDictValue] CreateDictionary(T[])
Internal.Utilities.Library.DelayInitArrayMap`3[T,TDictKey,TDictValue]: System.Collections.Generic.IDictionary`2[TDictKey,TDictValue] GetDictionary()
Internal.Utilities.Library.DelayInitArrayMap`3[T,TDictKey,TDictValue]: T[] GetArray()
Internal.Utilities.Library.DelayInitArrayMap`3[T,TDictKey,TDictValue]: Void .ctor(Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,T[]])
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,7 @@ FSharp.Compiler.AbstractIL.IL+ILMethodDefs: ILMethodDef[] AsArray()
FSharp.Compiler.AbstractIL.IL+ILMethodDefs: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.AbstractIL.IL+ILMethodDef] AsList()
FSharp.Compiler.AbstractIL.IL+ILMethodDefs: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.AbstractIL.IL+ILMethodDef] FindByName(System.String)
FSharp.Compiler.AbstractIL.IL+ILMethodDefs: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.AbstractIL.IL+ILMethodDef] TryFindInstanceByNameAndCallingSignature(System.String, ILCallingSignature)
FSharp.Compiler.AbstractIL.IL+ILMethodDefs: System.Collections.Generic.IDictionary`2[System.String,Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.AbstractIL.IL+ILMethodDef]] CreateDictionary(ILMethodDef[])
FSharp.Compiler.AbstractIL.IL+ILMethodImplDef: Boolean Equals(ILMethodImplDef)
FSharp.Compiler.AbstractIL.IL+ILMethodImplDef: Boolean Equals(System.Object)
FSharp.Compiler.AbstractIL.IL+ILMethodImplDef: Boolean Equals(System.Object, System.Collections.IEqualityComparer)
Expand Down Expand Up @@ -1619,6 +1620,7 @@ FSharp.Compiler.AbstractIL.IL+ILTypeDefLayout: Int32 GetHashCode(System.Collecti
FSharp.Compiler.AbstractIL.IL+ILTypeDefLayout: Int32 Tag
FSharp.Compiler.AbstractIL.IL+ILTypeDefLayout: Int32 get_Tag()
FSharp.Compiler.AbstractIL.IL+ILTypeDefLayout: System.String ToString()
FSharp.Compiler.AbstractIL.IL+ILTypeDefs: System.Collections.Generic.IDictionary`2[System.Tuple`2[Microsoft.FSharp.Collections.FSharpList`1[System.String],System.String],FSharp.Compiler.AbstractIL.IL+ILPreTypeDef] CreateDictionary(ILPreTypeDef[])
FSharp.Compiler.AbstractIL.IL+ILTypeInit+Tags: Int32 BeforeField
FSharp.Compiler.AbstractIL.IL+ILTypeInit+Tags: Int32 OnAny
FSharp.Compiler.AbstractIL.IL+ILTypeInit: Boolean Equals(ILTypeInit)
Expand Down Expand Up @@ -11710,4 +11712,8 @@ FSharp.Compiler.Xml.XmlDoc: System.String GetXmlText()
FSharp.Compiler.Xml.XmlDoc: System.String[] GetElaboratedXmlLines()
FSharp.Compiler.Xml.XmlDoc: System.String[] UnprocessedLines
FSharp.Compiler.Xml.XmlDoc: System.String[] get_UnprocessedLines()
FSharp.Compiler.Xml.XmlDoc: Void .ctor(System.String[], FSharp.Compiler.Text.Range)
FSharp.Compiler.Xml.XmlDoc: Void .ctor(System.String[], FSharp.Compiler.Text.Range)
Internal.Utilities.Library.DelayInitArrayMap`3[T,TDictKey,TDictValue]: System.Collections.Generic.IDictionary`2[TDictKey,TDictValue] CreateDictionary(T[])
Internal.Utilities.Library.DelayInitArrayMap`3[T,TDictKey,TDictValue]: System.Collections.Generic.IDictionary`2[TDictKey,TDictValue] GetDictionary()
Internal.Utilities.Library.DelayInitArrayMap`3[T,TDictKey,TDictValue]: T[] GetArray()
Internal.Utilities.Library.DelayInitArrayMap`3[T,TDictKey,TDictValue]: Void .ctor(Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,T[]])

0 comments on commit 5ee5e58

Please sign in to comment.