2 // System.Threading.Timer.cs
5 // Dick Porter (dick@ximian.com)
6 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
8 // (C) 2001, 2002 Ximian, Inc. http://www.ximian.com
9 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
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:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
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.
31 using System.Runtime.InteropServices;
32 using System.Collections;
34 namespace System.Threading
39 public sealed class Timer : MarshalByRefObject, IDisposable
41 #region Timer instance fields
42 TimerCallback callback;
46 long next_run; // in ticks
50 #region Timer static fields
51 static Thread scheduler;
52 static Hashtable jobs;
53 static AutoResetEvent change_event;
56 /* we use a static initializer to avoid race issues with the thread creation */
59 change_event = new AutoResetEvent (false);
60 jobs = new Hashtable ();
61 scheduler = new Thread (SchedulerThread);
62 scheduler.IsBackground = true;
68 /* use a monotonic time value later */
69 return DateTime.UtcNow.Ticks;
72 static private void SchedulerThread ()
74 Thread.CurrentThread.Name = "Timer-Scheduler";
76 long min_wait = long.MaxValue;
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;
87 expired = new ArrayList ();
90 t1.next_run = ticks + TimeSpan.TicksPerMillisecond * t1.period_ms;
93 if (t1.next_run != long.MaxValue) {
94 min_wait = Math.Min (min_wait, t1.next_run - ticks);
97 if (expired != null) {
98 int count = expired.Count;
99 for (int i = 0; i < count; ++i) {
100 jobs.Remove (expired [i]);
107 if (min_wait != long.MaxValue) {
108 change_event.WaitOne ((int)(min_wait / TimeSpan.TicksPerMillisecond), true);
110 change_event.WaitOne (Timeout.Infinite, true);
115 public Timer (TimerCallback callback, object state, int dueTime, int period)
118 throw new ArgumentOutOfRangeException ("dueTime");
121 throw new ArgumentOutOfRangeException ("period");
123 Init (callback, state, dueTime, period);
126 public Timer (TimerCallback callback, object state, long dueTime, long period)
129 throw new ArgumentOutOfRangeException ("dueTime");
132 throw new ArgumentOutOfRangeException ("period");
134 Init (callback, state, dueTime, period);
137 public Timer (TimerCallback callback, object state, TimeSpan dueTime, TimeSpan period)
138 : this (callback, state, (long)dueTime.TotalMilliseconds, (long)period.TotalMilliseconds)
142 [CLSCompliant(false)]
143 public Timer (TimerCallback callback, object state, uint dueTime, uint period)
144 : this (callback, state, (long) dueTime, (long) period)
149 public Timer (TimerCallback callback)
151 Init (callback, this, Timeout.Infinite, Timeout.Infinite);
155 void Init (TimerCallback callback, object state, long dueTime, long period)
157 this.callback = callback;
160 Change (dueTime, period);
163 public bool Change (int dueTime, int period)
165 return Change ((long)dueTime, (long)period);
168 public bool Change (long dueTime, long period)
170 if(dueTime > 4294967294)
171 throw new NotSupportedException ("Due time too large");
173 if(period > 4294967294)
174 throw new NotSupportedException ("Period too large");
177 throw new ArgumentOutOfRangeException ("dueTime");
180 throw new ArgumentOutOfRangeException ("period");
185 due_time_ms = dueTime;
189 } else if (dueTime == Timeout.Infinite) {
190 next_run = long.MaxValue;
192 next_run = dueTime * TimeSpan.TicksPerMillisecond + Ticks ();
195 if (next_run != long.MaxValue) {
196 Timer t = jobs [this] as Timer;
207 public bool Change (TimeSpan dueTime, TimeSpan period)
209 return Change ((long)dueTime.TotalMilliseconds, (long)period.TotalMilliseconds);
212 [CLSCompliant(false)]
213 public bool Change (uint dueTime, uint period)
215 if (dueTime > Int32.MaxValue)
216 throw new NotSupportedException ("Due time too large");
218 if (period > Int32.MaxValue)
219 throw new NotSupportedException ("Period too large");
221 return Change ((long) dueTime, (long) period);
224 public void Dispose ()
232 public bool Dispose (WaitHandle notifyObject)
235 NativeEventCalls.SetEvent_internal (notifyObject.Handle);