2 // DispatcherObject returned by BeginInvoke must allow:
3 // * Waiting until delegate is invoked.
4 // See: BeginInvoke documentation for details
6 // Implement the "Invoke" methods, they are currently not working.
8 // Add support for disabling the dispatcher and resuming it.
9 // Add support for Waiting for new tasks to be pushed, so that we dont busy loop.
10 // Add support for aborting an operation (emit the hook.operationaborted too)
12 // Very confusing information about Shutdown: it states that shutdown is
13 // not over, until all events are unwinded, and also states that all events
14 // are aborted at that point. See 'Dispatcher.InvokeShutdown' docs,
16 // Testing reveals that
17 // -> InvokeShutdown() stops processing, even if events are available,
18 // there is no "unwinding" of events, even of higher priority events,
19 // they are just ignored.
21 // The documentation for the Dispatcher family is poorly written, complete
22 // sections are cut-and-pasted that add no value and the important pieces
23 // like (what is a frame) is not on the APIs, but scattered everywhere else
25 // -----------------------------------------------------------------------
26 // Permission is hereby granted, free of charge, to any person obtaining
27 // a copy of this software and associated documentation files (the
28 // "Software"), to deal in the Software without restriction, including
29 // without limitation the rights to use, copy, modify, merge, publish,
30 // distribute, sublicense, and/or sell copies of the Software, and to
31 // permit persons to whom the Software is furnished to do so, subject to
32 // the following conditions:
34 // The above copyright notice and this permission notice shall be
35 // included in all copies or substantial portions of the Software.
37 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
38 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
39 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
40 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
41 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
42 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
43 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
45 // Copyright (c) 2006 Novell, Inc. (http://www.novell.com)
48 // Miguel de Icaza (miguel@novell.com)
51 using System.Collections;
52 using System.Collections.Generic;
53 using System.ComponentModel;
54 using System.Security;
55 using System.Threading;
57 namespace System.Windows.Threading {
66 public sealed class Dispatcher {
67 static Dictionary<Thread, Dispatcher> dispatchers = new Dictionary<Thread, Dispatcher> ();
68 static object olock = new object ();
69 static DispatcherFrame main_execution_frame = new DispatcherFrame ();
71 const int TOP_PRIO = (int)DispatcherPriority.Send;
73 PokableQueue [] priority_queues = new PokableQueue [TOP_PRIO+1];
79 // Used to notify the dispatcher thread that new data is available
84 // The hooks for this Dispatcher
86 DispatcherHooks hooks;
89 // The current DispatcherFrame active in a given Dispatcher, we use this to
90 // keep a linked list of all active frames, so we can "ExitAll" frames when
92 DispatcherFrame current_frame;
97 for (int i = 1; i <= (int) DispatcherPriority.Send; i++)
98 priority_queues [i] = new PokableQueue ();
99 wait = new EventWaitHandle (false, EventResetMode.AutoReset);
100 hooks = new DispatcherHooks (this);
103 [EditorBrowsable (EditorBrowsableState.Never)]
104 public bool CheckAccess ()
106 return Thread.CurrentThread == base_thread;
109 [EditorBrowsable (EditorBrowsableState.Never)]
110 public void VerifyAccess ()
112 if (Thread.CurrentThread != base_thread)
113 throw new InvalidOperationException ("Invoked from a different thread");
116 public static void ValidatePriority (DispatcherPriority priority, string parameterName)
118 if (priority < DispatcherPriority.Inactive || priority > DispatcherPriority.Send)
119 throw new InvalidEnumArgumentException (parameterName);
122 public DispatcherOperation BeginInvoke (Delegate method, params object[] args)
124 throw new NotImplementedException ();
127 public DispatcherOperation BeginInvoke (Delegate method, DispatcherPriority priority, params object[] args)
129 throw new NotImplementedException ();
133 [EditorBrowsable (EditorBrowsableState.Never)]
134 public DispatcherOperation BeginInvoke (DispatcherPriority priority, Delegate method)
136 if (priority < 0 || priority > DispatcherPriority.Send)
137 throw new InvalidEnumArgumentException ("priority");
138 if (priority == DispatcherPriority.Inactive)
139 throw new ArgumentException ("priority can not be inactive", "priority");
141 throw new ArgumentNullException ("method");
143 DispatcherOperation op = new DispatcherOperation (this, priority, method);
144 Queue (priority, op);
150 [EditorBrowsable (EditorBrowsableState.Never)]
151 public DispatcherOperation BeginInvoke (DispatcherPriority priority, Delegate method, object arg)
153 if (priority < 0 || priority > DispatcherPriority.Send)
154 throw new InvalidEnumArgumentException ("priority");
155 if (priority == DispatcherPriority.Inactive)
156 throw new ArgumentException ("priority can not be inactive", "priority");
158 throw new ArgumentNullException ("method");
160 DispatcherOperation op = new DispatcherOperation (this, priority, method, arg);
162 Queue (priority, op);
168 [EditorBrowsable (EditorBrowsableState.Never)]
169 public DispatcherOperation BeginInvoke (DispatcherPriority priority, Delegate method, object arg, params object [] args)
171 if (priority < 0 || priority > DispatcherPriority.Send)
172 throw new InvalidEnumArgumentException ("priority");
173 if (priority == DispatcherPriority.Inactive)
174 throw new ArgumentException ("priority can not be inactive", "priority");
176 throw new ArgumentNullException ("method");
178 DispatcherOperation op = new DispatcherOperation (this, priority, method, arg, args);
179 Queue (priority, op);
184 public object Invoke (Delegate method, params object[] args)
186 throw new NotImplementedException ();
189 public object Invoke (Delegate method, TimeSpan timeout, params object[] args)
191 throw new NotImplementedException ();
194 public object Invoke (Delegate method, TimeSpan timeout, DispatcherPriority priority, params object[] args)
196 throw new NotImplementedException ();
199 public object Invoke (Delegate method, DispatcherPriority priority, params object[] args)
201 throw new NotImplementedException ();
205 [EditorBrowsable (EditorBrowsableState.Never)]
206 public object Invoke (DispatcherPriority priority, Delegate method)
208 if (priority < 0 || priority > DispatcherPriority.Send)
209 throw new InvalidEnumArgumentException ("priority");
210 if (priority == DispatcherPriority.Inactive)
211 throw new ArgumentException ("priority can not be inactive", "priority");
213 throw new ArgumentNullException ("method");
215 DispatcherOperation op = new DispatcherOperation (this, priority, method);
216 Queue (priority, op);
217 PushFrame (new DispatcherFrame ());
219 throw new NotImplementedException ();
223 [EditorBrowsable (EditorBrowsableState.Never)]
224 public object Invoke (DispatcherPriority priority, Delegate method, object arg)
226 if (priority < 0 || priority > DispatcherPriority.Send)
227 throw new InvalidEnumArgumentException ("priority");
228 if (priority == DispatcherPriority.Inactive)
229 throw new ArgumentException ("priority can not be inactive", "priority");
231 throw new ArgumentNullException ("method");
233 Queue (priority, new DispatcherOperation (this, priority, method, arg));
234 throw new NotImplementedException ();
238 [EditorBrowsable (EditorBrowsableState.Never)]
239 public object Invoke (DispatcherPriority priority, Delegate method, object arg, params object [] args)
241 if (priority < 0 || priority > DispatcherPriority.Send)
242 throw new InvalidEnumArgumentException ("priority");
243 if (priority == DispatcherPriority.Inactive)
244 throw new ArgumentException ("priority can not be inactive", "priority");
246 throw new ArgumentNullException ("method");
248 Queue (priority, new DispatcherOperation (this, priority, method, arg, args));
250 throw new NotImplementedException ();
254 [EditorBrowsable (EditorBrowsableState.Never)]
255 public object Invoke (DispatcherPriority priority, TimeSpan timeout, Delegate method)
257 throw new NotImplementedException ();
261 [EditorBrowsable (EditorBrowsableState.Never)]
262 public object Invoke (DispatcherPriority priority, TimeSpan timeout, Delegate method, object arg)
264 throw new NotImplementedException ();
268 [EditorBrowsable (EditorBrowsableState.Never)]
269 public object Invoke (DispatcherPriority priority, TimeSpan timeout, Delegate method, object arg, params object [] args)
271 throw new NotImplementedException ();
274 void Queue (DispatcherPriority priority, DispatcherOperation x)
276 int p = ((int) priority);
277 PokableQueue q = priority_queues [p];
284 hooks.EmitOperationPosted (x);
286 if (Thread.CurrentThread != base_thread)
290 internal void Reprioritize (DispatcherOperation op, DispatcherPriority oldpriority)
292 int oldp = (int) oldpriority;
293 PokableQueue q = priority_queues [oldp];
298 Queue (op.Priority, op);
299 hooks.EmitOperationPriorityChanged (op);
302 public static Dispatcher CurrentDispatcher {
305 Thread t = Thread.CurrentThread;
306 Dispatcher dis = FromThread (t);
311 dis = new Dispatcher (t);
312 dispatchers [t] = dis;
318 public static Dispatcher FromThread (Thread thread)
322 if (dispatchers.TryGetValue (thread, out dis))
328 public Thread Thread {
335 public static void Run ()
337 PushFrame (main_execution_frame);
341 public static void PushFrame (DispatcherFrame frame)
344 throw new ArgumentNullException ("frame");
346 Dispatcher dis = CurrentDispatcher;
348 if (dis.HasShutdownFinished)
349 throw new InvalidOperationException ("The Dispatcher has shut down");
350 if (frame.dispatcher != null)
351 throw new InvalidOperationException ("Frame is already running on a different dispatcher");
352 if ((dis.flags & Flags.Disabled) != 0)
353 throw new InvalidOperationException ("Dispatcher processing has been disabled");
355 frame.ParentFrame = dis.current_frame;
356 dis.current_frame = frame;
358 frame.dispatcher = dis;
360 dis.RunFrame (frame);
363 void PerformShutdown ()
369 h (this, new EventArgs ());
371 flags |= Flags.Shutdown;
373 h = ShutdownFinished;
375 h (this, new EventArgs ());
377 priority_queues = null;
381 void RunFrame (DispatcherFrame frame)
384 while (queue_bits != 0){
385 for (int i = TOP_PRIO; i > 0 && queue_bits != 0; i--){
386 int current_bit = queue_bits & (1 << i);
387 if (current_bit != 0){
388 PokableQueue q = priority_queues [i];
391 DispatcherOperation task;
394 task = (DispatcherOperation) q.Dequeue ();
402 if (task.Status == DispatcherOperationStatus.Aborted)
403 hooks.EmitOperationAborted (task);
405 hooks.EmitOperationCompleted (task);
410 if (HasShutdownStarted){
415 // if we are done with this queue, leave.
418 queue_bits &= ~(1 << i);
424 // If a higher-priority task comes in, go do that
426 if (current_bit < (queue_bits & ~current_bit))
432 hooks.EmitInactive ();
436 } while (frame.Continue);
439 [EditorBrowsable (EditorBrowsableState.Advanced)]
440 public DispatcherHooks Hooks {
442 get { throw new NotImplementedException (); }
445 public bool HasShutdownStarted {
447 return (flags & Flags.ShutdownStarted) != 0;
451 public bool HasShutdownFinished {
453 return (flags & Flags.Shutdown) != 0;
458 // Do no work here, so that any events are thrown on the owning thread
461 public void InvokeShutdown ()
463 flags |= Flags.ShutdownStarted;
467 public void BeginInvokeShutdown (DispatcherPriority priority)
469 throw new NotImplementedException ();
473 public static void ExitAllFrames ()
475 Dispatcher dis = CurrentDispatcher;
477 for (DispatcherFrame frame = dis.current_frame; frame != null; frame = frame.ParentFrame){
478 if (frame.exit_on_request)
479 frame.Continue = false;
482 // Stop unwinding the frames at the first frame that is
489 public DispatcherProcessingDisabled DisableProcessing ()
491 throw new NotImplementedException ();
494 public event EventHandler ShutdownStarted;
495 public event EventHandler ShutdownFinished;
496 public event DispatcherUnhandledExceptionEventHandler UnhandledException;
497 public event DispatcherUnhandledExceptionFilterEventHandler UnhandledExceptionFilter;
500 internal class PokableQueue {
501 const int initial_capacity = 32;
503 int size, head, tail;
506 internal PokableQueue (int capacity)
508 array = new object [capacity];
511 internal PokableQueue () : this (initial_capacity)
515 public void Enqueue (object obj)
517 if (size == array.Length)
520 tail = (tail+1) % array.Length;
524 public object Dequeue ()
527 throw new InvalidOperationException ();
528 object result = array[head];
530 head = (head + 1) % array.Length;
536 int newc = array.Length * 2;
537 object[] new_contents = new object[newc];
538 array.CopyTo (new_contents, 0);
539 array = new_contents;
550 public void Remove (object obj)
552 for (int i = 0; i < size; i++){
553 if (array [(head+i) % array.Length] == obj){
554 for (int j = i; j < size-i; j++)
555 array [(head +j) % array.Length] = array [(head+j+1) % array.Length];
558 size = array.Length-1;