Replace SIZEOF_REGISTER with sizeof(mgreg_t) for consistency with sizeof(gpointer)
[mono.git] / mcs / class / corlib / System.Threading / SemaphoreSlim.cs
1 // SemaphoreSlim.cs
2 //
3 // Copyright (c) 2008 Jérémie "Garuma" Laval
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to deal
7 // in the Software without restriction, including without limitation the rights
8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be included in
13 // all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 // THE SOFTWARE.
22 //
23 //
24
25 using System;
26 using System.Diagnostics;
27
28 #if NET_4_0
29 namespace System.Threading
30 {
31         public class SemaphoreSlim : IDisposable
32         {
33                 const int spinCount = 10;
34                 const int deepSleepTime = 20;
35
36                 readonly int max;
37                 int currCount;
38                 bool isDisposed;
39
40                 EventWaitHandle handle;
41
42                 public SemaphoreSlim (int initial) : this (initial, int.MaxValue)
43                 {
44                 }
45
46                 public SemaphoreSlim (int initial, int max)
47                 {
48                         if (initial < 0 || initial > max || max < 0)
49                                 throw new ArgumentOutOfRangeException ("The initial  argument is negative, initial is greater than max, or max is not positive.");
50
51                         this.max = max;
52                         this.currCount = initial;
53                         this.handle = new ManualResetEvent (initial == 0);
54                 }
55
56                 ~SemaphoreSlim ()
57                 {
58                         Dispose(false);
59                 }
60
61                 public void Dispose ()
62                 {
63                         Dispose(true);
64                 }
65
66                 protected virtual void Dispose (bool managedRes)
67                 {
68                         isDisposed = true;
69                 }
70
71                 void CheckState ()
72                 {
73                         if (isDisposed)
74                                 throw new ObjectDisposedException ("The SemaphoreSlim has been disposed.");
75                 }
76
77                 public int CurrentCount {
78                         get {
79                                 return currCount;
80                         }
81                 }
82
83                 public int Release ()
84                 {
85                         return Release(1);
86                 }
87
88                 public int Release (int releaseCount)
89                 {
90                         CheckState ();
91                         if (releaseCount < 1)
92                                 throw new ArgumentOutOfRangeException ("releaseCount", "releaseCount is less than 1");
93
94                         // As we have to take care of the max limit we resort to CAS
95                         int oldValue, newValue;
96                         do {
97                                 oldValue = currCount;
98                                 newValue = (currCount + releaseCount);
99                                 newValue = newValue > max ? max : newValue;
100                         } while (Interlocked.CompareExchange (ref currCount, newValue, oldValue) != oldValue);
101
102                         handle.Set ();
103
104                         return oldValue;
105                 }
106
107                 public void Wait ()
108                 {
109                         Wait (CancellationToken.None);
110                 }
111
112                 public bool Wait (TimeSpan ts)
113                 {
114                         return Wait ((int)ts.TotalMilliseconds, CancellationToken.None);
115                 }
116
117                 public bool Wait (int millisecondsTimeout)
118                 {
119                         return Wait (millisecondsTimeout, CancellationToken.None);
120                 }
121
122                 public void Wait (CancellationToken token)
123                 {
124                         Wait (-1, token);
125                 }
126
127                 public bool Wait (TimeSpan ts, CancellationToken token)
128                 {
129                         CheckState();
130                         return Wait ((int)ts.TotalMilliseconds, token);
131                 }
132
133                 public bool Wait (int millisecondsTimeout, CancellationToken token)
134                 {
135                         CheckState ();
136                         if (millisecondsTimeout < -1)
137                                 throw new ArgumentOutOfRangeException ("millisecondsTimeout",
138                                                                        "millisecondsTimeout is a negative number other than -1");
139
140                         Watch sw = Watch.StartNew ();
141
142                         Func<bool> stopCondition = () => millisecondsTimeout >= 0 && sw.ElapsedMilliseconds > millisecondsTimeout;
143
144                         do {
145                                 bool shouldWait;
146                                 int result;
147
148                                 do {
149                                         token.ThrowIfCancellationRequested ();
150                                         if (stopCondition ())
151                                                 return false;
152
153                                         shouldWait = true;
154                                         result = currCount;
155
156                                         if (result > 0)
157                                                 shouldWait = false;
158                                         else
159                                                 break;
160                                 } while (Interlocked.CompareExchange (ref currCount, result - 1, result) != result);
161
162                                 if (!shouldWait) {
163                                         if (result == 1)
164                                                 handle.Reset ();
165                                         break;
166                                 }
167
168                                 SpinWait wait = new SpinWait ();
169
170                                 while (Thread.VolatileRead (ref currCount) <= 0) {
171                                         token.ThrowIfCancellationRequested ();
172                                         if (stopCondition ())
173                                                 return false;
174
175                                         if (wait.Count > spinCount)
176                                                 handle.WaitOne (Math.Min (Math.Max (millisecondsTimeout - (int)sw.ElapsedMilliseconds, 1), deepSleepTime));
177                                         else
178                                                 wait.SpinOnce ();
179                                 }
180                         } while (true);
181
182                         return true;
183                 }
184
185                 public WaitHandle AvailableWaitHandle {
186                         get {
187                                 return handle;
188                         }
189                 }
190         }
191 }
192 #endif