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
, object[] args
)
124 throw new NotImplementedException ();
127 public DispatcherOperation
BeginInvoke (Delegate method
, DispatcherPriority priority
, 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
, object[] args
)
186 throw new NotImplementedException ();
189 public object Invoke (Delegate method
, TimeSpan timeout
, object[] args
)
191 throw new NotImplementedException ();
194 public object Invoke (Delegate method
, TimeSpan timeout
, DispatcherPriority priority
, object[] args
)
196 throw new NotImplementedException ();
199 public object Invoke (Delegate method
, DispatcherPriority priority
, 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;