0

I need a re-entry task, and implementing similar code to https://blogs.msdn.microsoft.com/lucian/2014/03/03/async-re-entrancy-and-the-patterns-to-deal-with-it/ (pattern 5)

but I'm wondering if the CancellationTokenSource dispose is not missing. My implementation is adding it in .ContinueWith

    private Task _fooAsyncTask;
    private CancellationTokenSource _fooAsyncCancellation;

    public async Task Button1Click()
    {
        // Assume we're being called on UI thread... if not, the two assignments must be made atomic.
        // Note: we factor out "FooHelperAsync" to avoid an await between the two assignments. 
        // without an intervening await. 
        _fooAsyncCancellation?.Cancel();
        _fooAsyncCancellation = new CancellationTokenSource();
        _fooAsyncTask = FooHelperAsync(_fooAsyncCancellation.Token);

        await _fooAsyncTask.ContinueWith(task =>
        {
            _fooAsyncCancellation.Dispose();
            _fooAsyncCancellation = null;
        });
    }

    private async Task FooHelperAsync(CancellationToken cancel)
    {
        try { if (_fooAsyncTask != null) await _fooAsyncTask; }
        catch (OperationCanceledException) { }
        cancel.ThrowIfCancellationRequested();
        await FooAsync(cancel);
    }

    private async Task FooAsync(CancellationToken cancel)
    {
        //
    }

Is that correct?

Chris
  • 1,122
  • 1
  • 12
  • 14
  • There is no need to dispose the CTS. You may dispose it, but there are no unmanaged resources behind, that it is worth to dispose it. BTW you do not need a continuation, just await _fooAsyncTask – Sir Rufo Sep 05 '17 at 07:17
  • After a second look, it is not recommended to dispose the CTS - if you do, you will get an exception on *_fooAsyncCancellation?.Cancel();* – Sir Rufo Sep 05 '17 at 07:19
  • @SirRufo "it is not recommended to dispose the CTS" - do you have referecne for that. I was just searching for some "best practices" on that matter ... – Fildor Sep 05 '17 at 07:21
  • Maybe see this: https://stackoverflow.com/q/6960520/982149 . From what I read I learn that it is only important to dispose of _linked_ CTSs. – Fildor Sep 05 '17 at 07:26
  • Thanks both for your inputs – Chris Sep 05 '17 at 07:51

1 Answers1

0

You should change your code to

  • set the _fooAsyncCancellation to null on the UI thread (in continuation it is not)
  • guarantee, that you dispose the CTS you created for that task.

Here the modified code

Task _fooAsyncTask;
CancellationTokenSource _fooAsyncCancellation;

async void button1_Click(object sender, EventArgs e)
{
    _fooAsyncCancellation?.Cancel();
    using (var cts = new CancellationTokenSource())
    {
        _fooAsyncCancellation = cts;

        try
        {
            await FooAsyncHelper(cts.Token);
        }
        catch (OperationCanceledException) { }

        if (_fooAsyncCancellation == cts)
        {
            _fooAsyncCancellation = null;
        }
    }
}

async Task FooAsyncHelper(CancellationToken cancellationToken)
{
    try
    {
        if (_fooAsyncTask != null)
        {
            await _fooAsyncTask;
        }
    }
    catch (OperationCanceledException) { }
    cancellationToken.ThrowIfCancellationRequested();
    await FooAsync(cancellationToken);
}

async Task FooAsync(CancellationToken cancellationToken)
{
    // just some sample work to do
    for (int i = 0; i < 100; i++)
    {
        await Task.Delay(100);
        cancellationToken.ThrowIfCancellationRequested();
    }
}
Sir Rufo
  • 18,395
  • 2
  • 39
  • 73
  • Not sure to understand, in your first comment to said no needs to release. Now this new approach is less elegant, but I like the idea too. – Chris Sep 06 '17 at 06:32