|
1 | 1 | // Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. |
2 | 2 |
|
3 | | -#if FX_NO_CANCELLATIONTOKEN_CLASSES |
4 | | -namespaceSystem |
5 | | -openSystem |
6 | | -openMicrosoft.FSharp.Core |
7 | | -openMicrosoft.FSharp.Core.LanguagePrimitives.IntrinsicOperators |
8 | | -openMicrosoft.FSharp.Core.Operators |
9 | | -openMicrosoft.FSharp.Control |
10 | | -openMicrosoft.FSharp.Collections |
11 | | - |
12 | | -type[<Class>]AggregateException(exns:seq<exn>)= |
13 | | -inherit Exception() |
14 | | -letexnsList=new System.Collections.Generic.List<exn>(exns) |
15 | | -memberthis.InnerExceptions=new System.Collections.ObjectModel.ReadOnlyCollection<exn>(exnsList:> System.Collections.Generic.IList<exn>) |
16 | | - |
17 | | -namespaceSystem.Threading |
18 | | - #nowarn"864"// this is for typed Equals() in CancellationTokenRegistration and CancellationToken |
19 | | - |
20 | | -openSystem |
21 | | -openMicrosoft.FSharp.Core |
22 | | -openMicrosoft.FSharp.Core.LanguagePrimitives.IntrinsicOperators |
23 | | -openMicrosoft.FSharp.Core.Operators |
24 | | -openMicrosoft.FSharp.Control |
25 | | -openMicrosoft.FSharp.Collections |
26 | | - |
27 | | - |
28 | | -moduleinternalCancellationState= |
29 | | -[<Literal>] |
30 | | -letACTIVE=0 |
31 | | -[<Literal>] |
32 | | -letDISPOSED_ACTIVE=1 |
33 | | -[<Literal>] |
34 | | -letCANCELED=2 |
35 | | -[<Literal>] |
36 | | -letDISPOSED_CANCELED=3 |
37 | | - |
38 | | -[<Struct>] |
39 | | -[<CustomEquality; NoComparison>] |
40 | | -typeCancellationTokenRegistration= |
41 | | -valprivatesource:CancellationTokenSource |
42 | | -valprivateid:int64 |
43 | | - |
44 | | -internalnew(source,id)={ source= source; id= id} |
45 | | - |
46 | | -memberthis.Dispose()= |
47 | | -match this.sourcewith |
48 | | -|null->() |
49 | | -|_-> this.source.Deregister(this.id) |
50 | | - |
51 | | -memberthis.Equals(ctr:CancellationTokenRegistration)= |
52 | | -match this.sourcewith |
53 | | -|null-> isNull ctr.source |
54 | | -|_-> this.source.Equals(ctr.source)&& this.id= ctr.id |
55 | | - |
56 | | -overridethis.Equals(o:obj)= |
57 | | -match owith |
58 | | -|:? CancellationTokenRegistrationas ctr-> this.Equals(ctr) |
59 | | -|_->false |
60 | | - |
61 | | -overridethis.GetHashCode()= |
62 | | -match this.sourcewith |
63 | | -|null->0 |
64 | | -|_-> this.source.GetHashCode()^^^this.id.GetHashCode() |
65 | | - |
66 | | -static member(=)(left:CancellationTokenRegistration,right:CancellationTokenRegistration)= left.Equals(right) |
67 | | -static member(<>)(left:CancellationTokenRegistration,right:CancellationTokenRegistration)=not(left.Equals(right)) |
68 | | - |
69 | | -interface System.IDisposablewith |
70 | | -memberthis.Dispose()= this.Dispose() |
71 | | - |
72 | | -and [<Struct>] |
73 | | -[<CustomEquality; NoComparison>] |
74 | | -CancellationToken= |
75 | | - |
76 | | -valprivatesource:CancellationTokenSource |
77 | | - |
78 | | -internalnew(source)={ source= source} |
79 | | - |
80 | | -memberthis.IsCancellationRequested= |
81 | | -match this.sourcewith |
82 | | -|null->false |
83 | | -| source-> source.IsCancellationRequested |
84 | | - |
85 | | -memberthis.CanBeCanceled= this.source<> Unchecked.defaultof<_> |
86 | | - |
87 | | -memberthis.Register(action:Action<obj>,state:obj)= |
88 | | -match this.sourcewith |
89 | | -|null-> Unchecked.defaultof<_> |
90 | | -| source-> source.Register(action, state) |
91 | | - |
92 | | -memberthis.Equals(ct:CancellationToken)= |
93 | | -match this.sourcewith |
94 | | -|null-> isNull ct.source |
95 | | -|_-> this.source.Equals(ct.source) |
96 | | - |
97 | | -overridethis.Equals(o:obj)= |
98 | | -match owith |
99 | | -|:? CancellationTokenas ct-> this.Equals(ct) |
100 | | -|_->false |
101 | | - |
102 | | -overridethis.GetHashCode()= |
103 | | -match this.sourcewith |
104 | | -|null->0 |
105 | | -|_-> this.source.GetHashCode() |
106 | | - |
107 | | -static member(=)(left:CancellationToken,right:CancellationToken)= left.Equals(right) |
108 | | -static member(<>)(left:CancellationToken,right:CancellationToken)=not(left.Equals(right)) |
109 | | - |
110 | | -static memberNone=new CancellationToken(null) |
111 | | - |
112 | | -and [<Struct>] |
113 | | -[<NoEquality; NoComparison>] |
114 | | -internal CallbackInfo= |
115 | | -valprivateid:int64 |
116 | | -valprivateaction:Action<obj> |
117 | | -valprivatestate:obj |
118 | | - |
119 | | -new(id,action,state)={ id= id; action= action; state= state} |
120 | | - |
121 | | -memberthis.ID= this.id |
122 | | -memberthis.Action= this.action |
123 | | -memberthis.State= this.state |
124 | | - |
125 | | -and [<Class>][<Sealed>][<AllowNullLiteral>] |
126 | | -CancellationTokenSource private(token1:CancellationToken,token2:CancellationToken)as this= |
127 | | - |
128 | | -[<VolatileField>] |
129 | | -let mutablestate= CancellationState.ACTIVE |
130 | | - |
131 | | -// next registration id |
132 | | -let mutablenextID=0L; |
133 | | -// lazily initialized list of registrations |
134 | | -letregistrations=lazy(new System.Collections.Generic.List<CallbackInfo>()) |
135 | | - |
136 | | -// linking to tokens |
137 | | - |
138 | | -let mutablelinkedCtr1= Unchecked.defaultof<CancellationTokenRegistration> |
139 | | -let mutablelinkedCtr2= Unchecked.defaultof<CancellationTokenRegistration> |
140 | | -do |
141 | | -lethandler= Action<obj>(fun _-> |
142 | | -// Avoiding a race for Dispose versus Cancel for linked token sources: |
143 | | -// - CTS.Dispose deregisters its CTRs and sets state to DISPOSED_* |
144 | | -// - However if the cancellation is in progress in the source it is linked to, deregistration is a no-op and CTS may still receive cancellation notification |
145 | | -// - That cancellation notification arrives in disposed state |
146 | | -// We ignore cancellation notifications from linked sources in disposed state (so if cancellation/disposal race happens, disposal wins). |
147 | | - this.Cancel(dontThrowIfDisposed=true) |
148 | | -) |
149 | | - linkedCtr1<- token1.Register(handler,null) |
150 | | - linkedCtr2<- token2.Register(handler,null) |
151 | | - |
152 | | -publicnew()=new CancellationTokenSource(Unchecked.defaultof<_>,Unchecked.defaultof<_>) |
153 | | - |
154 | | -memberthis.Token=new CancellationToken(this) |
155 | | - |
156 | | -memberthis.Cancel()= this.Cancel(dontThrowIfDisposed=false) |
157 | | -memberprivatethis.Cancel(dontThrowIfDisposed):unit= |
158 | | -letoldState= Interlocked.CompareExchange(&state, CancellationState.CANCELED, CancellationState.ACTIVE) |
159 | | -match oldStatewith |
160 | | -| CancellationState.ACTIVE-> |
161 | | -if registrations.IsValueCreatedthen// we have at least one registration |
162 | | -letlist= registrations.Value |
163 | | -lettoRun= |
164 | | -// building a list of callback to run, in LIFO order |
165 | | - lock list(fun()-> |
166 | | -lettoRun= list|> Seq.fold(fun l info->(fun()-> info.Action.Invoke(info.State))::l)[] |
167 | | - list.Clear() |
168 | | - toRun) |
169 | | -letdoRun l f=// run callback, add any thrown exception to the list |
170 | | -try f(); l |
171 | | -with e-> e::l |
172 | | -letexns= List.fold doRun[] toRun |
173 | | -match exnswith |
174 | | -|[]->() |
175 | | -|_-> |
176 | | -// exns are in reverse order to the callbacks in toRun |
177 | | -// we rev here; mainline case (no exceptions at all) runs without any allocations for exception list |
178 | | -new AggregateException(exns|> List.rev)|> raise |
179 | | -else()// no registrations - do nothing |
180 | | -| CancellationState.CANCELED-> |
181 | | -()// cancellation already happened |
182 | | -|_-> |
183 | | -// DISPOSED_ACTIVE or DISPOSED_CANCELED |
184 | | -ifnot dontThrowIfDisposedthen |
185 | | -new ObjectDisposedException(typeof<CancellationTokenSource>.FullName)|> raise |
186 | | -else() |
187 | | - |
188 | | -memberthis.Dispose()= |
189 | | -try |
190 | | -// Unregister from linked sources before changing state. Otherwise callback may still execute and we will be canceled in disposed state |
191 | | -// Multiple CTR disposal is a no-op |
192 | | -try |
193 | | - linkedCtr2.Dispose() |
194 | | -finally |
195 | | - linkedCtr1.Dispose() |
196 | | -finally |
197 | | -letdisposeNow= |
198 | | -letoldState= Interlocked.CompareExchange(&state, CancellationState.DISPOSED_ACTIVE, CancellationState.ACTIVE) |
199 | | -if oldState= CancellationState.ACTIVEthen |
200 | | -true// previous state was ACTIVE, now disposing |
201 | | -else |
202 | | -letoldState= Interlocked.CompareExchange(&state, CancellationState.DISPOSED_CANCELED, CancellationState.CANCELED) |
203 | | -// if previous state was CANCELED, dispose now. Otherwise previous state was one of DISPOSED_* states, so already disposed |
204 | | - oldState= CancellationState.CANCELED |
205 | | -if disposeNowthen |
206 | | -if registrations.IsValueCreatedthen |
207 | | -letlist= registrations.Value |
208 | | - lock list(fun()-> list.Clear()) |
209 | | - |
210 | | -memberprivatethis.InternalIsCanceled throwOnDisposed= |
211 | | -match statewith |
212 | | -| CancellationState.ACTIVE->false |
213 | | -| CancellationState.CANCELED->true |
214 | | -| CancellationState.DISPOSED_CANCELED-> |
215 | | -if throwOnDisposedthen |
216 | | -new ObjectDisposedException(typeof<CancellationTokenSource>.FullName)|> raise |
217 | | -else |
218 | | -true |
219 | | -|_-> |
220 | | -if throwOnDisposedthen |
221 | | -new ObjectDisposedException(typeof<CancellationTokenSource>.FullName)|> raise |
222 | | -else |
223 | | -false |
224 | | - |
225 | | - |
226 | | -memberinternalthis.IsCancellationRequested= state= CancellationState.CANCELED|| state= CancellationState.DISPOSED_CANCELED |
227 | | - |
228 | | -memberinternalthis.Register(action:Action<obj>,state:obj)= |
229 | | -if this.InternalIsCanceledtruethen// do not register, invoke immediately |
230 | | - action.Invoke(state) |
231 | | - Unchecked.defaultof<_> |
232 | | -else |
233 | | -letlist= registrations.Value |
234 | | -letinvokeNow,r= |
235 | | - lock list(fun()-> |
236 | | -if this.InternalIsCanceledtruethen |
237 | | -true,new CancellationTokenRegistration(Unchecked.defaultof<_>,0L) |
238 | | -else |
239 | | -letid= nextID |
240 | | - nextID<- nextID+1L |
241 | | - list.Add(new CallbackInfo(id, action, state)) |
242 | | -false,new CancellationTokenRegistration(this, id) |
243 | | -) |
244 | | -if invokeNowthen action.Invoke(state) |
245 | | - r |
246 | | - |
247 | | -memberinternalthis.Deregister(id)= |
248 | | -if this.InternalIsCanceledfalsethen// ok to deregister after Dispose |
249 | | -()// After cancellation is requested no deregistration needed; |
250 | | -else |
251 | | -letlist= registrations.Value |
252 | | - lock list(fun()-> |
253 | | -if this.InternalIsCanceledfalsethen// ok to deregister after Dispose |
254 | | -() |
255 | | -else |
256 | | -letindex= |
257 | | -// Search backwards; we assume Register/Deregister are scoped |
258 | | -// so registered last will be deregistered first |
259 | | -let recloop i= |
260 | | -if i<0then(-1) |
261 | | -else |
262 | | -letcallbackInfo= list.[i] |
263 | | -if callbackInfo.ID= idthen i |
264 | | -else loop(i-1) |
265 | | - loop(list.Count-1) |
266 | | -if index>=0then |
267 | | - list.RemoveAt(index) |
268 | | -else |
269 | | -()// we do not punish double deregistering |
270 | | -) |
271 | | - |
272 | | - |
273 | | -interface System.IDisposablewith |
274 | | -memberthis.Dispose()= this.Dispose() |
275 | | -static memberCreateLinkedTokenSource(token1:CancellationToken,token2:CancellationToken)= |
276 | | -new CancellationTokenSource(token1,token2) |
277 | | -#endif |
278 | | - |
279 | 3 | namespaceMicrosoft.FSharp.Control |
280 | 4 |
|
281 | 5 | #nowarn"40" |
|