* corlib_test.dll.sources: added Consts.cs.
[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 using System.Collections;
33
34 namespace System.Threading
35 {
36 #if NET_2_0
37         [ComVisible (true)]
38 #endif
39         public sealed class Timer : MarshalByRefObject, IDisposable
40         {
41 #region Timer instance fields
42                 TimerCallback callback;
43                 object state;
44                 long due_time_ms;
45                 long period_ms;
46                 long next_run; // in ticks
47                 bool disposed;
48 #endregion
49
50 #region Timer static fields
51                 static Thread scheduler;
52                 static Hashtable jobs;
53                 static AutoResetEvent change_event;
54 #endregion
55
56                 /* we use a static initializer to avoid race issues with the thread creation */
57                 static Timer ()
58                 {
59                         change_event = new AutoResetEvent (false);
60                         jobs = new Hashtable ();
61                         scheduler = new Thread (SchedulerThread);
62                         scheduler.IsBackground = true;
63                         scheduler.Start ();
64                 }
65
66                 static long Ticks ()
67                 {
68                         /* use a monotonic time value later */
69                         return DateTime.UtcNow.Ticks;
70                 }
71
72                 static private void SchedulerThread ()
73                 {
74                         Thread.CurrentThread.Name = "Timer-Scheduler";
75                         while (true) {
76                                 long min_wait = long.MaxValue;
77                                 lock (jobs) {
78                                         ArrayList expired = null;
79                                         long ticks = Ticks ();
80                                         foreach (DictionaryEntry entry in jobs) {
81                                                 Timer t1 = entry.Value as Timer;
82                                                 if (t1.next_run <= ticks) {
83                                                         ThreadPool.QueueUserWorkItem (new WaitCallback (t1.callback), t1.state);
84                                                         if (t1.period_ms == -1 || ((t1.period_ms == 0 | t1.period_ms == Timeout.Infinite) && t1.due_time_ms != Timeout.Infinite)) {
85                                                                 t1.next_run = long.MaxValue;
86                                                                 if (expired == null)
87                                                                         expired = new ArrayList ();
88                                                                 expired.Add (t1);
89                                                         } else {
90                                                                 t1.next_run = ticks + TimeSpan.TicksPerMillisecond * t1.period_ms;
91                                                         }
92                                                 }
93                                                 if (t1.next_run != long.MaxValue) {
94                                                         min_wait = Math.Min (min_wait, t1.next_run - ticks);
95                                                 }
96                                         }
97                                         if (expired != null) {
98                                                 int count = expired.Count;
99                                                 for (int i = 0; i < count; ++i) {
100                                                         jobs.Remove (expired [i]);
101                                                 }
102                                                 expired.Clear ();
103                                                 if (count > 50)
104                                                         expired = null;
105                                         }
106                                 }
107                                 if (min_wait != long.MaxValue) {
108                                         change_event.WaitOne ((int)(min_wait / TimeSpan.TicksPerMillisecond), true);
109                                 } else {
110                                         change_event.WaitOne (Timeout.Infinite, true);
111                                 }
112                         }
113                 }
114
115                 public Timer (TimerCallback callback, object state, int dueTime, int period)
116                 {
117                         if (dueTime < -1)
118                                 throw new ArgumentOutOfRangeException ("dueTime");
119
120                         if (period < -1)
121                                 throw new ArgumentOutOfRangeException ("period");
122
123                         Init (callback, state, dueTime, period);
124                 }
125
126                 public Timer (TimerCallback callback, object state, long dueTime, long period)
127                 {
128                         if (dueTime < -1)
129                                 throw new ArgumentOutOfRangeException ("dueTime");
130
131                         if (period < -1)
132                                 throw new ArgumentOutOfRangeException ("period");
133
134                         Init (callback, state, dueTime, period);
135                 }
136
137                 public Timer (TimerCallback callback, object state, TimeSpan dueTime, TimeSpan period)
138                         : this (callback, state, (long)dueTime.TotalMilliseconds, (long)period.TotalMilliseconds)
139                 {
140                 }
141
142                 [CLSCompliant(false)]
143                 public Timer (TimerCallback callback, object state, uint dueTime, uint period)
144                         : this (callback, state, (long) dueTime, (long) period)
145                 {
146                 }
147
148 #if NET_2_0
149                 public Timer (TimerCallback callback)
150                 {
151                         Init (callback, this, Timeout.Infinite, Timeout.Infinite);
152                 }
153 #endif
154
155                 void Init (TimerCallback callback, object state, long dueTime, long period)
156                 {
157                         this.callback = callback;
158                         this.state = state;
159
160                         Change (dueTime, period);
161                 }
162
163                 public bool Change (int dueTime, int period)
164                 {
165                         return Change ((long)dueTime, (long)period);
166                 }
167
168                 public bool Change (long dueTime, long period)
169                 {
170                         if(dueTime > 4294967294)
171                                 throw new NotSupportedException ("Due time too large");
172
173                         if(period > 4294967294)
174                                 throw new NotSupportedException ("Period too large");
175
176                         if (dueTime < -1)
177                                 throw new ArgumentOutOfRangeException ("dueTime");
178
179                         if (period < -1)
180                                 throw new ArgumentOutOfRangeException ("period");
181
182                         if (disposed)
183                                 return false;
184
185                         due_time_ms = dueTime;
186                         period_ms = period;
187                         if (dueTime == 0) {
188                                 next_run = Ticks ();
189                         } else if (dueTime == Timeout.Infinite) {
190                                 next_run = long.MaxValue;
191                         } else {
192                                 next_run = dueTime * TimeSpan.TicksPerMillisecond + Ticks ();
193                         }
194                         lock (jobs) {
195                                 if (next_run != long.MaxValue) {
196                                         Timer t = jobs [this] as Timer;
197                                         if (t == null)
198                                                 jobs [this] = this;
199                                         change_event.Set ();
200                                 } else {
201                                         jobs.Remove (this);
202                                 }
203                         }
204                         return true;
205                 }
206
207                 public bool Change (TimeSpan dueTime, TimeSpan period)
208                 {
209                         return Change ((long)dueTime.TotalMilliseconds, (long)period.TotalMilliseconds);
210                 }
211
212                 [CLSCompliant(false)]
213                 public bool Change (uint dueTime, uint period)
214                 {
215                         if (dueTime > Int32.MaxValue)
216                                 throw new NotSupportedException ("Due time too large");
217
218                         if (period > Int32.MaxValue)
219                                 throw new NotSupportedException ("Period too large");
220
221                         return Change ((long) dueTime, (long) period);
222                 }
223
224                 public void Dispose ()
225                 {
226                         disposed = true;
227                         lock (jobs) {
228                                 jobs.Remove (this);
229                         }
230                 }
231
232                 public bool Dispose (WaitHandle notifyObject)
233                 {
234                         Dispose ();
235                         NativeEventCalls.SetEvent_internal (notifyObject.Handle);
236                         return true;
237                 }
238
239         }
240 }
241