Skip to content

Commit a488161

Browse files
committed
Give IAsyncDisposable a higher precedence than IDisposable, part of #97
1 parent 79b57bb commit a488161

File tree

2 files changed

+23
-28
lines changed

2 files changed

+23
-28
lines changed

src/FSharp.Control.TaskSeq.Test/TaskSeq.Using.Tests.fs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,10 @@ type private MultiDispose(disposed: int ref) =
2626
inherit OneGetter()
2727

2828
interface IDisposable with
29-
member _.Dispose() = disposed.Value <- !disposed + 1
29+
member _.Dispose() = disposed.Value <- 1
3030

3131
interface IAsyncDisposable with
32-
member _.DisposeAsync() = ValueTask(task { do disposed.Value <- !disposed + 1 })
32+
member _.DisposeAsync() = ValueTask(task { do disposed.Value <- -1 })
3333

3434
let private check = TaskSeq.length >> Task.map (should equal 1)
3535

@@ -67,7 +67,7 @@ let ``CE task: Using when type implements IDisposable and IAsyncDisposable`` ()
6767
}
6868

6969
check ts
70-
|> Task.map (fun _ -> disposed.Value |> should equal 1) // only one of the two dispose method should fire
70+
|> Task.map (fun _ -> disposed.Value |> should equal -1) // should prefer IAsyncDisposable, which returns -1
7171

7272
[<Fact>]
7373
let ``CE task: Using! when type implements IDisposable`` () =
@@ -103,4 +103,4 @@ let ``CE task: Using! when type implements IDisposable and IAsyncDisposable`` ()
103103
}
104104

105105
check ts
106-
|> Task.map (fun _ -> disposed.Value |> should equal 1) // only one of the two dispose method should fire
106+
|> Task.map (fun _ -> disposed.Value |> should equal -1) // should prefer IAsyncDisposable, which returns -1

src/FSharp.Control.TaskSeq/TaskSeqBuilder.fs

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -482,26 +482,18 @@ type TaskSeqBuilder() =
482482
true)
483483
)
484484

485-
member inline this.Using(disp: #IDisposable, body: #IDisposable -> TaskSeqCode<'T>) : TaskSeqCode<'T> =
485+
member inline this.Using(disp: #IAsyncDisposable, body: #IAsyncDisposable -> TaskSeqCode<'T>) : TaskSeqCode<'T> =
486486

487487
// A using statement is just a try/finally with the finally block disposing if non-null.
488-
this.TryFinally(
488+
this.TryFinallyAsync(
489489
(fun sm -> (body disp).Invoke(&sm)),
490490
(fun () ->
491-
// yes, this can be null from time to time
492491
if not (isNull (box disp)) then
493-
disp.Dispose())
494-
)
495-
496-
member inline this.For(sequence: seq<'TElement>, body: 'TElement -> TaskSeqCode<'T>) : TaskSeqCode<'T> =
497-
// A for loop is just a using statement on the sequence's enumerator...
498-
this.Using(
499-
sequence.GetEnumerator(),
500-
// ... and its body is a while loop that advances the enumerator and runs the body on each element.
501-
(fun e -> this.While((fun () -> e.MoveNext()), (fun sm -> (body e.Current).Invoke(&sm))))
492+
disp.DisposeAsync().AsTask()
493+
else
494+
Task.CompletedTask)
502495
)
503496

504-
505497
member inline _.Yield(v: 'T) : TaskSeqCode<'T> =
506498
TaskSeqCode<'T>(fun sm ->
507499
// This will yield with __stack_fin = false
@@ -513,8 +505,6 @@ type TaskSeqBuilder() =
513505
sm.Data.awaiter <- null
514506
__stack_fin)
515507

516-
member inline this.YieldFrom(source: seq<'T>) : TaskSeqCode<'T> = this.For(source, (fun v -> this.Yield(v)))
517-
518508
member inline _.Bind(task: Task<'TResult1>, continuation: ('TResult1 -> TaskSeqCode<'T>)) : TaskSeqCode<'T> =
519509
TaskSeqCode<'T>(fun sm ->
520510
let mutable awaiter = task.GetAwaiter()
@@ -581,22 +571,27 @@ type TaskSeqBuilder() =
581571
module MediumPriority =
582572
type TaskSeqBuilder with
583573

584-
member inline this.Using
585-
(
586-
disp: #IAsyncDisposable,
587-
body: #IAsyncDisposable -> TaskSeqCode<'T>
588-
) : TaskSeqCode<'T> =
574+
member inline this.Using(disp: #IDisposable, body: #IDisposable -> TaskSeqCode<'T>) : TaskSeqCode<'T> =
589575

590576
// A using statement is just a try/finally with the finally block disposing if non-null.
591-
this.TryFinallyAsync(
577+
this.TryFinally(
592578
(fun sm -> (body disp).Invoke(&sm)),
593579
(fun () ->
580+
// yes, this can be null from time to time
594581
if not (isNull (box disp)) then
595-
disp.DisposeAsync().AsTask()
596-
else
597-
Task.CompletedTask)
582+
disp.Dispose())
598583
)
599584

585+
member inline this.For(sequence: seq<'TElement>, body: 'TElement -> TaskSeqCode<'T>) : TaskSeqCode<'T> =
586+
// A for loop is just a using statement on the sequence's enumerator...
587+
this.Using(
588+
sequence.GetEnumerator(),
589+
// ... and its body is a while loop that advances the enumerator and runs the body on each element.
590+
(fun e -> this.While((fun () -> e.MoveNext()), (fun sm -> (body e.Current).Invoke(&sm))))
591+
)
592+
593+
member inline this.YieldFrom(source: seq<'T>) : TaskSeqCode<'T> = this.For(source, (fun v -> this.Yield(v)))
594+
600595
member inline this.For
601596
(
602597
source: #IAsyncEnumerable<'TElement>,

0 commit comments

Comments
 (0)