c# - Implement AsyncManualResetEvent using Lazy<T> to determine if the task has been awaited -


i'm implementing asyncmanualresetevent based on stephen toub's example. however, know if event, or specifically, underlying task<t> has been waited on.

i've investigated task class, , there doesn't seem sensible way determine if has ever been 'awaited' or if continuation has been added.

in case however, control access underlying task source, can listen calls waitasync method instead. in thinking how this, decided use lazy<t> , see if has been created.

sealed class asyncmanualresetevent {     public bool haswaiters => tcs.isvaluecreated;      public asyncmanualresetevent() {         reset();     }      public task waitasync() => tcs.value.task;      public void set() {         if (tcs.isvaluecreated) {             tcs.value.trysetresult(result: true);         }     }      public void reset() {         tcs = new lazy<taskcompletionsource<bool>>(lazythreadsafetymode.publicationonly);     }      lazy<taskcompletionsource<bool>> tcs; } 

my question then, whether safe approach, guarantee there never orphaned/lost continuations while event being reset?

if wanted know if called await on task (not fact called waitasync()) make custom awaiter acts wrapper taskawaiter used m_tcs.task.

public class asyncmanualresetevent {     private volatile completion _completion = new completion();      public bool haswaiters => _completion.haswaiters;      public completion waitasync()     {         return _completion;     }      public void set()     {         _completion.set();     }      public void reset()     {         while (true)         {             var completion = _completion;             if (!completion.iscompleted ||                 interlocked.compareexchange(ref _completion, new completion(), completion) == completion)                 return;         }     } }  public class completion {     private readonly taskcompletionsource<bool> _tcs;     private readonly completionawaiter _awaiter;      public completion()     {         _tcs = new taskcompletionsource<bool>(taskcreationoptions.runcontinuationsasynchronously);         _awaiter = new completionawaiter(_tcs.task, this);     }      public completionawaiter getawaiter() => _awaiter;     public bool iscompleted => _tcs.task.iscompleted;     public bool haswaiters { get; private set; }     public void set() => _tcs.trysetresult(true);      public struct completionawaiter : icriticalnotifycompletion     {         private readonly taskawaiter _taskawaiter;         private readonly completion _parent;          internal completionawaiter(task task, completion parent)         {             _parent = parent;             _taskawaiter = task.getawaiter();         }          public bool iscompleted => _taskawaiter.iscompleted;         public void getresult() => _taskawaiter.getresult();         public void oncompleted(action continuation)         {             _parent.haswaiters = true;             _taskawaiter.oncompleted(continuation);         }          public void unsafeoncompleted(action continuation)         {             _parent.haswaiters = true;             _taskawaiter.unsafeoncompleted(continuation);         }     } } 

now if registered continuation oncompleted or unsafeoncompleted bool haswaiters become true.

i added taskcreationoptions.runcontinuationsasynchronously fix issue stephen fixes task.factory.startnew @ end of article (it introduced .net after article written).


if want see if called waitasync can simplify lot, need class hold flag , completion source.

public class asyncmanualresetevent {     private volatile completionwrapper _completionwrapper = new completionwrapper();      public task waitasync()     {         var wrapper = _completionwrapper;         wrapper.waitasynccalled = true;         return wrapper.tcs.task;     }      public bool waitasynccalled     {         { return _completionwrapper.waitasynccalled; }     }      public void set() {         _completionwrapper.tcs.trysetresult(true); }      public void reset()     {         while (true)         {             var wrapper = _completionwrapper;             if (!wrapper.tcs.task.iscompleted ||                 interlocked.compareexchange(ref _completionwrapper, new completionwrapper(), wrapper) == wrapper)                 return;         }     }     private class completionwrapper     {         public taskcompletionsource<bool> tcs { get; } = new taskcompletionsource<bool>(taskcreationoptions.runcontinuationsasynchronously);         public bool waitasynccalled { get; set; }     } } 

Comments

Popular posts from this blog

sublimetext3 - what keyboard shortcut is to comment/uncomment for this script tag in sublime -

java - No use of nillable="0" in SOAP Webservice -

ubuntu - Laravel 5.2 quickstart guide gives Not Found Error -