Advertisement
Guest User

Untitled

a guest
Jan 11th, 2020
453
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 3.95 KB | None | 0 0
  1. /////////////////////////////////////////////////////////////////////////////////
  2. // paint.net                                                                   //
  3. // Copyright (C) dotPDN LLC, Rick Brewster, and contributors.                  //
  4. // All Rights Reserved.                                                        //
  5. /////////////////////////////////////////////////////////////////////////////////
  6.  
  7. // RELEASED INTO THE PUBLIC DOMAIN -- Do what you want with it, but I'm not liable for anything. -Rick Brewster
  8.  
  9. using System;
  10. using System.Threading;
  11.  
  12. namespace PaintDotNet.Threading
  13. {
  14.     // This code goes along with this Twitter thread: https://twitter.com/rickbrewPDN/status/1215811335805521920
  15.  
  16.     // version 2 -- January 11th, 2020
  17.     // FIXED: Previous version had a bug whereby the first call to Exit() on the first
  18.     //        thread that called Enter() would end up signaling the event. We need the
  19.     //        _last_ call to Exit() on the _first_ thread that called Enter() to do
  20.     //        the signaling. Otherwise re-entrancy support is completely broken.
  21.  
  22.     // Also worth pointing out is that this class is trusting that you use it correctly.
  23.     // A malicious thread could just call Exit() without having ever called Enter() and
  24.     // it would break you.
  25.  
  26.     /// <summary>
  27.     /// Implements a critical section that can be unlocked precisely once. This is useful
  28.     /// for guarding lazy initialization and avoiding a lock convoy once the value is
  29.     /// published.
  30.     ///
  31.     /// The first thread that calls Enter will acquire the lock, and then all other threads
  32.     /// will block until that first thread calls Exit. At that point, all threads will be
  33.     /// released and all further calls into Enter will return immediately. This helps to
  34.     /// avoid a lock convoy once the lock is released by the first thread.
  35.     /// </summary>
  36.     public sealed class SingleUseCriticalSection
  37.     {
  38.         private const int invalidThreadID = -0x0b4db33f; // thread IDs can't be negative
  39.  
  40.         private int enterCount;
  41.         private int exitCount;
  42.         private volatile ManualResetEvent resetEvent;
  43.         private int ownerThreadID = invalidThreadID;
  44.  
  45.         public SingleUseCriticalSection()
  46.         {
  47.             this.enterCount = 0;
  48.             this.resetEvent = new ManualResetEvent(false);
  49.         }
  50.  
  51.         public void Enter()
  52.         {
  53.             if (Interlocked.Increment(ref this.enterCount) == 1)
  54.             {
  55.                 this.ownerThreadID = Thread.CurrentThread.ManagedThreadId;
  56.             }
  57.             else if (Thread.CurrentThread.ManagedThreadId == this.ownerThreadID)
  58.             {
  59.                 // Support re-entrant access on the same thread.
  60.                 // We won't necessarily get a correct value for this if we're not on the thread
  61.                 // that entered the lock. It's possible that 2 threads race to call Enter(),  
  62.                 // one will get the lock and set the thread ID, and the other will call Enter()
  63.                 // and get invalidThreadID for the thread ID. However, since that isn't a valid
  64.                 // value for a managed thread ID, this will be okay.
  65.  
  66.                 // In order to make sure that the _last_ call to Exit() on the _first_ thread
  67.                 // that called Enter() is the one that signals the event, we perturb the exit count
  68.                 Interlocked.Decrement(ref this.exitCount);
  69.             }
  70.             else
  71.             {
  72.                 ManualResetEvent resetEvent = this.resetEvent;
  73.                 if (resetEvent != null)
  74.                 {
  75.                     resetEvent.WaitOne();
  76.                 }
  77.             }
  78.         }
  79.  
  80.         public void Exit()
  81.         {
  82.             if (Interlocked.Increment(ref this.exitCount) == 1)
  83.             {
  84.                 ManualResetEvent resetEvent = this.resetEvent;
  85.                 this.resetEvent = null;
  86.                 resetEvent.Set();
  87.             }
  88.         }
  89.     }
  90. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement