Skip to content

Commit da20f09

Browse files
Repo AssistCopilot
authored andcommitted
Add CompilerMessage warning to groupBy and groupByAsync for parallel consumption requirement
Adds FS9999 warning to both groupBy and groupByAsync to alert callers that the resulting sequence must be consumed with a parallel combinator (e.g. AsyncSeq.mapAsyncParallel) to avoid deadlocks. Closes #125 Co-authored-by: Copilot <[email protected]>
1 parent 24c47ce commit da20f09

2 files changed

Lines changed: 4 additions & 0 deletions

File tree

src/FSharp.Control.AsyncSeq/AsyncSeq.fs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1983,6 +1983,7 @@ module AsyncSeq =
19831983
toAsyncSeqImpl s.tail.Value
19841984

19851985

1986+
[<CompilerMessage("The result of groupByAsync must be consumed with a parallel combinator such as AsyncSeq.mapAsyncParallel. Sequential consumption will deadlock because sub-sequence completion depends on other sub-sequences being consumed concurrently.", 9999)>]
19861987
let groupByAsync (p:'a -> Async<'k>) (s:AsyncSeq<'a>) : AsyncSeq<'k * AsyncSeq<'a>> = asyncSeq {
19871988
let groups = Dictionary<'k, AsyncSeqSrc< 'a>>()
19881989
let close group =
@@ -2014,6 +2015,7 @@ module AsyncSeq =
20142015
raise ex }
20152016
yield! go () }
20162017

2018+
[<CompilerMessage("The result of groupBy must be consumed with a parallel combinator such as AsyncSeq.mapAsyncParallel. Sequential consumption will deadlock because sub-sequence completion depends on other sub-sequences being consumed concurrently.", 9999)>]
20172019
let groupBy (p:'a -> 'k) (s:AsyncSeq<'a>) : AsyncSeq<'k * AsyncSeq<'a>> =
20182020
groupByAsync (p >> async.Return) s
20192021
#endif

src/FSharp.Control.AsyncSeq/AsyncSeq.fsi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,13 +562,15 @@ module AsyncSeq =
562562
///
563563
/// Note that the resulting async sequence has to be processed in parallel (e.g AsyncSeq.mapAsyncParallel) becaused
564564
/// completion of sub-sequences depends on completion of other sub-sequences.
565+
[<CompilerMessage("The result of groupByAsync must be consumed with a parallel combinator such as AsyncSeq.mapAsyncParallel. Sequential consumption will deadlock because sub-sequence completion depends on other sub-sequences being consumed concurrently.", 9999)>]
565566
val groupByAsync<'T, 'Key when 'Key : equality> : projection:('T -> Async<'Key>) -> source:AsyncSeq<'T> -> AsyncSeq<'Key * AsyncSeq<'T>>
566567

567568
/// Applies a key-generating function to each element and returns an async sequence containing unique keys
568569
/// and async sequences containing elements corresponding to the key.
569570
///
570571
/// Note that the resulting async sequence has to be processed in parallel (e.g AsyncSeq.mapAsyncParallel) becaused
571572
/// completion of sub-sequences depends on completion of other sub-sequences.
573+
[<CompilerMessage("The result of groupBy must be consumed with a parallel combinator such as AsyncSeq.mapAsyncParallel. Sequential consumption will deadlock because sub-sequence completion depends on other sub-sequences being consumed concurrently.", 9999)>]
572574
val groupBy<'T, 'Key when 'Key : equality> : projection:('T -> 'Key) -> source:AsyncSeq<'T> -> AsyncSeq<'Key * AsyncSeq<'T>>
573575

574576
#if (NETSTANDARD || NET)

0 commit comments

Comments
 (0)