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