Skip to content

Commit 0f520bc

Browse files
majochapsfinaki
andauthored
Do not pass non-thread-safe logger to parallel computations in NodeCode.Parallel (#16717)
* don't pass non-threadsafe logger to parallel computations * no need to restore in forks --------- Co-authored-by: Petr <[email protected]>
1 parent 11e6619 commit 0f520bc

File tree

4 files changed

+63
-16
lines changed

4 files changed

+63
-16
lines changed

src/Compiler/Facilities/BuildGraph.fs

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ let wrapThreadStaticInfo computation =
2525
DiagnosticsThreadStatics.BuildPhase <- phase
2626
}
2727

28+
let unwrapNode (Node(computation)) = computation
29+
2830
type Async<'T> with
2931

3032
static member AwaitNodeCode(node: NodeCode<'T>) =
@@ -193,19 +195,28 @@ type NodeCode private () =
193195
}
194196

195197
static member Parallel(computations: NodeCode<'T> seq) =
196-
let diagnosticsLogger = DiagnosticsThreadStatics.DiagnosticsLogger
197-
let phase = DiagnosticsThreadStatics.BuildPhase
198+
node {
199+
let concurrentLogging = new CaptureDiagnosticsConcurrently()
200+
let phase = DiagnosticsThreadStatics.BuildPhase
201+
// Why does it return just IDisposable?
202+
use _ = concurrentLogging
198203

199-
computations
200-
|> Seq.map (fun (Node x) ->
201-
async {
202-
DiagnosticsThreadStatics.DiagnosticsLogger <- diagnosticsLogger
203-
DiagnosticsThreadStatics.BuildPhase <- phase
204-
return! x
205-
})
206-
|> Async.Parallel
207-
|> wrapThreadStaticInfo
208-
|> Node
204+
let injectLogger i computation =
205+
let logger = concurrentLogging.GetLoggerForTask($"NodeCode.Parallel {i}")
206+
207+
async {
208+
DiagnosticsThreadStatics.DiagnosticsLogger <- logger
209+
DiagnosticsThreadStatics.BuildPhase <- phase
210+
return! unwrapNode computation
211+
}
212+
213+
return!
214+
computations
215+
|> Seq.mapi injectLogger
216+
|> Async.Parallel
217+
|> wrapThreadStaticInfo
218+
|> Node
219+
}
209220

210221
[<RequireQualifiedAccess>]
211222
module GraphNode =

src/Compiler/Facilities/DiagnosticsLogger.fs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ open System.Reflection
1313
open System.Threading
1414
open Internal.Utilities.Library
1515
open Internal.Utilities.Library.Extras
16+
open System.Collections.Concurrent
1617

1718
/// Represents the style being used to format errors
1819
[<RequireQualifiedAccess>]
@@ -883,3 +884,17 @@ type StackGuard(maxDepth: int, name: string) =
883884

884885
static member GetDepthOption(name: string) =
885886
GetEnvInteger ("FSHARP_" + name + "StackGuardDepth") StackGuard.DefaultDepth
887+
888+
type CaptureDiagnosticsConcurrently() =
889+
let target = DiagnosticsThreadStatics.DiagnosticsLogger
890+
let loggers = ResizeArray()
891+
892+
member _.GetLoggerForTask(name) : DiagnosticsLogger =
893+
let logger = CapturingDiagnosticsLogger(name)
894+
loggers.Add logger
895+
logger
896+
897+
interface IDisposable with
898+
member _.Dispose() =
899+
for logger in loggers do
900+
logger.CommitDelayedDiagnostics target

src/Compiler/Facilities/DiagnosticsLogger.fsi

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,3 +463,10 @@ type CompilationGlobalsScope =
463463
member DiagnosticsLogger: DiagnosticsLogger
464464

465465
member BuildPhase: BuildPhase
466+
467+
type CaptureDiagnosticsConcurrently =
468+
new: unit -> CaptureDiagnosticsConcurrently
469+
470+
member GetLoggerForTask: string -> DiagnosticsLogger
471+
472+
interface IDisposable

tests/FSharp.Compiler.UnitTests/BuildGraphTests.fs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -235,22 +235,36 @@ module BuildGraphTests =
235235
Assert.shouldBeTrue graphNode.HasValue
236236
Assert.shouldBe (ValueSome 1) (graphNode.TryPeekValue())
237237

238-
238+
type ExampleException(msg) = inherit System.Exception(msg)
239+
239240
[<Fact>]
240241
let internal ``NodeCode preserves DiagnosticsThreadStatics`` () =
241242
let random =
242243
let rng = Random()
243244
fun n -> rng.Next n
244245

245-
let job phase _ = node {
246+
let job phase i = node {
246247
do! random 10 |> Async.Sleep |> NodeCode.AwaitAsync
247248
Assert.Equal(phase, DiagnosticsThreadStatics.BuildPhase)
249+
DiagnosticsThreadStatics.DiagnosticsLogger.DebugDisplay()
250+
|> Assert.shouldBe $"DiagnosticsLogger(NodeCode.Parallel {i})"
251+
252+
errorR (ExampleException $"job {i}")
248253
}
249254

250255
let work (phase: BuildPhase) =
251256
node {
252-
use _ = new CompilationGlobalsScope(DiscardErrorsLogger, phase)
253-
let! _ = Seq.init 8 (job phase) |> NodeCode.Parallel
257+
let n = 8
258+
let logger = CapturingDiagnosticsLogger("test NodeCode")
259+
use _ = new CompilationGlobalsScope(logger, phase)
260+
let! _ = Seq.init n (job phase) |> NodeCode.Parallel
261+
262+
let diags = logger.Diagnostics |> List.map fst
263+
264+
diags |> List.map _.Phase |> Set |> Assert.shouldBe (Set.singleton phase)
265+
diags |> List.map _.Exception.Message
266+
|> Assert.shouldBe (List.init n <| sprintf "job %d")
267+
254268
Assert.Equal(phase, DiagnosticsThreadStatics.BuildPhase)
255269
}
256270

0 commit comments

Comments
 (0)