Advertisement
Guest User

Single Use Critical Section

a guest
Jan 10th, 2020
616
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 2.92 KB | None | 0 0
  1. /////////////////////////////////////////////////////////////////////////////////
  2. // paint.net                                                                   //
  3. // Copyright (C) dotPDN LLC, Rick Brewster, and contributors.                  //
  4. // All Rights Reserved.                                                        //
  5. /////////////////////////////////////////////////////////////////////////////////
  6.  
  7. using System;
  8. using System.Threading;
  9.  
  10. namespace PaintDotNet.Threading
  11. {
  12.     /// <summary>
  13.     /// Implements a critical section that can be unlocked precisely once. This is useful
  14.     /// for guarding lazy initialization and avoiding a lock convoy once the value is
  15.     /// published.
  16.     ///
  17.     /// The first thread that calls Enter will acquire the lock, and then all other threads
  18.     /// will block until that first thread calls Exit. At that point, all threads will be
  19.     /// released and all further calls into Enter will return immediately. This helps to
  20.     /// avoid a lock convoy once the lock is released by the first thread.
  21.     /// </summary>
  22.     public sealed class SingleUseCriticalSection
  23.     {
  24.         private const int invalidThreadID = -0x0b4db33f; // thread IDs can't be negative
  25.  
  26.         private int enterCount;
  27.         private int exitCount;
  28.         private volatile ManualResetEvent resetEvent;
  29.         private int ownerThreadID = invalidThreadID;
  30.  
  31.         public SingleUseCriticalSection()
  32.         {
  33.             this.enterCount = 0;
  34.             this.resetEvent = new ManualResetEvent(false);
  35.         }
  36.  
  37.         public override void Enter()
  38.         {
  39.             if (Interlocked.Increment(ref this.enterCount) == 1)
  40.             {
  41.                 this.ownerThreadID = Thread.CurrentThread.ManagedThreadId;
  42.             }
  43.             else if (Thread.CurrentThread.ManagedThreadId == this.ownerThreadID)
  44.             {
  45.                 // Support re-entrant access on the same thread.
  46.                 // We won't necessarily get a correct value for this if we're not on the thread
  47.                 // that entered the lock. It's possible that 2 threads race to call Enter(),  
  48.                 // one will get the lock and set the thread ID, and the other will call Enter()
  49.                 // and get invalidThreadID for the thread ID. However, since that isn't a valid
  50.                 // value for a managed thread ID, this will be okay.
  51.             }
  52.             else
  53.             {
  54.                 ManualResetEvent resetEvent = this.resetEvent;
  55.                 if (resetEvent != null)
  56.                 {
  57.                     resetEvent.WaitOne();
  58.                 }
  59.             }
  60.         }
  61.  
  62.         public override void Exit()
  63.         {
  64.             if (Interlocked.Increment(ref this.exitCount) == 1)
  65.             {
  66.                 ManualResetEvent resetEvent = this.resetEvent;
  67.                 this.resetEvent = null;
  68.                 resetEvent.Set();
  69.             }
  70.         }
  71.     }
  72. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement