View | Details | Raw Unified | Return to bug 315999
Collapse All | Expand All

(-)System.Threading/Timer.cs (-158 / +91 lines)
Lines 29-34 Link Here
29
//
29
//
30
30
31
using System.Runtime.InteropServices;
31
using System.Runtime.InteropServices;
32
using System.Collections;
32
33
33
namespace System.Threading
34
namespace System.Threading
34
{
35
{
Lines 37-161 Link Here
37
#endif
38
#endif
38
	public sealed class Timer : MarshalByRefObject, IDisposable
39
	public sealed class Timer : MarshalByRefObject, IDisposable
39
	{
40
	{
40
		sealed class Runner : MarshalByRefObject
41
#region Timer instance fields
41
		{
42
		TimerCallback callback;
42
			ManualResetEvent wait;
43
		object state;
43
			AutoResetEvent start_event;
44
		long due_time_ms;
44
			TimerCallback callback;
45
		long period_ms;
45
			object state;
46
		long next_run; // in ticks
46
			int dueTime;
47
		bool disposed;
47
			int period;
48
#endregion
48
			bool disposed;
49
			bool aborted;
50
49
51
			public Runner (TimerCallback callback, object state, AutoResetEvent start_event)
50
#region Timer static fields
52
			{
51
		static Thread scheduler;
53
				this.callback = callback;
52
		static Hashtable jobs;
54
				this.state = state;
53
		static AutoResetEvent change_event;
55
				this.start_event = start_event;
54
#endregion
56
				this.wait = new ManualResetEvent (false);
57
			}
58
55
59
			public int DueTime {
56
		/* we use a static initializer to avoid race issues with the thread creation */
60
				get { return dueTime; }
57
		static Timer ()
61
				set { dueTime = value; }
58
		{
62
			}
59
			change_event = new AutoResetEvent (false);
60
			jobs = new Hashtable ();
61
			scheduler = new Thread (SchedulerThread);
62
			scheduler.IsBackground = true;
63
			scheduler.Start ();
64
		}
63
65
64
			public int Period {
66
		static long Ticks ()
65
				get { return period; }
67
		{
66
				set { period = value == 0 ? Timeout.Infinite : value; }
68
			/* use a monotonic time value later */
67
			}
69
			return DateTime.UtcNow.Ticks;
70
		}
68
71
69
			bool WaitForDueTime ()
72
		static private void SchedulerThread ()
70
			{
73
		{
71
				if (dueTime > 0) {
74
			Thread.CurrentThread.Name = "Timer-Scheduler";
72
					bool signaled;
75
			while (true) {
73
					do {
76
				long min_wait = long.MaxValue;
74
						wait.Reset ();
77
				lock (jobs) {
75
						signaled = wait.WaitOne (dueTime, false);
78
					long ticks = Ticks ();
76
					} while (signaled == true && !disposed && !aborted);
79
					foreach (DictionaryEntry entry in jobs) {
77
80
						Timer t1 = entry.Value as Timer;
78
					if (!signaled)
81
						if (t1.next_run <= ticks) {
79
						callback (state);
82
							ThreadPool.QueueUserWorkItem (new WaitCallback (t1.callback), t1.state);
80
83
							if (t1.period_ms == -1)
81
					if (disposed)
84
								t1.next_run = long.MaxValue;
82
						return false;
85
							else
83
				}
86
								t1.next_run = ticks + TimeSpan.TicksPerMillisecond * t1.period_ms;
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
						}
87
						}
139
88
						if (t1.next_run != long.MaxValue) {
140
						signaled = wait.WaitOne (period, false);
89
							min_wait = Math.Min (min_wait, t1.next_run - ticks);
141
142
						if (aborted || disposed)
143
							break;
144
145
						if (!signaled) {
146
							callback (state);
147
						} else if (!WaitForDueTime ()) {
148
							return;
149
						}
90
						}
150
					}
91
					}
151
				}
92
				}
93
				if (min_wait != long.MaxValue) {
94
					change_event.WaitOne ((int)(min_wait / TimeSpan.TicksPerMillisecond), true);
95
				} else {
96
					change_event.WaitOne (Timeout.Infinite, true);
97
				}
152
			}
98
			}
153
		}
99
		}
154
100
155
		Runner runner;
156
		AutoResetEvent start_event;
157
		WeakReference weak_t;
158
159
		public Timer (TimerCallback callback, object state, int dueTime, int period)
101
		public Timer (TimerCallback callback, object state, int dueTime, int period)
160
		{
102
		{
161
			if (dueTime < -1)
103
			if (dueTime < -1)
Lines 175-185 Link Here
175
			if (period < -1)
117
			if (period < -1)
176
				throw new ArgumentOutOfRangeException ("period");
118
				throw new ArgumentOutOfRangeException ("period");
177
119
178
			Init (callback, state, (int) dueTime, (int) period);
120
			Init (callback, state, dueTime, period);
179
		}
121
		}
180
122
181
		public Timer (TimerCallback callback, object state, TimeSpan dueTime, TimeSpan period)
123
		public Timer (TimerCallback callback, object state, TimeSpan dueTime, TimeSpan period)
182
			: this (callback, state, Convert.ToInt32(dueTime.TotalMilliseconds), Convert.ToInt32(period.TotalMilliseconds))
124
			: this (callback, state, (long)dueTime.TotalMilliseconds, (long)period.TotalMilliseconds)
183
		{
125
		{
184
		}
126
		}
185
127
Lines 196-231 Link Here
196
		}
