Merge branch 'master' of github.com:mono/mono
[mono.git] / mcs / class / System / System.Threading / Barrier.cs
1 // 
2 // Barrier.cs
3 //  
4 // Author:
5 //       Jérémie "Garuma" Laval <jeremie.laval@gmail.com>
6 // 
7 // Copyright (c) 2009 Jérémie "Garuma" Laval
8 // 
9 // Permission is hereby granted, free of charge, to any person obtaining a copy
10 // of this software and associated documentation files (the "Software"), to deal
11 // in the Software without restriction, including without limitation the rights
12 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 // copies of the Software, and to permit persons to whom the Software is
14 // furnished to do so, subject to the following conditions:
15 // 
16 // The above copyright notice and this permission notice shall be included in
17 // all copies or substantial portions of the Software.
18 // 
19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 // THE SOFTWARE.
26
27 #if NET_4_0 || BOOTSTRAP_NET_4_0
28
29 using System;
30
31 namespace System.Threading
32 {
33         public class Barrier : IDisposable
34         {
35                 const int MaxParticipants = 32767;
36                 Action<Barrier> postPhaseAction;
37                 
38                 int participants;
39                 bool cleaned;
40                 CountdownEvent cntd;
41                 ManualResetEventSlim postPhaseEvt = new ManualResetEventSlim ();
42                 long phase;
43                 
44                 public Barrier (int participants) : this (participants, null)
45                 {
46                 }
47                 
48                 public Barrier (int participants, Action<Barrier> postPhaseAction)
49                 {
50                         if (participants < 0 || participants > MaxParticipants)
51                                 throw new ArgumentOutOfRangeException ("participants");
52                         
53                         this.participants = participants;
54                         this.postPhaseAction = postPhaseAction;
55                         
56                         InitCountdownEvent ();
57                 }
58
59                 public void Dispose ()
60                 {
61                         Dispose (true);
62                 }
63
64                 protected virtual void Dispose (bool disposing)
65                 {
66                         if (disposing){
67                                 if (cntd != null){
68                                         cntd.Dispose ();
69                                         cntd = null;
70                                 }
71                                 postPhaseAction = null;
72                                 cleaned = true;
73                         }
74                 }
75                         
76                 void InitCountdownEvent ()
77                 {
78                         postPhaseEvt = new ManualResetEventSlim (false);
79                         cntd = new CountdownEvent (participants);
80                 }
81                 
82                 public long AddParticipant ()
83                 {
84                         return AddParticipants (1);
85                 }
86
87                 static Exception GetDisposed ()
88                 {
89                         return new ObjectDisposedException ("Barrier");
90                 }
91                 
92                 public long AddParticipants (int participantCount)
93                 {
94                         if (cleaned)
95                                 throw GetDisposed ();
96                         
97                         if (participantCount < 0)
98                                 throw new InvalidOperationException ();
99                         
100                         // Basically, we try to add ourselves and return
101                         // the phase. If the call return false, we repeatdly try
102                         // to add ourselves for the next phase
103                         do {
104                                 if (cntd.TryAddCount (participantCount)) {
105                                         Interlocked.Add (ref participants, participantCount);
106                                         return phase;
107                                 }
108                         } while (true);
109                 }
110                 
111                 public void RemoveParticipant ()
112                 {
113                         RemoveParticipants (1);
114                 }
115                 
116                 public void RemoveParticipants (int participantCount)
117                 {
118                         if (cleaned)
119                                 throw GetDisposed ();
120                         if (participantCount < 0)
121                                 throw new ArgumentOutOfRangeException ("participantCount");
122                         
123                         if (cntd.Signal (participantCount))
124                                 PostPhaseAction (postPhaseEvt);
125
126                         Interlocked.Add (ref participants, -participantCount);
127                 }
128                 
129                 public void SignalAndWait ()
130                 {
131                         if (cleaned)
132                                 throw GetDisposed ();
133                         SignalAndWait ((c) => { c.Wait (); return true; });
134                 }
135                 
136                 public void SignalAndWait (CancellationToken token)
137                 {
138                         if (cleaned)
139                                 throw GetDisposed ();
140                         SignalAndWait ((c) => { c.Wait (token); return true; });
141                 }
142
143                 public bool SignalAndWait (int millisecondTimeout)
144                 {
145                         if (cleaned)
146                                 throw GetDisposed ();
147                         return SignalAndWait ((c) => c.Wait (millisecondTimeout));
148                 }
149
150                 public bool SignalAndWait (TimeSpan ts)
151                 {
152                         if (cleaned)
153                                 throw GetDisposed ();
154                         return SignalAndWait ((c) => c.Wait (ts));
155                 }
156                 
157                 public bool SignalAndWait (int millisecondTimeout, CancellationToken token)
158                 {
159                         if (cleaned)
160                                 throw GetDisposed ();
161                         return SignalAndWait ((c) => c.Wait (millisecondTimeout, token));
162                 }
163                 
164                 public bool SignalAndWait (TimeSpan ts, CancellationToken token)
165                 {
166                         if (cleaned)
167                                 throw GetDisposed ();
168                         return SignalAndWait ((c) => c.Wait (ts, token));
169                 }
170                 
171                 bool SignalAndWait (Func<CountdownEvent, bool> associate)
172                 {
173                         bool result;
174                         CountdownEvent temp = cntd;
175                         ManualResetEventSlim evt = postPhaseEvt;
176                         
177                         if (!temp.Signal ()) {
178                                 result = Wait (associate, temp, evt);
179                         } else {
180                                 result = true;
181                                 PostPhaseAction (evt);
182                         }
183                         
184                         return result;
185                 }
186                 
187                 bool Wait (Func<CountdownEvent, bool> associate, CountdownEvent temp, ManualResetEventSlim evt)
188                 {
189                         if (!associate (temp))
190                                 return false;
191                         
192                         evt.Wait ();
193                         
194                         return true;
195                 }
196                 
197                 void PostPhaseAction (ManualResetEventSlim evt)
198                 {
199                         if (postPhaseAction != null) {
200                                 try {
201                                         postPhaseAction (this);
202                                 } catch (Exception e) {
203                                         throw new BarrierPostPhaseException (e);
204                                 }
205                         }
206                         
207                         InitCountdownEvent ();
208                         phase++;
209
210                         evt.Set ();
211                 }
212                 
213                 public long CurrentPhaseNumber {
214                         get {
215                                 return phase;
216                         }
217                 }
218                 
219                 public int ParticipantCount  {
220                         get {
221                                 return participants;
222                         }
223                 }
224         }
225 }
226 #endif