Skip to content

Commit f154e36

Browse files
committed
AsyncEnumerableBuffer throws ObjectDisposedException for access after disposal.
Add docs.
1 parent e52aa3d commit f154e36

4 files changed

Lines changed: 41 additions & 12 deletions

File tree

AsyncEnumerableExtensions/AsyncEnumerableExtensions.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@
44
<TargetFramework>netstandard1.1</TargetFramework>
55
<PackageId>CXuesong.AsyncEnumerableExtensions</PackageId>
66
<Authors>CXuesong</Authors>
7-
<Version>0.1.3</Version>
7+
<Version>0.1.4</Version>
88
<Description>Some rudimentary utilities to flavor Ix.Async. Such as asynchronous generator methods.</Description>
99
<RepositoryUrl>https://github.com/CXuesong/AsyncEnumerableExtensions</RepositoryUrl>
1010
<RepositoryType>git</RepositoryType>
1111
<PackageReleaseNotes>See https://github.com/CXuesong/AsyncEnumerableExtensions/releases .</PackageReleaseNotes>
1212
<PackageLicenseUrl>https://github.com/CXuesong/AsyncEnumerableExtensions/blob/master/LICENSE.txt</PackageLicenseUrl>
1313
<PackageProjectUrl>https://github.com/CXuesong/AsyncEnumerableExtensions</PackageProjectUrl>
1414
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
15+
<GenerateDocumentationFile>True</GenerateDocumentationFile>
1516
<SignAssembly>true</SignAssembly>
1617
<AssemblyOriginatorKeyFile>../AsyncEnumerableExtensions.snk</AssemblyOriginatorKeyFile>
1718
</PropertyGroup>

AsyncEnumerableExtensions/AsyncEnumerableFactory.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,43 @@ namespace AsyncEnumerableExtensions
1212
public static class AsyncEnumerableFactory
1313
{
1414

15+
/// <summary>
16+
/// Creates an instance of <see cref="IAsyncEnumerable{T}"/> from an asynchronous generator method.
17+
/// </summary>
18+
/// <typeparam name="T">Type of the items.</typeparam>
19+
/// <param name="generator">Generator method.</param>
20+
/// <returns>The encapsulated <see cref="IAsyncEnumerable{T}"/>.</returns>
21+
/// <remarks>
22+
/// <para>The <paramref name="generator"/> parameter should use <see cref="IAsyncEnumerableSink{T}"/>
23+
/// passed in to yield the items.</para>
24+
/// <para>In this overload, the asynchronous generator accepts a <see cref="CancellationToken"/>
25+
/// which is cancelled when the <see cref="IAsyncEnumerator{T}"/> is disposed.</para>
26+
/// </remarks>
27+
/// <see cref="IAsyncEnumerableSink{T}.Yield(T)"/>
28+
/// <see cref="IAsyncEnumerableSink{T}.Wait"/>
29+
/// <see cref="AsyncEnumerableSinkExtensions.YieldAndWait{T}(IAsyncEnumerableSink{T},T)"/>
1530
public static IAsyncEnumerable<T> FromAsyncGenerator<T>(Func<IAsyncEnumerableSink<T>, CancellationToken, Task> generator)
1631
{
32+
if (generator == null) throw new ArgumentNullException(nameof(generator));
1733
return new TaskAsyncEnumerable<T>(generator);
1834
}
1935

36+
/// <summary>
37+
/// Creates an instance of <see cref="IAsyncEnumerable{T}"/> from an asynchronous generator method.
38+
/// </summary>
39+
/// <typeparam name="T">Type of the items.</typeparam>
40+
/// <param name="generator">Generator method.</param>
41+
/// <returns>The encapsulated <see cref="IAsyncEnumerable{T}"/>.</returns>
42+
/// <remarks>
43+
/// <para>The <paramref name="generator"/> parameter should use <see cref="IAsyncEnumerableSink{T}"/>
44+
/// passed in to yield the items. </para>
45+
/// </remarks>
46+
/// <see cref="IAsyncEnumerableSink{T}.Yield(T)"/>
47+
/// <see cref="IAsyncEnumerableSink{T}.Wait"/>
48+
/// <see cref="AsyncEnumerableSinkExtensions.YieldAndWait{T}(IAsyncEnumerableSink{T},T)"/>
2049
public static IAsyncEnumerable<T> FromAsyncGenerator<T>(Func<IAsyncEnumerableSink<T>, Task> generator)
2150
{
51+
if (generator == null) throw new ArgumentNullException(nameof(generator));
2252
return new TaskAsyncEnumerable<T>(generator);
2353
}
2454

AsyncEnumerableExtensions/IAsyncEnumerableSink.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public interface IAsyncEnumerableSink<in T>
3232
/// <param name="cancellationToken">The token used to cancel waiting.</param>
3333
/// <returns>A task that completes when the yielded item has been consumed.</returns>
3434
/// <exception cref="OperationCanceledException">The wait operation has been cancelled.</exception>
35-
/// <exception cref="ObjectDisposedException">The sink does not accepts items anymore.</exception>
35+
/// <exception cref="ObjectDisposedException">The sink does not accept items anymore.</exception>
3636
Task Wait(CancellationToken cancellationToken);
3737
}
3838

