using PaintDotNet.SystemLayer; using System; using System.Threading; namespace PaintDotNet.Threading { /// /// Threading primitive that allows you to "count" and to wait on two conditions: /// 1. Empty -- this is when we have not dished out any "tokens" /// 2. NotFull -- this is when we currently have 1 or more "tokens" out in the wild /// Note that the tokens given by Acquire() *must* be disposed. Otherwise things /// won't work right! /// public class WaitableCounter { /// /// The minimum value that may be passed to the constructor for initialization. /// public static int MinimumCount { get { return WaitHandleArray.MinimumCount; } } /// /// The maximum value that may be passed to the construct for initialization. /// public static int MaximumCount { get { return WaitHandleArray.MaximumCount; } } private sealed class CounterToken : IDisposable { private WaitableCounter parent; private int index; public int Index { get { return this.index; } } public CounterToken(WaitableCounter parent, int index) { this.parent = parent; this.index = index; } public void Dispose() { parent.Release(this); } } private WaitHandleArray freeEvents; // each of these is signaled (set) when the corresponding slot is 'free' private WaitHandleArray inUseEvents; // each of these is signaled (set) when the corresponding slot is 'in use' private object theLock; public WaitableCounter(int maxCount) { if (maxCount < 1 || maxCount > 64) { throw new ArgumentOutOfRangeException("maxCount", "must be between 1 and 64, inclusive"); } this.freeEvents = new WaitHandleArray(maxCount); this.inUseEvents = new WaitHandleArray(maxCount); for (int i = 0; i < maxCount; ++i) { this.freeEvents[i] = new ManualResetEvent(true); this.inUseEvents[i] = new ManualResetEvent(false); } this.theLock = new object(); } private void Release(CounterToken token) { ((ManualResetEvent)this.inUseEvents[token.Index]).Reset(); ((ManualResetEvent)this.freeEvents[token.Index]).Set(); } public IDisposable AcquireToken() { lock (this.theLock) { int index = WaitForNotFull(); ((ManualResetEvent)this.freeEvents[index]).Reset(); ((ManualResetEvent)this.inUseEvents[index]).Set(); return new CounterToken(this, index); } } public bool IsEmpty() { return IsEmpty(0); } public bool IsEmpty(uint msTimeout) { return freeEvents.AreAllSignaled(msTimeout); } public void WaitForEmpty() { freeEvents.WaitAll(); } public int WaitForNotFull() { int returnVal = freeEvents.WaitAny(); return returnVal; } } }