Roslyn: IAsyncDisposable, using statements, and async/await

Created on 28 Jan 2015  Â·  77Comments  Â·  Source: dotnet/roslyn

With the introduction of support for await inside of a finally block, I'd like to propose the following extension to the using statement in C#.

The System.IAsyncDisposable interface

public interface IAsyncDisposable : IDisposable
{
  Task DisposeAsync();
}

Modification to the using statement

When a using statement appears inside of an async method, the translation of the using statement is modified to the following.

A using statement of the form

using (ResourceType resource = expression) statement

corresponds to one of four possible expansions. When ResourceType is a non-nullable value type which implements System.IAsyncDisposable, the expansion is

{
    ResourceType resource = expression;
    try {
        statement;
    }
    finally {
        await ((IAsyncDisposable)resource).DisposeAsync();
    }
}

Otherwise, when ResourceType is a non-nullable value type which does not implement System.IAsyncDisposable, the expansion is

{
    ResourceType resource = expression;
    try {
        statement;
    }
    finally {
        ((IDisposable)resource).Dispose();
    }
}

Otherwise, when ResourceType is a nullable value type or a reference type other than dynamic, the expansion is:

{
    ResourceType resource = expression;
    try {
        statement;
    }
    finally {
        if (resource != null) {
            IAsyncDisposable tmp = resource as IAsyncDisposable;
            if (tmp != null) {
                await tmp.DisposeAsync();
            }
            else {
                ((IDisposable)resource).Dispose();
            }
        }
    }
}

Otherwise, when ResourceType is dynamic, the expansion is

{
    ResourceType resource = expression;
    IDisposable d = (IDisposable)resource;
    try {
        statement;
    }
    finally {
        if (d != null) {
            IAsyncDisposable tmp = d as IAsyncDisposable;
            if (tmp != null) {
                await tmp.DisposeAsync();
            }
            else {
                d.Dispose();
            }
        }
    }
}

The IAsyncDisposable interface has no impact on the expansion of using statements which appear in any context other than a method marked with the async modifier.

1 - Planning Area-Language Design Feature Request Feature Specification Language-C#

Most helpful comment

I find it scary that there'll be an implicit "await" in your code (i.e. re-entrancy vulnerability) with no indication of it in your code. Could you tell me your thoughts on a compound keyword?

async using (var x = <expr>)
{
   ...
}

All 77 comments

I find it scary that there'll be an implicit "await" in your code (i.e. re-entrancy vulnerability) with no indication of it in your code. Could you tell me your thoughts on a compound keyword?

async using (var x = <expr>)
{
   ...
}

I like the idea and I prefer it with @ljw1004 addition.
Also, would it be possible to change the synchronization context on which DisposeAsync is called?

@giggio, ah, excellent point. That's what sunk the idea when we thought about it a few years ago. The point is that a library would always want

await ((IAsyncDisposable)resource).DisposeAsync().ConfigureAwait(false);

but a user app would always want it without the ConfigureAwait.

I don't have an answer for you yet, but I accept your challenge :sunglasses:

@terrajobst It would be great to merge this proposal with the one that you did a few years back?

I'll dig it up and see what additional thoughts we had back then but as far as I remember, @sharwell pretty much nailed it already.

I propose an extension method be provided for the type IAsyncDisposable. However, while the use case makes sense, I'm not sure this is possible.

public static T ConfigureDispose<T>(this T disposable, bool continueOnCapturedContext)
    where T : IAsyncDisposable

This would allow you to do the following:

using (ResourceType resource = expression.ConfigureDispose(false))
{
    ...
}

I hope that expansions are conceptual but not actual, because AFAIK original "using" statement does not perform boxing to a disposable "struct"

@zahirtezcan The original post uses language and examples very similar to the actual C# Language Specification. The expansions define _behavior_; a compiler is free to emit IL code that differs from this source code provided the observable behavior matches the definition. The primary case where this happens today involves avoiding boxing of struct types.

In my apps, every method call that might go over the network or disk is one that will fail at some point during normal healthy execution of my program and I have to cope with it. So I wrote two NuGet packages (AsyncStackTraceEx and AsyncMrFlakey) so I can write

Await FooAsync().Log("a")  ' uses a library that gives better Exception.StackTrace in case of failure
Await FooAsync().Flakey()  ' lets me simulate a network/disk failure during my testing

I guess I'd now have to add overloads for .Log and .Flakey which take an IAsyncDisposable, as well as the existing overloads that take Task and Task and IAsyncAction and IAsyncOperation.

I think this is not a pattern that should be promoted. Your code should not typically rely on the result of a dispose method, and your dispose should not be long running. If you need to do lots of work in your dispose, then it's already a red flag. If that's the case, then you should just do a Task.Run() inside the dispose and return immediately.

Microsoft has been extremely apprehensive to support situations where you might implicitly block a method for an indeterminate amount of time.

I concur with @kbirger on this. The intent of a Dispose method is to release a resource opportunistically, and resource teardown should almost always be synchronous / short-running. Allowing async disposal seems to encourage the "perform some mandatory non-resource-teardown action at this deterministic time" pattern. In async methods in particular this pattern could lead to a pit of failure because (a) the async callback by definition does not run at a deterministic point and (b) it's not even guaranteed to run _at all_.