AsyncEnumerableExtensions/TaskAsyncEnumerable.cs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,26 @@
88

99
namespace AsyncEnumerableExtensions
1010
{
11-
public class TaskAsyncEnumerable<T> : IAsyncEnumerable<T>
11+
internal class TaskAsyncEnumerable<T> : IAsyncEnumerable<T>
1212
{
1313

1414
private readonly Func<IAsyncEnumerableSink<T>, CancellationToken, Task> generator;
15-
private readonly bool acceptsCancellationToken;
1615

1716
public TaskAsyncEnumerable(Func<IAsyncEnumerableSink<T>, Task> sourceTask)
1817
{
1918
if (sourceTask == null) throw new ArgumentNullException(nameof(sourceTask));
2019
generator = (sink, ct) => sourceTask(sink);
21-
acceptsCancellationToken = false;
2220
}
2321

2422
public TaskAsyncEnumerable(Func<IAsyncEnumerableSink<T>, CancellationToken, Task> sourceTask)
2523
{
2624
this.generator = sourceTask ?? throw new ArgumentNullException(nameof(sourceTask));
27-
acceptsCancellationToken = true;
2825
}
2926

3027
/// <inheritdoc />
3128
public IAsyncEnumerator<T> GetEnumerator()
3229
{
33-
return new Enumerator(generator, acceptsCancellationToken);
30+
return new Enumerator(generator);
3431
}
3532

3633
private class Enumerator : IAsyncEnumerator<T>
@@ -43,7 +40,7 @@ private class Enumerator : IAsyncEnumerator<T>
4340
private CancellationToken lastMoveNextCancellationToken = CancellationToken.None;
4441
private CancellationTokenSource lastCombinedCancellationTokenSource;
4542

46-
public Enumerator(Func<IAsyncEnumerableSink<T>, CancellationToken, Task> generator, bool acceptsCancellationToken)
43+
public Enumerator(Func<IAsyncEnumerableSink<T>, CancellationToken, Task> generator)
4744
{
4845
Debug.Assert(generator != null);
4946
this.generator = generator;
@@ -188,10 +185,11 @@ public bool Yield(IEnumerable<T> items)
188185

189186
public Task Wait(CancellationToken cancellationToken)
190187
{
191-
cancellationToken.ThrowIfCancellationRequested();
188+
if (cancellationToken.IsCancellationRequested)
189+
return Task.Delay(-1, cancellationToken);
192190
lock (syncLock)
193191
{
194-
if (queue == null) throw new ObjectDisposedException(nameof(AsyncEnumerableBuffer<T>));
192+
if (queue == null) return ObjectDisposedTask;
195193
if (queue.Count == 0) return CompletedTask;
196194
if (onQueueExhaustedTcs == null)
197195
{
@@ -249,8 +247,8 @@ internal void Terminate()
249247
{
250248
lock (syncLock)
251249
{
252-
onQueueExhaustedTcs?.TrySetCanceled();
253-
onYieldTcs?.TrySetCanceled();
250+
onQueueExhaustedTcs?.TrySetException(new ObjectDisposedException(nameof(AsyncEnumerableBuffer<T>)));
251+
onYieldTcs?.TrySetException(new ObjectDisposedException(nameof(AsyncEnumerableBuffer<T>)));
254252
onQueueExhaustedTcs = null;
255253
onYieldTcs = null;
256254
queue = null;

0 commit comments

Comments
 (0)