Skip to content

Commit b416593

Browse files
github-actions[bot]Repo AssistCopilot
authored
Add CompilerMessage warning to groupBy and groupByAsync for parallel consumption requirement (#235)
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: Repo Assist <[email protected]> Co-authored-by: Copilot <[email protected]>
1 parent 24c47ce commit b416593

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)