[reflection] Coop handles icalls in System.Reflection and System.RuntimeTypeHandle...
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / Validation / BackgroundValidationSynchronizer.cs
1 // <copyright>
2 //   Copyright (c) Microsoft Corporation.  All rights reserved.
3 // </copyright>
4
5 namespace System.Activities.Presentation.Validation
6 {
7     using System.Activities.Validation;
8     using System.Runtime;
9     using System.Threading;
10     using System.Windows.Threading;
11
12     internal class BackgroundValidationSynchronizer<TValidationResult> : ValidationSynchronizer
13     {
14         internal readonly SynchronizerState Idle;
15         internal readonly SynchronizerState Validating;
16         internal readonly SynchronizerState CancellingForNextValidation;
17         internal readonly SynchronizerState CancellingForDeactivation;
18         internal readonly SynchronizerState ValidationDeactivated;
19
20         private readonly object thisLock = new object();
21
22         private Func<ValidationReason, CancellationToken, TValidationResult> validationWork;
23         private Action<TValidationResult> updateWork;
24
25         private CancellationTokenSource cancellationTokenSource;
26         private TaskDispatcher dispatcher;
27
28         private SynchronizerState currentState;
29         private ValidationReason lastValidationReason;
30
31         private int deactivationReferenceCount;
32
33         internal BackgroundValidationSynchronizer(TaskDispatcher dispatcher, Func<ValidationReason, CancellationToken, TValidationResult> validationWork, Action<TValidationResult> updateWork)
34         {
35             Fx.Assert(validationWork != null, "validationWork should not be null and is ensured by caller.");
36             Fx.Assert(updateWork != null, "updateWork should not be null and is ensured by caller.");
37
38             this.Idle = new IdleState(this);
39             this.Validating = new ValidatingState(this);
40             this.CancellingForNextValidation = new CancellingForNextValidationState(this);
41             this.CancellingForDeactivation = new CancellingForDeactivationState(this);
42             this.ValidationDeactivated = new ValidationDeactivatedState(this);
43             this.dispatcher = dispatcher;
44             this.validationWork = validationWork;
45             this.updateWork = updateWork;
46             this.currentState = this.Idle;
47         }
48
49         internal SynchronizerState CurrentState
50         {
51             get
52             {
53                 return this.currentState;
54             }
55
56             private set
57             {
58                 this.currentState = value;
59                 this.OnCurrentStateChanged();
60             }
61         }
62
63         internal override void Validate(ValidationReason validationReason)
64         {
65             lock (this.thisLock)
66             {
67                 this.CurrentState = this.CurrentState.Validate(validationReason);
68             }
69         }
70
71         internal override void DeactivateValidation()
72         {
73             lock (this.thisLock)
74             {
75                 Fx.Assert(this.deactivationReferenceCount >= 0, "It should never happen -- deactivationReferenceCount < 0.");
76                 if (this.deactivationReferenceCount == 0)
77                 {
78                     this.CurrentState = this.CurrentState.DeactivateValidation();
79                     if (this.CurrentState != this.ValidationDeactivated)
80                     {
81                         Monitor.Wait(this.thisLock);
82                     }
83                 }
84
85                 this.deactivationReferenceCount++;
86             }
87         }
88
89         internal override void ActivateValidation()
90         {
91             lock (this.thisLock)
92             {
93                 this.deactivationReferenceCount--;
94                 Fx.Assert(this.deactivationReferenceCount >= 0, "It should never happen -- deactivationReferenceCount < 0.");
95                 if (this.deactivationReferenceCount == 0)
96                 {
97                     this.CurrentState = this.CurrentState.ActivateValidation();
98                 }
99             }
100         }
101
102         // for unit test only
103         protected virtual void OnCurrentStateChanged()
104         {
105         }
106
107         private void ValidationCompleted(TValidationResult result)
108         {
109             lock (this.thisLock)
110             {
111                 this.CurrentState = this.CurrentState.ValidationCompleted(result);
112             }
113         }
114
115         private void ValidationCancelled()
116         {
117             lock (this.thisLock)
118             {
119                 this.CurrentState = this.CurrentState.ValidationCancelled();
120             }
121         }
122
123         private void CancellableValidate(object state)
124         {
125             Fx.Assert(state is ValidationReason, "unusedState should always be a ValidationReason.");
126             ValidationReason reason = (ValidationReason)state;
127             try
128             {
129                 Fx.Assert(this.cancellationTokenSource != null, "this.cancellationTokenSource should be constructed");
130                 TValidationResult result = this.validationWork(reason, this.cancellationTokenSource.Token);
131                 this.ValidationCompleted(result);
132             }
133             catch (OperationCanceledException)
134             {
135                 this.ValidationCancelled();
136             }
137         }
138
139         private void Cancel()
140         {
141             Fx.Assert(this.cancellationTokenSource != null, "Cancel should be called only when the work is active, and by the time the cancellationTokenSource should not be null.");
142             Fx.Assert(this.cancellationTokenSource.IsCancellationRequested == false, "We should only request for cancel once.");
143             this.cancellationTokenSource.Cancel();
144         }
145
146         private void ValidationWork(ValidationReason reason)
147         {
148             this.cancellationTokenSource = new CancellationTokenSource();
149             this.dispatcher.DispatchWorkOnBackgroundThread(Fx.ThunkCallback(new WaitCallback(this.CancellableValidate)), reason);
150         }
151
152         private void UpdateUI(TValidationResult result)
153         {
154             this.dispatcher.DispatchWorkOnUIThread(DispatcherPriority.ApplicationIdle, new Action(() => { this.updateWork(result); }));
155         }
156
157         internal abstract class SynchronizerState
158         {
159             public SynchronizerState(BackgroundValidationSynchronizer<TValidationResult> parent)
160             {
161                 Fx.Assert(parent != null, "parent should not be null.");
162
163                 this.Parent = parent;
164             }
165
166             protected BackgroundValidationSynchronizer<TValidationResult> Parent { get; private set; }
167
168             public abstract SynchronizerState Validate(ValidationReason reason);
169
170             public abstract SynchronizerState ValidationCompleted(TValidationResult result);
171             
172             public abstract SynchronizerState ValidationCancelled();
173
174             public abstract SynchronizerState DeactivateValidation();
175
176             public abstract SynchronizerState ActivateValidation();
177         }
178
179         private class IdleState : SynchronizerState
180         {
181             public IdleState(BackgroundValidationSynchronizer<TValidationResult> parent)
182                 : base(parent)
183             {
184             }
185
186             public override SynchronizerState Validate(ValidationReason reason)
187             {
188                 this.Parent.ValidationWork(reason);
189                 return this.Parent.Validating;
190             }
191
192             public override SynchronizerState ValidationCompleted(TValidationResult result)
193             {
194                 Fx.Assert(false, "This should never happen - we are idle, so there is no work to complete.");
195                 return this;
196             }
197
198             public override SynchronizerState ValidationCancelled()
199             {
200                 Fx.Assert(false, "This should never happen - we are idle, so there is no work to be cancelled.");
201                 return this;
202             }
203
204             public override SynchronizerState DeactivateValidation()
205             {
206                 return this.Parent.ValidationDeactivated;
207             }
208
209             public override SynchronizerState ActivateValidation()
210             {
211                 Fx.Assert(false, "This should never happen - validation hasn't been deactivated, so there is no possibility for activate validation.");
212                 return this;
213             }
214         }
215
216         private class ValidatingState : SynchronizerState
217         {
218             public ValidatingState(BackgroundValidationSynchronizer<TValidationResult> parent)
219                 : base(parent)
220             {
221             }
222
223             public override SynchronizerState Validate(ValidationReason reason)
224             {
225                 this.Parent.Cancel();
226                 this.Parent.lastValidationReason = reason;
227                 return this.Parent.CancellingForNextValidation;
228             }
229
230             public override SynchronizerState ValidationCompleted(TValidationResult result)
231             {
232                 this.Parent.cancellationTokenSource = null;
233                 this.Parent.UpdateUI(result);
234                 return this.Parent.Idle;
235             }
236
237             public override SynchronizerState ValidationCancelled()
238             {
239                 Fx.Assert(false, "This should never happen - we haven't request for cancel yet, so there is no work to be cancelled.");
240                 return this;
241             }
242
243             public override SynchronizerState DeactivateValidation()
244             {
245                 this.Parent.Cancel();
246                 return this.Parent.CancellingForDeactivation;
247             }
248
249             public override SynchronizerState ActivateValidation()
250             {
251                 Fx.Assert(false, "This should never happen - validation hasn't been deactivated, so there is no possibility for activate validation.");
252                 return this;
253             }
254         }
255
256         private class CancellingForNextValidationState : SynchronizerState
257         {
258             public CancellingForNextValidationState(BackgroundValidationSynchronizer<TValidationResult> parent)
259                 : base(parent)
260             {
261             }
262
263             public override SynchronizerState Validate(ValidationReason reason)
264             {
265                 this.Parent.lastValidationReason = reason;
266                 return this.Parent.CancellingForNextValidation;
267             }
268
269             public override SynchronizerState ValidationCompleted(TValidationResult result)
270             {
271                 this.Parent.cancellationTokenSource = null;
272                 this.Parent.UpdateUI(result);
273                 this.Parent.ValidationWork(this.Parent.lastValidationReason);
274                 return this.Parent.Validating;
275             }
276
277             public override SynchronizerState ValidationCancelled()
278             {
279                 this.Parent.cancellationTokenSource = null;
280                 this.Parent.ValidationWork(this.Parent.lastValidationReason);
281                 return this.Parent.Validating;
282             }
283
284             public override SynchronizerState DeactivateValidation()
285             {
286                 return this.Parent.CancellingForDeactivation;
287             }
288
289             public override SynchronizerState ActivateValidation()
290             {
291                 Fx.Assert(false, "This should never happen - validation hasn't been deactivated, so there is no possibility for activate validation.");
292                 return this;
293             }
294         }
295
296         private class CancellingForDeactivationState : SynchronizerState
297         {
298             public CancellingForDeactivationState(BackgroundValidationSynchronizer<TValidationResult> parent)
299                 : base(parent)
300             {
301             }
302
303             public override SynchronizerState Validate(ValidationReason reason)
304             {
305                 // Validation need to give way to commit so that we have a responsive UI.
306                 return this.Parent.CancellingForDeactivation;
307             }
308
309             public override SynchronizerState ValidationCompleted(TValidationResult result)
310             {
311                 this.Parent.cancellationTokenSource = null;
312                 this.Parent.UpdateUI(result);
313                 Monitor.Pulse(this.Parent.thisLock);
314                 return this.Parent.ValidationDeactivated;
315             }
316
317             public override SynchronizerState ValidationCancelled()
318             {
319                 this.Parent.cancellationTokenSource = null;
320                 Monitor.Pulse(this.Parent.thisLock);
321                 return this.Parent.ValidationDeactivated;
322             }
323
324             public override SynchronizerState DeactivateValidation()
325             {
326                 return this.Parent.CancellingForDeactivation;
327             }
328
329             public override SynchronizerState ActivateValidation()
330             {
331                 Fx.Assert(false, "This should never happen - validation hasn't been deactivated, so there is no possibility for activate validation.");
332                 return this;
333             }
334         }
335
336         private class ValidationDeactivatedState : SynchronizerState
337         {
338             public ValidationDeactivatedState(BackgroundValidationSynchronizer<TValidationResult> parent)
339                 : base(parent)
340             {
341             }
342
343             public override SynchronizerState Validate(ValidationReason reason)
344             {
345                 // no-op - because commit will trigger validation anyway.
346                 return this.Parent.ValidationDeactivated;
347             }
348
349             public override SynchronizerState ValidationCompleted(TValidationResult result)
350             {
351                 Fx.Assert(false, "This should never happen - we are committing, so there is no validation work in progress, not to mention the possibility for them to be completed.");
352                 return this;
353             }
354
355             public override SynchronizerState ValidationCancelled()
356             {
357                 Fx.Assert(false, "This should never happen - we are committing, not to mention the possibility for them to be cancelled.");
358                 return this;
359             }
360
361             public override SynchronizerState DeactivateValidation()
362             {
363                 Fx.Assert(false, "This should never happen - validation has already been deactivated, so we shouldn't DeactivateValidation again.");
364                 return this;
365             }
366
367             public override SynchronizerState ActivateValidation()
368             {
369                 return this.Parent.Idle;
370             }
371         }
372     }
373 }