138
		}
197
#endif
139
#endif
198
140
199
		void Init (TimerCallback callback, object state, int dueTime, int period)
141
		void Init (TimerCallback callback, object state, long dueTime, long period)
200
		{
142
		{
201
			start_event = new AutoResetEvent (false);
143
			this.callback = callback;
202
			runner = new Runner (callback, state, start_event);
144
			this.state = state;
203
			Change (dueTime, period);
204
			Thread t = new Thread (new ThreadStart (runner.Start));
205
145
206
			weak_t = new WeakReference (t);
146
			Change (dueTime, period);
207
			
208
			t.IsBackground = true;
209
			t.Start ();
210
		}
147
		}
211
148
212
		public bool Change (int dueTime, int period)
149
		public bool Change (int dueTime, int period)
213
		{
150
		{
214
			if (dueTime < -1)
151
			return Change ((long)dueTime, (long)period);
215
				throw new ArgumentOutOfRangeException ("dueTime");
216
217
			if (period < -1)
218
				throw new ArgumentOutOfRangeException ("period");
219
220
			if (runner == null)
221
				return false;
222
223
			start_event.Reset ();
224
			runner.Abort ();
225
			runner.DueTime = dueTime;
226
			runner.Period = period;
227
			start_event.Set ();
228
			return true;
229
		}
152
		}
230
153
231
		public bool Change (long dueTime, long period)
154
		public bool Change (long dueTime, long period)
Lines 236-247 Link Here
236
			if(period > 4294967294)
159
			if(period > 4294967294)
237
				throw new NotSupportedException ("Period too large");
160
				throw new NotSupportedException ("Period too large");
238
161
239
			return Change ((int) dueTime, (int) period);
162
			if (dueTime < -1)
163
				throw new ArgumentOutOfRangeException ("dueTime");
164
165
			if (period < -1)
166
				throw new ArgumentOutOfRangeException ("period");
167
168
			if (disposed)
169
				return false;
170
171
			due_time_ms = dueTime;
172
			period_ms = period;
173
			if (dueTime == 0) {
174
				next_run = Ticks ();
175
			} else if (dueTime == Timeout.Infinite) {
176
				next_run = long.MaxValue;
177
			} else {
178
				next_run = dueTime * TimeSpan.TicksPerMillisecond + Ticks ();
179
			}
180
			lock (jobs) {
181
				if (next_run != long.MaxValue) {
182
					Timer t = jobs [this] as Timer;
183
					if (t == null)
184
						jobs [this] = this;
185
					change_event.Set ();
186
				} else {
187
					jobs.Remove (this);
188
				}
189
			}
190
			return true;
240
		}
191
		}
241
192
242
		public bool Change (TimeSpan dueTime, TimeSpan period)
193
		public bool Change (TimeSpan dueTime, TimeSpan period)
243
		{
194
		{
244
			return Change (Convert.ToInt32(dueTime.TotalMilliseconds), Convert.ToInt32(period.TotalMilliseconds));
195
			return Change ((long)dueTime.TotalMilliseconds, (long)period.TotalMilliseconds);
245
		}
196
		}
246
197
247
		[CLSCompliant(false)]
198
		[CLSCompliant(false)]
Lines 253-275 Link Here
253
			if (period > Int32.MaxValue)
204
			if (period > Int32.MaxValue)
254
				throw new NotSupportedException ("Period too large");
205
				throw new NotSupportedException ("Period too large");
255
206
256
			return Change ((int) dueTime, (int) period);
207
			return Change ((long) dueTime, (long) period);
257
		}
208
		}
258
209
259
		public void Dispose ()
210
		public void Dispose ()
260
		{
211
		{
261
			Thread t = (Thread)weak_t.Target;
212
			disposed = true;
262
				
213
			lock (jobs) {
263
			if (t != null && t.IsAlive) {
214
				jobs.Remove (this);
264
				if (t != Thread.CurrentThread)
265
					t.Abort ();
266
			}
215
			}
267
268
			if (runner != null) {
269
				runner.Dispose ();
270
				runner = null;
271
			}
272
			GC.SuppressFinalize (this);
273
		}
216
		}
274
217
275
		public bool Dispose (WaitHandle notifyObject)
218
		public bool Dispose (WaitHandle notifyObject)
Lines 279-294 Link Here
279
			return true;
222
			return true;
280
		}
223
		}
281
224
282
		~Timer ()
283
		{
284
			Thread t = (Thread)weak_t.Target;
285
			
286
			if (t != null && t.IsAlive)
287
				t.Abort ();
288
289
			if (runner != null)
290
				runner.Abort ();
291
		}
292
	}
225
	}
293
}
226
}
294
227

Return to bug 315999