source: other-projects/playing-in-the-street/summer-2013/trunk/Microsoft.Kinect.Toolkit/ContextEventWrapper.cs@ 28895

Last change on this file since 28895 was 28895, checked in by davidb, 10 years ago

Base Kinect Project

File size: 15.6 KB
Line 
1//-----------------------------------------------------------------------
2// <copyright file="ContextEventWrapper.cs" company="Microsoft Corporation">
3// Copyright (c) Microsoft Corporation. All rights reserved.
4// </copyright>
5// <summary>
6// This file contains the internal classes used to track the synchronization
7// context and events handling.
8// </summary>
9//-----------------------------------------------------------------------
10
11namespace Microsoft.Kinect.Toolkit
12{
13 using System;
14 using System.Reflection;
15 using System.Threading;
16
17 /// <summary>
18 /// The possible methods for sending data to the synchronization context.
19 /// </summary>
20 public enum ContextSynchronizationMethod
21 {
22 /// <summary>
23 /// The send method is used to pass synchronous message to the context.
24 /// </summary>
25 Send,
26
27 /// <summary>
28 /// The post method is used to pass an asynchronous message to the context.
29 /// </summary>
30 Post
31 }
32
33 /// <summary>
34 /// Wrapper that holds a collection of event handlers for a specific type and associates
35 /// a context with them.
36 /// </summary>
37 /// <typeparam name="T">EventArgs for the specific event.</typeparam>
38 /// <remarks>
39 /// Note that this has the implementation of IDisposable but not the interface.
40 /// You may call Dispose on it if you need to. Otherwise you can let the GC handle
41 /// things.
42 /// </remarks>
43 public class ContextEventWrapper<T> where T : EventArgs
44 {
45 /// <summary>
46 /// This holds the list of context to handler mappings.
47 /// </summary>
48 private readonly ThreadSafeCollection<ContextHandlerPair> actualHandlers;
49
50 /// <summary>
51 /// This is the method used process the message by the synchronization context.
52 /// </summary>
53 private readonly ContextSynchronizationMethod method;
54
55 /// <summary>
56 /// This keeps track of the disposed state of the object.
57 /// </summary>
58 private bool isDisposed;
59
60 /// <summary>
61 /// Initializes a new instance of the ContextEventWrapper class.
62 /// </summary>
63 /// <param name="method">Determines whether the context will use Post or Send. Default is Post.</param>
64 public ContextEventWrapper(ContextSynchronizationMethod method)
65 {
66 isDisposed = false;
67 this.method = method;
68 actualHandlers = new ThreadSafeCollection<ContextHandlerPair>();
69 }
70
71 /// <summary>
72 /// Initializes a new instance of the ContextEventWrapper class.
73 /// </summary>
74 public ContextEventWrapper() : this(ContextSynchronizationMethod.Post)
75 {
76 }
77
78 /// <summary>
79 /// Gets a value indicating whether this wrapper has any actual handlers registered.
80 /// </summary>
81 public bool HasHandlers
82 {
83 get { return actualHandlers.Count > 0; }
84 }
85
86 /// <summary>
87 /// Adds an event handler and associates it with the current context.
88 /// </summary>
89 /// <param name="originalHandler">The new event to add to the list of handlers.</param>
90 public void AddHandler(EventHandler<T> originalHandler)
91 {
92 if (originalHandler != null)
93 {
94 actualHandlers.Add(new ContextHandlerPair(originalHandler, new SynchronizationContextIdentifier(SynchronizationContext.Current)));
95 }
96 }
97
98 /// <summary>
99 /// Removes the event handler associated with the current context.
100 /// </summary>
101 /// <param name="originalHandler">The event to remove from the list of handlers.</param>
102 public void RemoveHandler(EventHandler<T> originalHandler)
103 {
104 SynchronizationContextIdentifier currentContextId = new SynchronizationContextIdentifier(SynchronizationContext.Current);
105 ContextHandlerPair pairToRemove = null;
106
107 // Find the first matching pair
108 foreach (ContextHandlerPair pair in actualHandlers)
109 {
110 EventHandler<T> handler = pair.Handler;
111 SynchronizationContextIdentifier contextId = pair.ContextId;
112
113 if (currentContextId == contextId && handler == originalHandler)
114 {
115 // Stop on first find
116 pairToRemove = pair;
117 break;
118 }
119 }
120
121 // remove if found
122 if (pairToRemove != null)
123 {
124 actualHandlers.Remove(pairToRemove);
125 }
126 }
127
128 /// <summary>
129 /// Invokes all registered event handlers.
130 /// </summary>
131 /// <param name="sender">The sender of the message.</param>
132 /// <param name="eventArgs">The event arguments to be passed to the handler.</param>
133 public void Invoke(object sender, T eventArgs)
134 {
135 if (HasHandlers)
136 {
137 // Invoke each handler on the list
138 // Note: Enumerator is a snapshotting enumerator, so this is thread-safe and reentrency safe.
139 foreach (ContextHandlerPair pair in actualHandlers)
140 {
141 EventHandler<T> handler = pair.Handler;
142 SynchronizationContext context = pair.ContextId.Context;
143
144 if (context == null)
145 {
146 handler(sender, eventArgs);
147 }
148 else if (method == ContextSynchronizationMethod.Post)
149 {
150 context.Post(SendOrPostDelegate, new ContextEventHandlerArgsWrapper(handler, sender, eventArgs));
151 }
152 else if (method == ContextSynchronizationMethod.Send)
153 {
154 context.Send(SendOrPostDelegate, new ContextEventHandlerArgsWrapper(handler, sender, eventArgs));
155 }
156 }
157 }
158 }
159
160 /// <summary>
161 /// This method marks the object as disposed.
162 /// </summary>
163 public void Dispose()
164 {
165 isDisposed = true;
166 actualHandlers.Clear();
167 }
168
169 /// <summary>
170 /// Internal handler that matches the delegates for SynchronizationContext.Post/Send.
171 /// </summary>
172 /// <param name="state">State packed as ContextEventHandlerArgsWrapper ( handler + sender + args ).</param>
173 private void SendOrPostDelegate(object state)
174 {
175 // This can get disposed before the marshalling is done
176 if (isDisposed)
177 {
178 return;
179 }
180
181 var currentState = (ContextEventHandlerArgsWrapper)state;
182 currentState.Handler(currentState.Sender, currentState.Args);
183 }
184
185 /// <summary>
186 /// Container class to hold event handler, sender and args so that it can be
187 /// marshalled using the Synchronization context.
188 /// </summary>
189 private class ContextEventHandlerArgsWrapper
190 {
191 /// <summary>
192 /// Initializes a new instance of the ContextEventHandlerArgsWrapper class.
193 /// </summary>
194 /// <param name="handler">The event handler.</param>
195 /// <param name="sender">The sending object.</param>
196 /// <param name="args">The argument object.</param>
197 public ContextEventHandlerArgsWrapper(EventHandler<T> handler, object sender, T args)
198 {
199 Handler = handler;
200 Sender = sender;
201 Args = args;
202 }
203
204 /// <summary>
205 /// Gets the associated event handler.
206 /// </summary>
207 public EventHandler<T> Handler { get; private set; }
208
209 /// <summary>
210 /// Gets the sending object.
211 /// </summary>
212 public object Sender { get; private set; }
213
214 /// <summary>
215 /// Gets the event arguments object.
216 /// </summary>
217 public T Args { get; private set; }
218 }
219
220 /// <summary>
221 /// Identifies a SynchronizationContext taking the potential that it is a DispatcherSynchronizationContext
222 /// into account. While the actual SynchronizationContext instance may change, the internal referenced
223 /// Dispatcher will not, so we use this for determining identity when available.
224 /// </summary>
225 private class SynchronizationContextIdentifier
226 {
227 /// <summary>
228 /// Name of the internal field in DispatcherSynchronizationContext to reflect on when
229 /// comparing SynchronizationContextIdentifiers.
230 /// </summary>
231 private const string DispatcherFieldName = "_dispatcher";
232
233 /// <summary>
234 /// The unqualified name of the DispatcherSynchronizationContext type.
235 /// </summary>
236 private const string DispatcherSynchronizationContextName = "DispatcherSynchronizationContext";
237
238 /// <summary>
239 /// Storage for the Dispatcher object, if one exists.
240 /// </summary>
241 private object dispatcherObject;
242
243 /// <summary>
244 /// Initializes a new instance of the SynchronizationContextIdentifier class.
245 /// </summary>
246 /// <param name="context">The SynchronizationContext to extract identity for.</param>
247 public SynchronizationContextIdentifier(SynchronizationContext context)
248 {
249 Context = context;
250
251 if (!object.ReferenceEquals(context, null))
252 {
253 Type contextType = context.GetType();
254 if (DispatcherSynchronizationContextName == contextType.Name)
255 {
256 FieldInfo dispatcherField = contextType.GetField(DispatcherFieldName, BindingFlags.Instance | BindingFlags.NonPublic);
257 if (dispatcherField != null)
258 {
259 dispatcherObject = dispatcherField.GetValue(context);
260 }
261 }
262 }
263 }
264
265 /// <summary>
266 /// Gets the SynchronizationContext held by the SynchronizationContextIdentifier.
267 /// </summary>
268 public SynchronizationContext Context { get; private set; }
269
270 /// <summary>
271 /// This method compares two SynchronizationContextIdentifiers.
272 /// </summary>
273 /// <param name="contextId1">The first SynchronizationContextIdentifier to compare.</param>
274 /// <param name="contextId2">The second SynchronizationContextIdentifier to compare.</param>
275 /// <returns>Returns true if they are equal, false otherwise.</returns>
276 public static bool operator ==(SynchronizationContextIdentifier contextId1, SynchronizationContextIdentifier contextId2)
277 {
278 if (object.ReferenceEquals(contextId1, null) || object.ReferenceEquals(contextId2, null))
279 {
280 return false;
281 }
282
283 return contextId1.Equals(contextId2);
284 }
285
286 /// <summary>
287 /// This method compares two SynchronizationContextIdentifiers.
288 /// </summary>
289 /// <param name="contextId1">The first SynchronizationContextIdentifier to compare.</param>
290 /// <param name="contextId2">The second SynchronizationContextIdentifier to compare.</param>
291 /// <returns>Returns false if they are equal, true otherwise.</returns>
292 public static bool operator !=(SynchronizationContextIdentifier contextId1, SynchronizationContextIdentifier contextId2)
293 {
294 if (object.ReferenceEquals(contextId1, null) || object.ReferenceEquals(contextId2, null))
295 {
296 return true;
297 }
298
299 return !contextId1.Equals(contextId2);
300 }
301
302 /// <summary>
303 /// Gets the hashcode of the object.
304 /// </summary>
305 /// <returns>Hashcode of the SynchronizationContextIdentifier.</returns>
306 public override int GetHashCode()
307 {
308 if (object.ReferenceEquals(dispatcherObject, null))
309 {
310 return object.ReferenceEquals(Context, null) ? 0 : Context.GetHashCode();
311 }
312 else
313 {
314 return dispatcherObject.GetHashCode();
315 }
316 }
317
318 /// <summary>
319 /// Returns whether this SynchronizationContextIdentifier is equivalent to obj.
320 /// </summary>
321 /// <param name="obj">The object to compare for equivalence.</param>
322 /// <returns>True if this SynchronizationContextIdentifier is equivalent to obj, false otherwise.</returns>
323 public override bool Equals(object obj)
324 {
325 SynchronizationContextIdentifier contextId = obj as SynchronizationContextIdentifier;
326
327 if (object.ReferenceEquals(contextId, null))
328 {
329 return false;
330 }
331
332 return this.Equals(contextId);
333 }
334
335 /// <summary>
336 /// Returns whether this SynchronizationContextIdentifier is equivalent to obj.
337 /// </summary>
338 /// <param name="contextId">The object to compare for equivalence.</param>
339 /// <returns>True if this SynchronizationContextIdentifier is equivalent to obj, false otherwise.</returns>
340 public bool Equals(SynchronizationContextIdentifier contextId)
341 {
342 if (object.ReferenceEquals(contextId, null))
343 {
344 return false;
345 }
346
347 if (dispatcherObject == null && contextId.dispatcherObject == null)
348 {
349 // If no Dispatcher is present, just compare the SynchronizationContexts.
350 return Context == contextId.Context;
351 }
352
353 return dispatcherObject == contextId.dispatcherObject;
354 }
355 }
356
357 /// <summary>
358 /// Container class to associate an event handler with a context so that they
359 /// act as a single key in a list.
360 /// </summary>
361 private class ContextHandlerPair
362 {
363 /// <summary>
364 /// Initializes a new instance of the ContextHandlerPair class.
365 /// </summary>
366 /// <param name="handler">The target handler.</param>
367 /// <param name="contextId">The target context identifier.</param>
368 public ContextHandlerPair(EventHandler<T> handler, SynchronizationContextIdentifier contextId)
369 {
370 Handler = handler;
371 ContextId = contextId;
372 }
373
374 /// <summary>
375 /// Gets the associated synchronization context identifier.
376 /// </summary>
377 public SynchronizationContextIdentifier ContextId
378 {
379 get;
380 private set;
381 }
382
383 /// <summary>
384 /// Gets the associated event handler.
385 /// </summary>
386 public EventHandler<T> Handler
387 {
388 get;
389 private set;
390 }
391 }
392 }
393}
Note: See TracBrowser for help on using the repository browser.