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