1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //----------------------------------------------------------------
4 namespace System.ServiceModel.Discovery
5 {
6     using System;
7     using System.Collections.Generic;
8     using System.Runtime;
9     using System.Threading;
10     using System.Xml;
11     using SR2 = System.ServiceModel.Discovery.SR;
12 
13     class AsyncOperationLifetimeManager
14     {
15         [Fx.Tag.SynchronizationObject()]
16         object thisLock;
17 
18         bool isAborted;
19         AsyncWaitHandle closeHandle;
20         Dictionary<UniqueId, AsyncOperationContext> activeOperations;
21 
AsyncOperationLifetimeManager()22         public AsyncOperationLifetimeManager()
23         {
24             this.thisLock = new object();
25             this.activeOperations = new Dictionary<UniqueId, AsyncOperationContext>();
26         }
27 
28         public bool IsAborted
29         {
30             get
31             {
32                 return this.isAborted;
33             }
34         }
35 
36         public bool IsClosed
37         {
38             get
39             {
40                 return this.closeHandle != null;
41             }
42         }
43 
TryAdd(AsyncOperationContext context)44         public bool TryAdd(AsyncOperationContext context)
45         {
46             Fx.Assert(context != null, "The context must be non null.");
47 
48             lock (this.thisLock)
49             {
50                 if (this.IsAborted || this.IsClosed)
51                 {
52                     return false;
53                 }
54                 if (this.activeOperations.ContainsKey(context.OperationId))
55                 {
56                     return false;
57                 }
58                 this.activeOperations.Add(context.OperationId, context);
59             }
60 
61             return true;
62         }
63 
Abort()64         public AsyncOperationContext[] Abort()
65         {
66             AsyncOperationContext[] retValue = null;
67             bool setCloseHandle = false;
68 
69             lock (this.thisLock)
70             {
71                 if (this.IsAborted)
72                 {
73                     return new AsyncOperationContext[] { };
74                 }
75                 else
76                 {
77                     this.isAborted = true;
78                 }
79 
80                 retValue = new AsyncOperationContext[this.activeOperations.Count];
81                 this.activeOperations.Values.CopyTo(retValue, 0);
82                 this.activeOperations.Clear();
83                 setCloseHandle = this.closeHandle != null;
84             }
85 
86             if (setCloseHandle)
87             {
88                 this.closeHandle.Set();
89             }
90 
91             return retValue;
92         }
93 
TryLookup(UniqueId operationId, out AsyncOperationContext context)94         public bool TryLookup(UniqueId operationId, out AsyncOperationContext context)
95         {
96             bool success;
97 
98             lock (this.thisLock)
99             {
100                 success = this.activeOperations.TryGetValue(operationId, out context);
101             }
102 
103             return success;
104         }
105 
106         public bool TryLookup<T>(UniqueId operationId, out T context) where T : AsyncOperationContext
107         {
108             AsyncOperationContext asyncContext = null;
109             if (TryLookup(operationId, out asyncContext))
110             {
111                 context = asyncContext as T;
112                 if (context != null)
113                 {
114                     return true;
115                 }
116             }
117 
118             context = null;
119             return false;
120         }
121 
122         public T Remove<T>(UniqueId operationId) where T : AsyncOperationContext
123         {
124             AsyncOperationContext context = null;
125             bool setCloseHandle = false;
126 
127             lock (this.thisLock)
128             {
129                 if ((this.activeOperations.TryGetValue(operationId, out context)) &&
130                     (context is T))
131                 {
132                     this.activeOperations.Remove(operationId);
133                     setCloseHandle = (this.closeHandle != null) && (this.activeOperations.Count == 0);
134                 }
135                 else
136                 {
137                     context = null;
138                 }
139             }
140 
141             if (setCloseHandle)
142             {
143                 this.closeHandle.Set();
144             }
145 
146             return context as T;
147         }
148 
TryRemoveUnique(object userState, out AsyncOperationContext context)149         public bool TryRemoveUnique(object userState, out AsyncOperationContext context)
150         {
151             bool success = false;
152             bool setCloseHandle = false;
153             context = null;
154 
155             lock (this.thisLock)
156             {
157                 foreach (AsyncOperationContext value in this.activeOperations.Values)
158                 {
159                     if (object.Equals(value.UserState, userState))
160                     {
161                         if (success)
162                         {
163                             success = false;
164                             break;
165                         }
166                         else
167                         {
168                             context = value;
169                             success = true;
170                         }
171                     }
172                 }
173 
174                 if (success)
175                 {
176                     this.activeOperations.Remove(context.OperationId);
177                     setCloseHandle = (this.closeHandle != null) && (this.activeOperations.Count == 0);
178                 }
179             }
180 
181             if (setCloseHandle)
182             {
183                 this.closeHandle.Set();
184             }
185 
186             return success;
187         }
188 
Close(TimeSpan timeout)189         public void Close(TimeSpan timeout)
190         {
191             InitializeCloseHandle();
192             if (!this.closeHandle.Wait(timeout))
193             {
194                 throw FxTrace.Exception.AsError(new TimeoutException(SR2.TimeoutOnOperation(timeout)));
195             }
196         }
197 
BeginClose(TimeSpan timeout, AsyncCallback callback, object state)198         public IAsyncResult BeginClose(TimeSpan timeout, AsyncCallback callback, object state)
199         {
200             InitializeCloseHandle();
201             return new CloseAsyncResult(this.closeHandle, timeout, callback, state);
202         }
203 
EndClose(IAsyncResult result)204         public void EndClose(IAsyncResult result)
205         {
206             CloseAsyncResult.End(result);
207         }
208 
InitializeCloseHandle()209         void InitializeCloseHandle()
210         {
211             bool setCloseHandle = false;
212             lock (this.thisLock)
213             {
214                 this.closeHandle = new AsyncWaitHandle(EventResetMode.ManualReset);
215                 setCloseHandle = (this.activeOperations.Count == 0);
216 
217                 if (this.IsAborted)
218                 {
219                     setCloseHandle = true;
220                 }
221             }
222             if (setCloseHandle)
223             {
224                 this.closeHandle.Set();
225             }
226         }
227 
228         class CloseAsyncResult : AsyncResult
229         {
230             static Action<object, TimeoutException> onWaitCompleted = new Action<object, TimeoutException>(OnWaitCompleted);
231             AsyncWaitHandle asyncWaitHandle;
232 
CloseAsyncResult(AsyncWaitHandle asyncWaitHandle, TimeSpan timeout, AsyncCallback callback, object state)233             internal CloseAsyncResult(AsyncWaitHandle asyncWaitHandle, TimeSpan timeout, AsyncCallback callback, object state)
234                 : base(callback, state)
235             {
236                 this.asyncWaitHandle = asyncWaitHandle;
237                 if (this.asyncWaitHandle.WaitAsync(onWaitCompleted, this, timeout))
238                 {
239                     Complete(true);
240                 }
241             }
242 
OnWaitCompleted(object state, TimeoutException asyncException)243             static void OnWaitCompleted(object state, TimeoutException asyncException)
244             {
245                 CloseAsyncResult thisPtr = (CloseAsyncResult)state;
246                 thisPtr.Complete(false, asyncException);
247             }
248 
End(IAsyncResult result)249             internal static void End(IAsyncResult result)
250             {
251                 AsyncResult.End<CloseAsyncResult>(result);
252             }
253         }
254     }
255 }
256