WaitableCounter.cs 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. using PaintDotNet.SystemLayer;
  2. using System;
  3. using System.Threading;
  4. namespace PaintDotNet.Threading
  5. {
  6. /// <summary>
  7. /// Threading primitive that allows you to "count" and to wait on two conditions:
  8. /// 1. Empty -- this is when we have not dished out any "tokens"
  9. /// 2. NotFull -- this is when we currently have 1 or more "tokens" out in the wild
  10. /// Note that the tokens given by Acquire() *must* be disposed. Otherwise things
  11. /// won't work right!
  12. /// </summary>
  13. public class WaitableCounter
  14. {
  15. /// <summary>
  16. /// The minimum value that may be passed to the constructor for initialization.
  17. /// </summary>
  18. public static int MinimumCount
  19. {
  20. get
  21. {
  22. return WaitHandleArray.MinimumCount;
  23. }
  24. }
  25. /// <summary>
  26. /// The maximum value that may be passed to the construct for initialization.
  27. /// </summary>
  28. public static int MaximumCount
  29. {
  30. get
  31. {
  32. return WaitHandleArray.MaximumCount;
  33. }
  34. }
  35. private sealed class CounterToken
  36. : IDisposable
  37. {
  38. private WaitableCounter parent;
  39. private int index;
  40. public int Index
  41. {
  42. get
  43. {
  44. return this.index;
  45. }
  46. }
  47. public CounterToken(WaitableCounter parent, int index)
  48. {
  49. this.parent = parent;
  50. this.index = index;
  51. }
  52. public void Dispose()
  53. {
  54. parent.Release(this);
  55. }
  56. }
  57. private WaitHandleArray freeEvents; // each of these is signaled (set) when the corresponding slot is 'free'
  58. private WaitHandleArray inUseEvents; // each of these is signaled (set) when the corresponding slot is 'in use'
  59. private object theLock;
  60. public WaitableCounter(int maxCount)
  61. {
  62. if (maxCount < 1 || maxCount > 64)
  63. {
  64. throw new ArgumentOutOfRangeException("maxCount", "must be between 1 and 64, inclusive");
  65. }
  66. this.freeEvents = new WaitHandleArray(maxCount);
  67. this.inUseEvents = new WaitHandleArray(maxCount);
  68. for (int i = 0; i < maxCount; ++i)
  69. {
  70. this.freeEvents[i] = new ManualResetEvent(true);
  71. this.inUseEvents[i] = new ManualResetEvent(false);
  72. }
  73. this.theLock = new object();
  74. }
  75. private void Release(CounterToken token)
  76. {
  77. ((ManualResetEvent)this.inUseEvents[token.Index]).Reset();
  78. ((ManualResetEvent)this.freeEvents[token.Index]).Set();
  79. }
  80. public IDisposable AcquireToken()
  81. {
  82. lock (this.theLock)
  83. {
  84. int index = WaitForNotFull();
  85. ((ManualResetEvent)this.freeEvents[index]).Reset();
  86. ((ManualResetEvent)this.inUseEvents[index]).Set();
  87. return new CounterToken(this, index);
  88. }
  89. }
  90. public bool IsEmpty()
  91. {
  92. return IsEmpty(0);
  93. }
  94. public bool IsEmpty(uint msTimeout)
  95. {
  96. return freeEvents.AreAllSignaled(msTimeout);
  97. }
  98. public void WaitForEmpty()
  99. {
  100. freeEvents.WaitAll();
  101. }
  102. public int WaitForNotFull()
  103. {
  104. int returnVal = freeEvents.WaitAny();
  105. return returnVal;
  106. }
  107. }
  108. }