(I'm speaking only for myself, not any team within Microsoft.)

@GrabYourPitchforks, I disagree. Think of a network connection with a server. It's reasonable that "disposing" of the connection would involve sending a message to the server to let it release its resources gracefully, and awaiting until the server has either acknowledged receipt of that notification, or torn down its resources.

@ljw1004, in your example, it sounds like there's also a chance that the graceful bit won't happen, which means there could be an exception. This would be highly undesirable behavior. And again... if it's a potentially long-running task, it doesn't belong in a dispose method. If you must put it in a dispose method, then it should be fire-and-forget, which is to say Task.Run().

Just putting it out there -- even WCF Client (which you get from AddServiceReference) has "CloseAsync".

  • Not even FileStream.Dispose() meets the requirement of a short running operation that won't take an indeterminate amount of time.
  • Dispose() was never a guaranteed call. Not even the resource cleanup of CriticalFinalizerObject is guaranteed.
  • This proposal _does_ provide deterministic cleanup, in the sense that you know when your Task completes. It's very different from leaving everything up to the GC.

@ljw1004 - I think these are different. An asynchronous close has its place, and you will always have the ability to create a closing method which returns a task, but that is different from having this done automatically in a using block.

In what way is Dispose() not guaranteed? I'm not sure I grasp what you're saying here.

Another thought I have about the long-running aspect is, it seems that for clean-up code, you would want to be able to set a timeout. It sounds like it would be more elegant to just handle it using try-finally at this point, so that you can correctly handle the control logic for your cleanup.

Something that might be of interest, esp. the rationale for various decisions:

https://www.python.org/dev/peps/pep-0492/#asynchronous-context-managers-and-async-with

I agree with using async using here. Makes things clearer.

For real life use cases that fall outside what was mentioned, xUnit just added IAsyncFixture to allow for async initialization and disposal of fixture information: https://github.com/xunit/xunit/issues/424

Just because it hasn't been mentioned as far as I've seen... should it take a cancellation token? That would potentially handle @kbirger's timeout aspect, although of course a cancellation token is advisory rather than "stomp on the thread"...

Something like?:

public interface IAsyncDisposable : IDisposable
{
    Task DisposeAsync(CancellationToken token = default(CancellationToken));
}

Edit: how would this work when being used inside a theoretical using... Would you just have to be explicit and unroll it yourself?

@RichiCoder1 read the suggestion of @sharwell above which could be adapted to solve this elegantly and still use _using_:

public static T WithCancellation<T>(this T disposable, CancellationToken token)
    where T : IAsyncDisposable

using (var x = Fred().WithCancellation(c)) {
   ...
}

The issue with that is how does it get passed into the Dispose? I'm not sure how that'd actually get implemented.

@RichiCoder1 here's a rough sketch:

NetworkResource GetResource() {...}

class NetworkResource : IAsyncDisable
{
   private CancellationToken token;
   private bool configFlag;
   public NetworkResource WithCancellation(CancellationToken token) {this.token = token; return this;}
   public ConfigureAwait(bool configFlag) {this.configFlag = configFlag; return this;}
   public async ConfiguredTaskAwaiter DisposeAsync()
   {
      return DoDisposeInternal(token).ConfigureAwait(configFlag);
   }
   private async Task DoDisposeInternal(...) {...}
}

That way using (await var x = GetResource().WithCancellation(ct)) ... will end up with the correct cancellation flag for when it does the async disposing.

So, the interface would end up looking something like this?:

public interface IAsyncDisposable : IDisposable
{
    IAsyncDisposable ConfigureAwait(bool continueOnCapturedContext);
    Task DisposeAsync();
}

public interface ICancellableAsyncDisposable<T> : IDisposable 
{
    T WithCancellation(CancellationToken token);
}

And the implementer is responsible for ensuring configure await and cancellation tokens are flowed properly?

ex:

public class DisposableResource : ICancellableAsyncDisposable<DisposableResource>
{
     /* ... other properties ... */
    private CancellationToken _token = default(CancellationToken);
    private bool _continueOnCapturedContext= true;     

     public DisposableResource(string identifier) { /* ... */ }

     public DisposableResource ConfigureAwait(bool continueOnCapturedContext) { _continueOnCapturedContext = continueOnCapturedContext);

    public DisposableResource WithCancellation(CancellationToken token) { _token = token; }

    public Task DisposeAsync()
    {
         return DoExpensiveRelease(_token).ConfigureAwait(_continueOnCapturedContext);
    }
}

usage:

var cancellationToken = someCancellationSource.Token;
using(var resource = new DisposableResource(id).WithCancellation(cancellationToken).ConfigureAwait(false)) {
    /* do stuff */
}

@RichiCoder1 Don't you think that if the resource supports cancellation it should provide appropriate constructor overloads?

Since there is a Task involving, I think it wouldn't be a good idea to ignore it. It should return to the caller (awaiter) and then you can decide on ConfigureAwait, etc.

Maybe

Task task = using(var res = new AsyncDisposable(cancellationToken)) { }

and of course you can await it right away

await using(var res = new AsyncDisposable(cancellationToken)) { }

But, this can be done with some extension methods,

static async Task UsingAsync<T>(this T disposable, Action<T> action)
    where T : IAsyncDisposable
{
    try { action(disposable); }
    finally { await disposable.DisposeAsync(); }
}

static async Task UsingAsync<T>(this T disposable, Func<T, Task> func)
    where T : IAsyncDisposable
{
    try { await func(disposable); }
    finally { await disposable.DisposeAsync(); }
}

static async Task<TResult> UsingAsync<T, TResult>(this T disposable, Func<T, TResult> func)
    where T : IAsyncDisposable
{
    try { return func(disposable); }
    finally { await disposable.DisposeAsync(); }
}

static async Task<TResult> UsingAsync<T, TResult>(this T disposable, Func<T, Task<TResult>> func)
    where T : IAsyncDisposable
{
    try { return await func(disposable); }
    finally { await disposable.DisposeAsync(); }
}

right?

Thumbs up for await using(...){}.
But just for thought, if we had an interface conceptually like this:
public interface IAsyncDisposable<T> { T Dispose(); }
must that T have been a Task, or T could have been an "awaitable" object in order to make that await using work?

@zahirtezcan no, you've got the code wrong. it's not DisposeAsync that returns, its the callback. however, If an exception thrown, we will await that Task anyway (in finally) but no value returns (Task fails).

Sorry for my English... I try to mean that, for IAsyncDispose interface, do we really (technically) need to return a Task or can that return type of Dispose method be any awaitable?

@zahirtezcan I think the point is to return a Task, as it's the standard awaitable type, so one can use async/await in the DisposeAsync implementation.

:+1: for

c# async using(var resource = new Resource()) { ... }

@aluanhaddad Where is my Task?

@alrz you're right.

c# await using(var resource = new Resource()) { ... }

Is much better and covers the ConfigureAwait issue.

@aluanhaddad await is better because you need async on the enclosing method to be able to await a Task. And in your example, I still don't see where ConfigureAwait should go. Probably

await using(var res = new Res()).ConfigureAwait(false) {}

But I have no idea what production rules would allow this. In fact, using became an _expression_?

On second thought, or third, it is a little bit odd because if the using block contains await expressions then they are awaited first. I was thinking you could put ConfigureAwait as

c# await using(var resource = new Resource()) { ... }.ConfigureAwait(false);

Which seems like something you would approve of :wink:

@aluanhaddad This has a very similar issue to #6788 — there is a Task in the heart of the class. It's been suggested that new FooAsync() should return a Task. But it's not reasonable to do this just because it's IAsyncDisposable. In the same proposal I've suggested that it should be integrated with #114, #111 and #5881 so you can call ConfigureAwait when you create the object, like new FooAsync().ConfigureAwait(false) and it will be all taken care of. (also see my comment above). Although, you can't tell that if a class is IAsyncDisposable also it has an async constructor; in that case, I think #5402 would be a better approach. Note that in your example using is an _expression_, so it returns — you could use it without awaiting it or assigning it. Therefore, this would cause an extensive lookahead for the compiler to figure out that using is an _expression_ or _statement_. In #5402 I have suggested that only an _expression_ should be allowed in the body of the _using-expression_.

But if it returns a task, it is an expression. If you want to acquire a reference to the task I don't see how that can be avoided. With such syntax I would assume it was an expression. My implication was that it would always have a value. Furthermore, that value would always be a Task and not a Task<T> because it represents a dispose operation. At any rate, my allusion to expression statements was intended to be humorous.

Proposal to address the CancellationToken and ConfigureAwait issues:

Different interfaces for cancellable dispose

``` c#
public interface IAsyncDisposable : IDisposable
{
Task DisposeAsync();
}

public interface IAsyncCancellableDisposable : IAsyncDisposable
{
Task DisposeAsync(CancellationToken ct);
}

#### Optional named parameters in `async using`

``` c#
async using(var foo = new Foo())
{
    //...
}

// Compiler error if type Foo is not IAsyncCancellableDisposable
async using(var foo = new Foo(), cancellationToken:ct)
{
    //...
}

async using(var foo = new Foo(), continueOnCapturedContext:false)
{
    //...
}

async using(var foo = new Foo(), cancellationToken:ct, continueOnCapturedContext:false)
{
    //...
}

My current thoughts are that adding a CancellationToken to a Dispose method feels wrong because it seems like you're saying: "Okay, go and clean yourself up - but you may be asked to stop part of the way through". So what are the semantics meant to be if the cancellation token is triggered half way through cleaning up a network resource for example?

Secondly, I'd like to suggest that you open up the possibility to return anything that is awaitable from the DisposeAsync method. Example:

_New Interfaces_

interface IAsyncDisposable : IDisposable
{
    IAwaitable DisposeAsync();
}

interface IAwaitable : ICriticalNotifyCompletion
{
    bool IsCompleted { get; }
    void GetResult();
}

_Sample Implementation_

public struct LightweightAwaiter : IAwaitable
{
    public bool IsCompleted => false;
    public void GetResult() {}
    public void OnCompleted(Action continuation) => continuation();
    public void UnsafeOnCompleted(Action continuation) { }
}

static class SampleExtensions
{
    public static IAwaitable GetAwaiter(this IAwaitable awaiter) 
                                                   => awaiter;  // Required, but not sure why?
    public static LightweightAwaiter GetAwaiter(this int control) => new LightweightAwaiter();
}

public class Test
{
    IAwaitable Meh() => null;

    async Task Test2()
    {
        await 100;
        await Meh();
        await new LightweightAwaiter();
    }
}

Having never written a custom awaiter, I haven't tried the above sample, but it compiles perfectly fine. _Still need to figure out why GetAwaiter is required_ That said, I think it opens up a number of possibilities and I'm keen to hear your thoughts.

'async using' looks frustrating. Why not 'await using'?

I think that await using is misguiding, it would imply that the using statement returns an awaitable expression.

You are not "awaiting" the using block, it's a declaration of an asynchronous using statement, this is why I prefer async using.

If I may add my $0.02, I think it is a mistake to define

public interface IAsyncDisposable : IDisposable
{
Task DisposeAsync();
}

... because then a disposable object has both Dispose and DisposeAsync methods, which really doesn't make sense given that each of them is supposed to dispose of the resources. And actually there is nothing to stop someone from putting the async keyword on Dispose as well as on DisposeAsync, so you could end up with two different async dispose functions in the same implementation. Ideally we would redefine IDisposable like so:

public interface IDisposable
{
Task Dispose(); // return null if not async
}

However if that is not feasible then the next best thing would be:

public interface IAwaitableDisposable: IDisposable
{
///

/// Returns the Task created by an async void Dispose() implementation, or null if 
/// the implementation of Dispose is not async. 
/// </summary>


Task DisposeTask { get; }

}

The implementer could define the set accessor and use that to store the Task.

That would be a significant breaking change and a rather dangerous use of null.

I agree the first definition in my post was meant to be a nonstandard, but the second definition in my post (IAwaitableDispose) would not be a breaking change at all.

As for null, I would say that the null vs no-null values provide meaningful information in this case. It's certainly better than having two functions present that are supposed to accomplish the same thing (Dispose and DisposeAsync) which is what happens if IAsyncDisposable derives from IDisposable. If you have both Dispose and DisposeAsync present, then you need a bit somewhere to tell you which one to call. The null in my proposal provides that information.

This is better than saying that the Dispose part of the IAsyncDisposable interface should be ignored by implementors. What is it there for if nobody is allowed to implement it? The risk of null is better than the risk of someone implementing Dispose instead of DisposeAsync.

If the caller ignores DisposeTask then the result will be that the Dispose task will run in the background. This really is no worse than the possibility that the caller in IAsyncDisposble would call Dispose instead of DisposeAsync. But at least in IAwaitableDispose there is a coherent meaning to the definition of the functions, with no duplication.

Steve

Sent from my iPhone

On May 9, 2016, at 1:02 AM, Yaakov [email protected] wrote:

That would be a significant breaking change and a rather dangerous use of null.

—
You are receiving this because you commented.
Reply to this email directly or view it on GitHub

I have mixed feelings about this feature request. While it would be nice to have an 'async using' directive in some scenarios, I'm wondering if that could be encouraging bad practices.

I think the essence of IDisposable is to clear managed resources and prepare the object for garbage collection. Keeping in mind that Dispose if often called in a finally block, this should be fast, should not involve blocking IO and should not throw exceptions.

By adding IAsyncDisposable, it would encourage a Dipose to be potentially slow, possibly IO bound, and to occasionally throw exceptions (like timeouts).

When a class needs to be asynchronously cleaned up, a better practice could be to have an async method (e.g. ShutdownAsync) to gracefully cleanup the object and implement Dispose as an abortive cleanup in case ShutdownAsync failed.

@JeffCyr IDisposable was intended to simplify writing code that uses unmanaged resources properly. The primary motivation for IAsyncDisposable was code which operates on remote unmanaged resources, such as those exposed by some cloud services APIs. This leaves application developers working with these resources with a choice among the following:

  1. Add IAsyncDisposable and place the use of these resources in async methods with using statements (succinct and also optimal given the limitations required by the external API)
  2. Write verbose code which resembles the behavior of IAsyncDisposable without being able to use using statements
  3. Use IDisposable with using statements, potentially blocking threads on pending IO operations and dramatically reducing the ability of IIS to serve large numbers of clients
  4. Ignore the cleanup altogether, or perhaps "just" ignore cleanup in the event of an error of some sort (async cleanup but not wrapped in a using or equivalent)

Something that concerns me is

async using (var x = f())
{
    if (b) break;
    if (c) return 15;
    id (d) throw new Exception();
}

all these flow-of-control statements that we're used to might now cause an implicit await. I don't like implicit awaits! I wonder if those implicit awaits should be called out...

async using (var x = f())
{
    if (b) await break;
    if (c) await return 15;
    id (d) await throw new Exception();
}

I like Jeff's suggestion. It would be nice to simply have IAsyncShutdown { Task ShutdownAsync(); } to make it clear that shutting down and disposing are two different things.

Jeff seems to envision that objects would implement both IDisposable and IAsyncShutdown, with Dispose doing an immediate/abortive release of resources.

The challenge with implementing both is that if you ever did use the object in a using statement then you would have an abortive cleanup when you exit the using block. I guess you have to remember to await your shutdown before the end of the using block.

Going back to the OP's suggestion, should the using block semantics be adjusted so that if the referenced object implements IAsyncShutdown then await ShutdownAsync() is done before the using block ends? I am not sure. What about exceptions thrown from ShutdownAsync, for example?

sjb

Sent from my iPhone

On May 9, 2016, at 8:06 AM, Jeff Cyr [email protected] wrote:

I have mixed feelings about this feature request. While it would be nice to have an 'async using' directive in some scenarios, I'm wondering if that could be encouraging bad practices.

I think the essence of IDisposable is to clear managed resources and prepare the object for garbage collection. Keeping in mind that Dispose if often called in a finally block, this should be fast, should not involve blocking IO and should not throw exceptions.

By adding IAsyncDisposable, it would encourage a Dipose to be potentially slow, possibly IO bound, and to occasionally throw exceptions (like timeouts).

When a class needs to be asynchronously cleaned up, a better practice could be to have an async method (e.g. ShutdownAsync) to gracefully cleanup the object and implement Dispose as an abortive cleanup in case ShutdownAsync failed.

—
You are receiving this because you commented.
Reply to this email directly or view it on GitHub

The most popular use for using is to execute some code when a scope is being exited. Whether or not this is use to to logically free up resources or to perform some other sort of of checking or enforcement is irrelevant. It’s about attaching code to scope exiting. async/await as it is works very nicely with using because it enables your method to return a Task _without logically exiting the scope_ (by having the first await within a using block). The option to have the compiler enforce awaiting something when exiting a scope would be very useful enable cleaner code.

Existing Workaround

The current way to clean up a scope with asynchronus code can be done semi-cleanly with the help of lambdas. I recommend this pattern over just exposing cleanup as an additional API if your goal is to make forgetting to call extra APIs hard. That’s one goal of using and this pattern is even be stricter:

await AwaitGuardedObjectFactory.UseAsync(
    async instance =>
    {
        await instance.DoSomething();
        // Do other things that should happen before instance is cleaned up/completed here…
    });

class AwaitGuardedObjectFactory
{
    public static async Task UseAsync(Func<AwaitGuardedObject, Task> action)
    {
        var o = new AwaitGuardedObject();
        try
        {
            await action(o);
        }
        finally
        {
            await o.DisposeAsync();
        }
    }
}

Inheriting IDisposable?

IAsyncDisposable should not implement IDisposable.

  • This would be confusing. When would the void IDisposable.Dispose() be called relative to the Task IAsyncDisposable.DisposeAsync()?
  • If some code implements IAsyncDisposable, it shouldn’t be required to implement IDisposable. Requiring IDisposable will result in a lot of classes having boilerplate like public void Dispose() { if (!DisposeAsyncCalledCorrectly) { throw new InvalidOperationException("Please call DisposeAsync() instead."); } }.
  • If code implements IAsyncDisposable, it should be able to rely on the compiler’s using calling it. This code might want to require a compiler that supports IAsyncDisposable. It would be able to do this by not implementing IDisposable in the first place. If IAsyncDisposable implements IDisposable, then older compilers will silently compile that code (if someone stubs in IAsyncDisposable to support the newer compiler when targeting an older framework)—omitting the call/await of IAsyncDisposable.DisposeAsync()! Even if the existing using has weak reliability guarantees, it at least guarantees that Dispose() succeeds before the statement after the using block executes. It would be nice to be able to rely on the same behavior when implementing IAsyncDisposable and instructing users to use using.

If a type implements both IDisposable and IAsyncDisposable (which it may), the compiler should only call one method or the other. To maintain code backwards compatibility, I almost think in this situation the compiler should call IDisposable.Dispose() and ignore IAsyncDisposable.DisposeAsync() because that would be what the existing compiler does given the same types. Of course, the new compiler should emit a warning when this happens.

Otherwise, if some syntax for marking the using as async/await (see below) is adopted, that should decide between whether the compiler calls IDisposable.Dispose() or IAsyncDisposable.DisposeAsync(). There is no need for a compiler warning then—the user _wants_ to call the abortive/destructive IDisposable interface if not opting into the async variant of using.

Marking the Implicit await

Regarding async using () versus await using (), await sounds more correct at first because this would effectively introduce an await at the close of the using block. However, using doesn’t evaluate to an expression, so I guess async using () might be better.

Requiring async/await is crucial because developers rely on the fact that async methods execute synchronously until the first await. So some sign that await is being introduced implicitly by requiring the code to contain a corresponding await or async is important. And, again, this makes me want the keyword to be await all that much more. And it would be _better_ if that await can be where the actual await would happen because that would make things clearer when scanning the code searching for that end-of-synchronous-code point. How about something like:

using (MyScopedThingWithAsyncCleanup())
{
    // Compiler error if using expression implements IAsyncDisposable and the
    // block is not followed by “await;” a la “do {} while («boolExpression»);”
} await;

This would be unprecedented syntax, but I think it is quite clear to the reader of such code _where_ the implicit await will be introduced. I thought about putting await just inside of the braces as the last pseudo-statement, but that has the problem that it looks like a statement that wouldn’t be executed if the using block were exited by something like return or break. Though @ljw1004 ’s suggestion of requiring await at every possible exit point has merit too, but that would pollute the code quite a bit.

@ljw1004 do you want to champion this?

Yes happy to champion it!

@sharwell, regarding your earlier statements such as
Not even FileStream.Dispose() meets the requirement of a short running operation that won't take an indeterminate amount of time.

I think that's a fair point. However, I think the design considerations get a bit more nuanced than just the operation duration. It may be fair to say the operation might take some time, however, I think guaranteeing the operation doesn't throw is much more important. Consider how things ended up working (or not) when WCF did the equivalent of a TCP close rather than Abort in its implementation of IDisposable:
https://msdn.microsoft.com/en-us/library/aa355056(v=vs.110).aspx

IMO, that decision was a huge mess, because, as a result, the only recommended approach ended up being never to use the using statement with WCF. Ultimately, I tend to agree with @kbirger and @GrabYourPitchforks. If IAsyncDisposable were offered, I think the most natural implementations of it would be operations that can fail and throw. If the finally block were being executed after the try block threw an exception, the exception from running DisposeAsync() would mask the original exception, just like what happened with WCF.

So ultimately I think any asynchronous operation shouldn't be done automatically via a finally block mechanism, not necessarily just because it's long-running, but because it could fail and throw, masking any original exception.

Because the finally block runs even if the try block throws an exception, I think operations there should be the equivalent of aborting a transaction/giving up, not committing a transaction. To prevent masking another exception, I believe commit-style operations should instead happen only inside/at the end of a try block, such as:

```c#
try
{
DoWork();
await obj.CloseAsync();
}
finally
{
IDisposable disposable = obj as IDisposable;
if (disposable != null) disposable.Dispose();
}

Here's an alternate proposal: Add ICloseable and ICloseableAsync (or perhaps "Committable" instead), which automatically get called for "commit"-style Close operations, but at the end of the try block rather than inside the finally block.

```c#
public interface ICloseable : IDisposable
{
    void Close(); // or Commit(), if the interface is named ICommittable
}

public interface ICloseableAsync : IDisposable
{
    Task CloseAsync(); // I wish this could support CancellationTokens as well, but the using statement doesn't provide one...
}

Update the using statement to work as follows:

```c#
try
{
// body of using statement

// Commit
ICloseable closeable = obj as ICloseable;
if (closeable != null) obj.Close();

}
finally
{
// Abort, if not already committed
IDisposable disposable = obj as IDisposable;
if (disposable != null) disposable.Dispose();
}

And add an await using statement that works as follows:
```c#
try
{
    // body of using statement

    // Commit
    ICloseableAsync closeableAsync = obj as ICloseableAsync;
    if (closeableAsync != null) await obj.CloseAsync();
    else
    {
        ICloseable closeable = obj as ICloseable;
        if (closeable != null) closeable.Close();
    }
}
finally
{
    //  Abort, if not already committed
    IDisposable disposable = obj as IDisposable;
    if (disposable != null) disposable.Dispose();
}

In this proposal, the Close/CloseAsync operation only gets called if the body of the using statement doesn't result in an exception. I think this approach would provide the benefits of IAsyncDisposable without the WCF problem of making it unusable in practice due to masking exceptions when the body of the using statement has already thrown.

The using statement is currently designed for IDisposable,
and IDisposable was designed to free unmanaged resources,
but I personally end up using using for more scenarios.

But in reality something like this would be helpful:

interface IActivatable {
    void Activate();
    void Deactivate();
}

interface IAsyncActivatable {
    Task Activate();
    Task Deactivate();
}

using (activatable) {
    SomeCode();
}

await using (activatable) {
    await SomeCodeAsync();
}

I imagine scenarios like

using (invincibility) { ... }
await using (loadingScreen) { ... await LoadLevel(); ... }
await using (var p = this.AddProgressToUI(animated: true)) {
    await Load(p); // p.Progress = 0.5;
}
await using (extraSpeedAbility) { await Task.Delay(5000); }

and cases where something must be changed temporarily.

It would behave like the IDisposable pattern (ensuring the Deactivate method is called),
but an IActivatable can be activated and deactivated multiple times,
with async support.

Yes, you can use IDisposable for non-async scenarios, but

  • it feels like hacking the language
  • you have to choose between allocating a new disposable, or using your objects Dispose method for another reason, other than disposing.

IDisposable documentation says:
"Implement IDisposable only if you are using unmanaged resources directly."

but the using block is also useful for other scenarios.

Should at least the IDisposable interface documentation be updated? or is it really not recommended to use IDisposable for these scenarios?

IDisposable was designed to free unmanaged resources

I have to disagree. It is designed to end a scope of some activity and may incur managed tasks, like writing the final block of a crypto transform or sending a network message. Or unsubscribing event handlers, or removing a static dictionary entry, or otherwise deregistering or invoking cleanup or even just nulling fields to help GC in a (very) specialized case where someone is still holding onto your instance.

The .NET Framework marks many classes IDisposable that do not have unmanaged resources. I also use this pattern heavily in my code. using (new SingleInstanceMode()), using (list.SuppressListChangeEvents()), using (var tempFile = new TempSettingsFile()), using (var ifX = ilGenerator.EmitIf()), using (list.AssertCollectionChangedEvents(...)) etc.

But yes, I have the same goals as you w.r.t. IAsyncDisposable.

Exactly!

CancellationToken Register method uses IDisposable to subscribe / unsubscribe handlers.

But the documentation states:

Implement IDisposable only if you are using unmanaged resources directly.
If your app simply uses an object that implements IDisposable,
don't provide an IDisposable implementation.

https://msdn.microsoft.com/en-us/library/system.idisposable(v=vs.110).aspx

But using is more powerful than that.

Besides, objects that were Disposed are assumed to be non-usable, or at an invalid state,
and subsequent calls to Dispose would have no effect,

But using Dispose to other reasons than to release memory does not mean that
your object was invalidated (or disposed).

And then this happens:

void UseEffect() {
  var effect = NewEffect();

  using (effect) {
    // something
  }

  // is effect a valid instance here? (only inspecting the Dispose implementation to know)

  using (effect) {
    // Did it do something?
  }
}

Or you can allocate a new object every time you want to use the using block, and it can be used only once.

I had no idea that the current documentation says this literally all over:

Provides a mechanism for releasing unmanaged resources.

The primary use of this interface is to release unmanaged resources.

You should implement IDisposable only if your type uses unmanaged resources directly.

It's of course ridiculous. In the IDE, you're given quick actions to implement either the unmanaged IDisposable pattern or the purely managed pattern.

Also, I am of the frame of mind that the unmanaged (IDisposable + finalizer) pattern should never be used for unmanaged resources when you can implement a SafeHandle instead. Example managed Dispose + SafeHandle.

@ljw1004 With all the discussion and suggestions here, what is your current thought on what the syntax/rules should be for this proposal? Or is it too early to ask?

@binki I'm no longer the champion for this feature (I switched job last fall). In my mind it remains vexing...

I'm interesting in Kotlin continuations: https://github.com/Kotlin/kotlin-coroutines/blob/master/kotlin-coroutines-informal.md#continuation-interface -- these provide a uniform library-defined mechanism for writing any kind of resumable method (e.g. async methods, iterators, async iterators, ...). So someone could write await(...) just as a library method and it would do the same compiler transformation as C# does, or they could write yield(...) as a library method. So they must deal with the same issue as IAsyncDisposable. How do they deal with it?

Hmm, Kotlin coroutines defines an extension method with a language-supported suspend keyword (I’m confused, I can’t see that keyword defined in the docs—it’s not somehow defined by the library, is it? I don’t think Kotlin is quite like Haxe which has a rich macro syntax/codgen support enabling you to try to imitate async/await transformations without even modifying the compiler). suspend seems to be like async/await without await. Somehow calling suspend functions from other ones gives the called function the ability to suspend the caller.

How they would handle using (or use as they call it) is the same as using a C# extension method accepting either Task<T> or T where T : IAsyncDisposable which would be similar in nature to the “workaround” I wrote earlier. Unfortunately, C# starts looking ugly when each line is a new lambda call (whereas kotlin makes lambdas look almost like blocks—example of their synchronous using). But maybe this looks OK:

// This would call the Task<T> overload of UsingAsync(). There should *not*
// be parens around the await.
await GetResourceAsync().UsingAsync(async resource => {
    await resource.DoSomethingAsync();
});

…where UsingAsync would use try{…}finally{…} to call IAsyncDisposable.DisposeAsync() at the appropriate time. It requires the developer to be disciplined about keeping the instantiation/acquisition of resources and call to UsingAsync() close to each other/on the same line.

Maybe if this pattern could be promoted to the BCL, that would be enough. No need for new confusing syntax since that was hard to agree upon?

I do not want a lambda. You get capture issues and warnings, it looks ugly, it's hard to type, it's additional overhead that the try...finally await DisposeAsync() pattern does not have

@jnm2 Though, regarding capture issues, wouldn’t you have no issues beyond the same issues you have with async anyway? I.e., is there actually something you can do inside try{…} in an async function but not in a lambda? Also, I am not sure, but wouldn’t the lambda’s capture reuse the same locals-storing object that C# is already creating for the outer async function and only create an extra object for its own locals? I do expect using the lambda to add a bit of overhead that could be avoided with new syntax, though, so I guess new syntax is preferred after all.

Please consider another design
```C#

/// <summary>
/// Asynchronously disposable.
/// </summary>
public interface IAsyncDisposable
{
    /// <summary>
    /// Dispose task. Returns valid dispose task even <see cref="StartDispose"/> was not called yet. <br/>
    /// When something goes wrong and problem cannot be handled - task should be completed with unprocessable exception to hint
    /// application crash.
    /// </summary>
    Task DisposeTask { get; }

    /// <summary>
    /// Initiates async disposing, allowed to be called multiple times. Should never <see langword="throw"/> an exception.
    /// </summary>
    void StartDispose();
}

```
I found this design many times more suitable in actual framework building. But possibly both design should coexist (for different purposes).

One big change to original proposal is the ability to have valid "Task" that can be used for continuations and regardless of dispose started or not.

@dmitriyse A class desiring such “only start the disposable once” behavior could just return the same Task in subsequent calls to IAsyncDisposable.DisposeAsync(). I can sort of see how it might be easier to consume what you wrote, but I think the original proposal makes some good decisions:

  1. An interface with just Task DisposeAsync(); is simpler to implement. It only has one member.
  2. The original proposal is closer to the existing IDisposable.Dispose() which developers are already familiar with. The synchronous corollary has the same issues that are solved by your suggestion, but in practice developers either fix consuming code to only ever call Dispose() once (especially when consuming BCL classes) or they make their Dispose() implementations idempotent. Introducing a new interface where the “only” difference is that it’s asynchronous instead of synchronous makes it easier for existing C# developers to adapt to. And if you want to make your implementation idempotent, you’re always free to just return the same Task in subsequent calls.

Hi! Yes, returning the same Task from AsyncDispose is a good and possible idea. And I also share your reasons. But one case is not covered by Task DisposeAsync(). You cannot get Task and not query disposing.
So task in my suggestion works as event. You can subscribe on this task even no Dispose been started.
In the original proposal:
"Task DisposeAsync()" method is only way to get Task. So you will always start to dispose before receive awaitable Task. My library hardly requires behavior when one consumer subscrubes on a dispose Task, and other consumer firing async dispose.

I think requirement to have "dispose finished" Task-event (while don't do dispose) is not so rare . And if IAsyncDisposable will not support this case than some third party libraries will add additional contract IAsyncDisposableEx "with blackjack and hookers". Possibly it;s a reasonable solution to have IAsyncDisposableEx inherited from IAsyncDisposable with DisposedTask property, because "async using" implemented for IAsyncDisposable will also perfectly work with IAsyncDisposableEx.
So my suggestion should be transformed to:

    /// <summary>
    /// Asynchronously disposable with additional features.
    /// </summary>
    public interface IAsyncDisposableEx: IAsyncDisposable
    {
        /// <summary>
        /// Dispose task. Returns valid dispose task even <see cref="AsyncDispose"/> was not called yet. <br/>
       /// The same task will be always returned from IAsyncDisposable.AsyncDispose() method (in the future calls).<br/>
        /// When something goes wrong and problem cannot be handled - task should be completed with unprocessable exception to hint
        /// application crash.
        /// </summary>
        Task DisposedTask { get; }
    }

Please also consider to support this scenario https://github.com/dotnet/csharplang/issues/216

Possible way is:

public interface ICancellableAsync: IDisposableAsync 
{ 
    void OnException (Exception ex);
}

Such a feature could be great for inherently IO bound dispose like the TransactionScope disposal too. (Of course it would require enabling async operations in System.Transactions, which currently has no support for it.)

Otherwise ressources which requires IO bound cleanup should all provide some CloseAsync for allowing to await them in nominal cases (when no exceptions have been raised) and have the Dispose freed of those IO bound operation in those cases. The disposal would still perform those operations if required (exception prior to the CloseAync await).

The discussion here about using is a good example of one of the many thorny issues around how to evolve the c# language in view of the new async / await paradigm. Recently I opened a similar discussion about asynchrony and lock (which so far has not gained much traction). There was also a comment made elsewhere that iteration with await inside of yield can lead to similar issues as would occur if await were allowed inside of lock. Also if we look at the discussion above we can see a similar questions starting to arise for try / finally, such as how should finally deal with tasks that are being awaited in the try block, similar to the question of how using should deal with async operations.

Overall, the control structures of c# were designed at a time when the normal programming paradigm was synchronous rather than asynchronous. And when asynchrony did occur, it was usually associated with threads rather than Tasks as we have today.

It seems possible to me that we may need await versions of most of the major flow-of-control structures in the language. So we might have await using (...) {...} and await lock (...) {...} and try { ... } await catch (...) {...} await finally {...} and perhaps even await if (...) {...}. The question of how await should interact with foreach may be already answered by the C#8 proposal for async enumerables.

It's easy for me to suggest, but I would like to call on @MadsTorgersen and the rest of the MS team to step back and give this some comprehensive thought (if it is not already being done).

There is actually no need to asynchronously dispose and object. The state of an object can always be invalidated synchronously and cleanup can occur on the threadpool.
Example for disposable pattern:

protected virtual void Dispose(bool disposing)
{
    if (disposing)
    {
        if(!_disposed)
        {
            _disposed = true;
            Task.Run(async () =>
            {
                // Do asynchronous cleanup here
                await ...
            }
        }
    }
}

Why this works is a contract, that Dispose must not throw an exception.

@mlehmk That only works if Dispose has no observable behavior.

Pretend you implement that logic in FileStream that you've opened for exclusive access to a file, that you want to delete that immediately afterwards after disposing the FileStream. How would you know when you can delete the file?

Comment: since this thread was opened a couple of years ago, I have been using (ha ha) the following interface: IAsyncShutdown { Task ShutdownAsync();}. I designed my program so that when the service provider scope exits, it will shut down and then dispose all the services.

When I did this, I eventually found that I needed a _ShuttingDown_ event in the IAsyncShutdown interface taking HandledEventArgs. The reason is that the program state may not always be suitable for the execution of the _ShutdownAsync_ implemented by the service. For example, in some program states one may want to abandon an operation rather than complete it, or vice versa.

This is a language design issue, so should be moved to csharplang. Since it is already tracked as part of the async-streams feature (candidate for C# 8.0, prototyped in async-streams branch), I'll go ahead and close the present issue. Thanks

Was this page helpful?
0 / 5 - 0 ratings