A call to CancellationTokenSource.Cancel is hijacked by an awaiter and never returns


a have scenario call cancellationtokensource.cancel not return. instead, execution inside cancel diverted code being cancelled place catches operationcanceledexception.

see full example on github: https://github.com/ladimolnar/samples/tree/master/sources/asyncissue

the issue can reproduced in console apps, background agents universal apps windows , background agents universal apps windows phone 8.1.

the issue cannot reproduced in universal apps windows code works expect call cancel returns immediately.

some code, let’s call “the client code” running on thread t1 , calls:

cancellationtokensource.cancel

some code, let’s call “the worker code” runs loop on threadpool. because loop contains awaiters, execution before , after awaiters may switch thread another. excerpt of worker code:

try {     // code being cancelled.     await task.delay(5000, cancellationtoken); } catch (operationcanceledexception) { }

when code above call "await task.delay(...)", execution on thread t2. expect continuation awaiter including code inside catch on either t2 or on thread other t1. surprising when cancellation happens, code inside catch shows on thread t1. call stack of thread t1 shows this:

asyncissue.exe!asyncissue.myworker.dobackgroundwork()           --- in catch (operationcanceledexception) [resuming async method]           [external code]  asyncissue.exe!asyncissue.myworker.cancelcurrentworkerloop()    --- cancellationtokensource.cancel called asyncissue.exe!asyncissue.myworker.jolt()


the thread t1 cancellationtokensource.cancel called "client code" executes cancel , before returning cancel continues executing code inside “catch (operationcanceledexception)”. catch statement async code being cancelled. long code following catch not yield calling new await, "client code" called cancel never gets control back.

my expectation cancel record cancellation request , return caller. not happen seems bug in framework. code calls cancel never expects execution diverted code being cancelled.

what described above bad enough. however, full example shows how scenario can worse. “client code” , “worker code” never expected run on same thread. there arbitration mechanism regulates access resources via locks. mechanism ensures “worker code” never enter tight loop. both “client code” , “worker code” on same threat, reliance on locks no longer prevents , “worker code” starts tight loop. client code never gets control called cancel.

here attempted repro behavior:

environment                                                         issue can reproduced console app                                                         yes background agent universal app windows                    yes background agent universal app windows phone 8.1          yes universal app windows                                           no. behavior expect. cancel returns immediately.

some information development environment:

microsoft visual studio professional 2013
version 12.0.31101.00 update 4
microsoft .net framework
version 4.5.51650

here output of console app

========================================================= loop step 1. starting loop. thread: 11 fooasync starting. before calling task.delay. thread id: 6 loop step 2. before task.delay. thread: 11 fooasync call myworker.jolt(). thread id: 6 current worker loop being cancelled. call cancellationtokensource.cancel(). thread: 6 <my comment:      here client code on thread 6 canceling task.delay "worker code" executing.> first chance exception of type 'system.threading.tasks.taskcanceledexception' occurred in mscorlib.dll wait interrupted. thread: 6 <my comment:      here in “catch (operationcanceledexception)”. fact on      thread 6 unexpected. @ point on call stack on same stack     call cancel.> doing actual work. thread: 6 loop step 4. ending loop. thread: 6 =========================================================

here output universal app windows:

========================================================= loop step 1. starting loop. thread: 4 loop step 2. before task.delay. thread: 4 onjoltworkerclick call myworker.jolt(). thread id: 3 current worker loop being cancelled. call cancellationtokensource.cancel(). thread: 3 <my comment:      here "client code" on thread 3 canceling task.delay "worker code" executing.> current worker loop cancelled. thread: 3 <my comment:      here "client code" on thread 3 gets control cancel. expect.> first chance exception of type 'system.threading.tasks.taskcanceledexception' occurred in mscorlib.dll wait interrupted. thread: 4 <my comment:      here in “catch (operationcanceledexception)”. fact on      thread 4 i'd expect. @ point on thread different      1 "client code" running.     call cancel.> doing actual work. thread: 4 loop step 4. ending loop. thread: 4 =========================================================





Visual Studio Languages  ,  .NET Framework  >  Visual C#



Comments

Popular posts from this blog

Azure DocumentDB Owner resource does not exist

job syspolicy_purge_history job fail in sqlserver 2008

Trying to register with public marketplace error with 'Get-AzureStackStampInformation'