title: Reliable delayed execution class in C#
body: <p>To control delays between animations in an iOS game I've developed I have written this &quot;Delayer&quot; class below. But I'm experiencing some rare random crashes possible related to objects being deallocated:</p>
  <pre><code>0x102933344 - /var/containers/Bundle/Application/52DE96A6-70CF-4D3D-A6F0-3DDAB4F31347/DiscDrop.iOS.app/DiscDrop.iOS : mono_dump_native_crash_info
  0x1029295b0 - /var/containers/Bundle/Application/52DE96A6-70CF-4D3D-A6F0-3DDAB4F31347/DiscDrop.iOS.app/DiscDrop.iOS : mono_handle_native_crash
  0x10293776c - /var/containers/Bundle/Application/52DE96A6-70CF-4D3D-A6F0-3DDAB4F31347/DiscDrop.iOS.app/DiscDrop.iOS : mono_sigsegv_signal_handler_debug
  0x1dd9f69fc - /usr/lib/system/libsystem_platform.dylib : &lt;redacted&gt;
  0x1dcfd9b9c - /usr/lib/libobjc.A.dylib : &lt;redacted&gt;
  0x1dcfd9b9c - /usr/lib/libobjc.A.dylib : &lt;redacted&gt;
  0x1dddf7bb0 - /System/Library/Frameworks/CoreFoundation.framework/CoreFoundation : _CFAutoreleasePoolPop
  0x1de871744 - /System/Library/Frameworks/Foundation.framework/Foundation : &lt;redacted&gt;
  </code></pre>
  <p>so I am wondering if anyone can spot a problem with the class:</p>
  <pre><code>public class Delayer
  {
      private readonly List&lt;CancellationTokenSource&gt; cancellationTokenSources;
  
      public Delayer()
      {
          this.cancellationTokenSources = new List&lt;CancellationTokenSource&gt;();
      }
  
      public void DelayedCall(float delay, Action callback)
      {
          CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
          this.cancellationTokenSources.Add(cancellationTokenSource);
          _ = Delayer.CallbackAfterDelay(delay, cancellationTokenSource, delegate
          {
              this.cancellationTokenSources.Remove(cancellationTokenSource);
              callback?.Invoke();
          });
      }
  
      public void CancelAll()
      {
          foreach (CancellationTokenSource cancellationTokenSource in this.cancellationTokenSources)
          {
              cancellationTokenSource.Cancel();   // cancellationTokenSource.Dispose(); here doesnt help leak
          }
          this.cancellationTokenSources.Clear();
      }
  
      private static async Task CallbackAfterDelay(float delay, CancellationTokenSource cancellationTokenSource, Action callback)
      {
          await Task.Delay((int)(delay * 1000), cancellationTokenSource.Token);
          if (cancellationTokenSource.IsCancellationRequested)
          {
              return;
          }
          try
          {
              callback?.Invoke();
          }
          catch (Exception exception)
          {
              System.Diagnostics.Debug.WriteLine(&quot;### Common.Delayer.DelayedCall caught exception={0}&quot;, exception.Message);
              throw;
          }
      }
  }
  </code></pre>
  <p>This is an example of how I use it:</p>
  <pre><code>this.delayer = new Delayer();
  //...
  this.delayer.DelayedCall(delay: 0.5f, callback: delegate
  {
      this.PlaySound(duration: 0.2f);
  }
  </code></pre>