2006-07-04 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / corlib / System.Threading / Timer.cs
1 //
2 // System.Threading.Timer.cs
3 //
4 // Authors:
5 //      Dick Porter (dick@ximian.com)
6 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 //
8 // (C) 2001, 2002 Ximian, Inc.  http://www.ximian.com
9 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30
31 using System.Runtime.InteropServices;
32
33 namespace System.Threading
34 {
35 #if NET_2_0
36         [ComVisible (true)]
37 #endif
38         public sealed class Timer : MarshalByRefObject, IDisposable
39         {
40                 sealed class Runner : MarshalByRefObject
41                 {
42                         ManualResetEvent wait;
43                         AutoResetEvent start_event;
44                         TimerCallback callback;
45                         object state;
46                         int dueTime;
47                         int period;
48                         bool disposed;
49                         bool aborted;
50
51                         public Runner (TimerCallback callback, object state, AutoResetEvent start_event)
52                         {
53                                 this.callback = callback;
54                                 this.state = state;
55                                 this.start_event = start_event;
56                                 this.wait = new ManualResetEvent (false);
57                         }
58
59                         public int DueTime {
60                                 get { return dueTime; }
61                                 set { dueTime = value; }
62                         }
63
64                         public int Period {
65                                 get { return period; }
66                                 set { period = value == 0 ? Timeout.Infinite : value; }
67                         }
68
69                         bool WaitForDueTime ()
70                         {
71                                 if (dueTime > 0) {
72                                         bool signaled;
73                                         do {
74                                                 wait.Reset ();
75                                                 signaled = wait.WaitOne (dueTime, false);
76                                         } while (signaled == true && !disposed && !aborted);
77
78                                         if (!signaled)
79                                                 callback (state);
80
81                                         if (disposed)
82                                                 return false;
83                                 }
84                                 else
85                                         callback (state);
86
87                                 return true;
88                         }
89
90                         public void Abort ()
91                         {
92                                 lock (this) {
93                                         aborted = true;
94                                         wait.Set ();
95                                 }
96                         }
97                         
98                         public void Dispose ()
99                         {
100                                 lock (this) {
101                                         disposed = true;
102                                         Abort ();
103                                 }
104                         }
105
106                         public void Start ()
107                         {
108                                 while (!disposed && start_event.WaitOne ()) {
109                                         if (disposed)
110                                                 return;
111
112                                         aborted = false;
113
114                                         if (dueTime == Timeout.Infinite)
115                                                 continue;
116
117                                         if (!WaitForDueTime ())
118                                                 return;
119
120                                         if (aborted || (period == Timeout.Infinite))
121                                                 continue;
122
123                                         bool signaled = false;
124                                         while (true) {
125                                                 if (disposed)
126                                                         return;
127
128                                                 if (aborted)
129                                                         break;
130
131                                                 try {
132                                                         wait.Reset ();
133                                                 } catch (ObjectDisposedException) {
134                                                         // FIXME: There is some race condition
135                                                         //        here when the thread is being
136                                                         //        aborted on exit.
137                                                         return;
138                                                 }
139
140                                                 signaled = wait.WaitOne (period, false);
141
142                                                 if (aborted || disposed)
143                                                         break;
144
145                                                 if (!signaled) {
146                                                         callback (state);
147                                                 } else if (!WaitForDueTime ()) {
148                                                         return;
149                                                 }
150                                         }
151                                 }
152                         }
153                 }
154
155                 Runner runner;
156                 AutoResetEvent start_event;
157                 Thread t;
158
159                 public Timer (TimerCallback callback, object state, int dueTime, int period)
160                 {
161                         if (dueTime < -1)
162                                 throw new ArgumentOutOfRangeException ("dueTime");
163
164                         if (period < -1)
165                                 throw new ArgumentOutOfRangeException ("period");
166
167                         Init (callback, state, dueTime, period);
168                 }
169
170                 public Timer (TimerCallback callback, object state, long dueTime, long period)
171                 {
172                         if (dueTime < -1)
173                                 throw new ArgumentOutOfRangeException ("dueTime");
174
175                         if (period < -1)
176                                 throw new ArgumentOutOfRangeException ("period");
177
178                         Init (callback, state, (int) dueTime, (int) period);
179                 }
180
181                 public Timer (TimerCallback callback, object state, TimeSpan dueTime, TimeSpan period)
182                         : this (callback, state, Convert.ToInt32(dueTime.TotalMilliseconds), Convert.ToInt32(period.TotalMilliseconds))
183                 {
184                 }
185
186                 [CLSCompliant(false)]
187                 public Timer (TimerCallback callback, object state, uint dueTime, uint period)
188                         : this (callback, state, (long) dueTime, (long) period)
189                 {
190                 }
191
192 #if NET_2_0
193                 public Timer (TimerCallback callback)
194                 {
195                         Init (callback, this, Timeout.Infinite, Timeout.Infinite);
196                 }
197 #endif
198
199                 void Init (TimerCallback callback, object state, int dueTime, int period)
200                 {
201                         start_event = new AutoResetEvent (false);
202                         runner = new Runner (callback, state, start_event);
203                         Change (dueTime, period);
204                         t = new Thread (new ThreadStart (runner.Start));
205                         t.IsBackground = true;
206                         t.Start ();
207                 }
208
209                 public bool Change (int dueTime, int period)
210                 {
211                         if (dueTime < -1)
212                                 throw new ArgumentOutOfRangeException ("dueTime");
213
214                         if (period < -1)
215                                 throw new ArgumentOutOfRangeException ("period");
216
217                         if (runner == null)
218                                 return false;
219
220                         start_event.Reset ();
221                         runner.Abort ();
222                         runner.DueTime = dueTime;
223                         runner.Period = period;
224                         start_event.Set ();
225                         return true;
226                 }
227
228                 public bool Change (long dueTime, long period)
229                 {
230                         if(dueTime > 4294967294)
231                                 throw new NotSupportedException ("Due time too large");
232
233                         if(period > 4294967294)
234                                 throw new NotSupportedException ("Period too large");
235
236                         return Change ((int) dueTime, (int) period);
237                 }
238
239                 public bool Change (TimeSpan dueTime, TimeSpan period)
240                 {
241                         return Change (Convert.ToInt32(dueTime.TotalMilliseconds), Convert.ToInt32(period.TotalMilliseconds));
242                 }
243
244                 [CLSCompliant(false)]
245                 public bool Change (uint dueTime, uint period)
246                 {
247                         if (dueTime > Int32.MaxValue)
248                                 throw new NotSupportedException ("Due time too large");
249
250                         if (period > Int32.MaxValue)
251                                 throw new NotSupportedException ("Period too large");
252
253                         return Change ((int) dueTime, (int) period);
254                 }
255
256                 public void Dispose ()
257                 {
258                         if (t != null && t.IsAlive) {
259                                 if (t != Thread.CurrentThread)
260                                         t.Abort ();
261                                 t = null;
262                         }
263                         if (runner != null) {
264                                 runner.Dispose ();
265                                 runner = null;
266                         }
267                         GC.SuppressFinalize (this);
268                 }
269
270                 public bool Dispose (WaitHandle notifyObject)
271                 {
272                         Dispose ();
273                         NativeEventCalls.SetEvent_internal (notifyObject.Handle);
274                         return true;
275                 }
276
277                 ~Timer ()
278                 {
279                         if (t != null && t.IsAlive)
280                                 t.Abort ();
281
282                         if (runner != null)
283                                 runner.Abort ();
284                 }
285         }
286 }
287