RiSE4fun Code for SealAll the programs analyzed on RiSE4fun for Sealen-USrise4fun &copy; 2013 Microsoft CorporationThu, 23 May 2013 06:05:29 -0700http://rise4fun.com//Images/Rise.gifRiSE4fun Code for SealtGhttp://rise4fun.com/Seal/tGSeal/tG<pre>using System&#59;&#10;class Test &#123;&#10; Test f&#59;&#10; String g&#59;&#10;public static Test param&#59;&#10; public void ImpureMethod&#40;&#41;&#123; &#10; var x &#61; param.f&#59;&#10; x.g &#61; &#34;Hello&#34;&#59;&#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00Zhttp://rise4fun.com/Seal/ZSeal/Z<pre>using System&#59;&#10;using System.Collections.Generic&#59;&#10;class Test &#123;&#10; public Test f &#61; null&#59;&#10; public void PureMethod&#40;Test param&#41;&#123;&#10; List&#60;Test&#62; l &#61; new List&#60;Test&#62;&#40;&#41;&#59;&#10; l.Add&#40;param&#41;&#59;&#10;&#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00Thttp://rise4fun.com/Seal/TSeal/T<pre>using System&#59;&#10;using System.Collections.Generic&#59;&#10;class Test &#123;&#10; public Test f &#61; null&#59;&#10; public void PureMethod&#40;Test param&#41;&#123;&#10; List&#60;Test&#62; l &#61; new List&#60;Test&#62;&#40;&#41;&#59;&#10; l.Add&#40;param&#41;&#59;&#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00szhttp://rise4fun.com/Seal/szSeal/sz<pre>Could not find sample or program http584747rise4fun.com47images47logo_msr_small.png</pre>2013-05-23T06:05:29-07:00mohttp://rise4fun.com/Seal/moSeal/mo<pre>WZYIDq &#60;a href&#61;&#34;http&#58;&#47;&#47;uhzzapgzpklr.com&#47;&#34;&#62;uhzzapgzpklr&#60;&#47;a&#62;, &#91;url&#61;http&#58;&#47;&#47;zhdhhwsyzdqj.com&#47;&#93;zhdhhwsyzdqj&#91;&#47;url&#93;, &#91;link&#61;http&#58;&#47;&#47;rrqmajzmxwaf.com&#47;&#93;rrqmajzmxwaf&#91;&#47;link&#93;, http&#58;&#47;&#47;zhfxwvwbbjwv.com&#47;</pre>2013-05-23T06:05:29-07:00mnhttp://rise4fun.com/Seal/mnSeal/mn<pre>using System&#59;&#10;class TestInstanceDelegates&#123;&#10; TestInstanceDelegates f &#61; null&#59; &#10;&#10; void foo&#40;System.Func&#60;TestInstanceDelegates, int&#62; del&#41;&#123;&#10; del&#40;this&#41;&#59;&#10; &#125;&#10;&#10; public void start&#40;TestInstanceDelegates testDel1, TestInstanceDelegates testDel2&#41;&#123; &#10; testDel1.foo&#40;&#40;TestInstanceDelegates param&#41; &#61;&#62;&#10; &#123;&#10; testDel2.f &#61; new TestInstanceDelegates&#40;&#41;&#59;&#10; param.f &#61; testDel2.f&#59;&#10; return 0&#59;&#10; &#125;&#41;&#59;&#10; &#125;&#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00mGhttp://rise4fun.com/Seal/mGSeal/mG<pre>using System&#59;&#10;using System.Collections.Generic&#59;&#10;using System.Runtime.CompilerServices&#59;&#10;using System.Threading&#59;&#10;using System.Threading.Tasks&#59;&#10;&#10;namespace Roslyn.Utilities&#10;&#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Represents a value that can be retrieved synchronously or asynchronously by many clients.&#10; &#47;&#47;&#47; The value will be computed on-demand the moment the first client asks for it. While being&#10; &#47;&#47;&#47; computed, more clients can request the value. As long as there are outstanding clients the&#10; &#47;&#47;&#47; underlying computation will proceed. If all outstanding clients cancel their request then&#10; &#47;&#47;&#47; the underlying value computation will be cancelled as well.&#10; &#47;&#47;&#47; &#10; &#47;&#47;&#47; Creators of an &#60;see cref&#61;&#34;AsyncLazy&#123;T&#125;&#34; &#47;&#62; can specify whether the result of the computation is&#10; &#47;&#47;&#47; cached for future requests or not. Choosing to not cache means the computation functions are kept&#10; &#47;&#47;&#47; alive, whereas caching means the value &#40;but not functions&#41; are kept alive once complete.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; internal sealed class AsyncLazy&#60;T&#62;&#10; &#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The underlying function that starts an asynchronous computation of the resulting value.&#10; &#47;&#47;&#47; Null&#39;ed out once we&#39;ve computed the result and we&#39;ve been asked to cache it. Otherwise,&#10; &#47;&#47;&#47; it is kept around in case the value needs to be computed again.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The underlying function that starts an synchronous computation of the resulting value.&#10; &#47;&#47;&#47; Null&#39;ed out once we&#39;ve computed the result and we&#39;ve been asked to cache it, or if we&#10; &#47;&#47;&#47; didn&#39;t get any synchronous function given to us in the first place.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Func&#60;CancellationToken, T&#62; synchronousComputeFunction&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Whether or not we should keep the value around once we&#39;ve computed it.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private readonly bool cacheResult&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The Task that holds the cached result.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Task&#60;T&#62; cachedResult&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Mutex used to protect reading and writing to all mutable objects and fields. Traces&#10; &#47;&#47;&#47; indicate that there&#39;s negligible contention on this lock, hence we can save some memory&#10; &#47;&#47;&#47; by using a single lock for all AsyncLazy instances. Only trivial and non-reentrant work&#10; &#47;&#47;&#47; should be done while holding the lock.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private static readonly NonReentrantLock gate &#61; new NonReentrantLock&#40;useThisInstanceForSynchronization&#58; true&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The hash set of all currently outstanding asynchronous requests. Null if there are no requests,&#10; &#47;&#47;&#47; and will never be empty.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private HashSet&#60;Request&#62; requests &#61; null&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; If an asynchronous request is active, the CancellationTokenSource that allows for&#10; &#47;&#47;&#47; cancelling the underlying computation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private CancellationTokenSource asynchronousComputationCancellationSource &#61; null&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Whether a computation is active or queued on any thread, whether synchronous or&#10; &#47;&#47;&#47; asynchronous.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool computationActive &#61; false&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Creates an AsyncLazy that always returns the value, analogous to &#60;see cref&#61;&#34;Task.FromResult&#123;T&#125;&#34; &#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public AsyncLazy&#40;T value&#41;&#10; &#123;&#10; this.cacheResult &#61; true&#59;&#10; this.cachedResult &#61; new Task&#60;T&#62;&#40;&#40;&#41; &#61;&#62; &#123;return value&#59;&#125;&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Important&#58; callers of this constructor should ensure that the compute function returns&#10; &#47;&#47;&#47; a task in a non-blocking fashion. i.e. the function should &#42;not&#42; synchronously compute&#10; &#47;&#47;&#47; a value and then return it using Task.FromResult. Instead, it should return an actual&#10; &#47;&#47;&#47; task that operates asynchronously. If this function synchronously computes a value&#10; &#47;&#47;&#47; then that will cause locks to be held in this type for excessive periods of time.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public AsyncLazy&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, bool cacheResult&#41;&#10; &#58; this&#40;asynchronousComputeFunction, synchronousComputeFunction&#58; null, cacheResult&#58; cacheResult&#41;&#10; &#123;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Creates an AsyncLazy that supports both asynchronous computation and inline synchronous&#10; &#47;&#47;&#47; computation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;asynchronousComputeFunction&#34;&#62;A function called to start the asynchronous&#10; &#47;&#47;&#47; computation. This function should be cheap and non-blocking.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;synchronousComputeFunction&#34;&#62;A function to do the work synchronously, which&#10; &#47;&#47;&#47; is allowed to block. This function should not be implemented by a simple Wait on the&#10; &#47;&#47;&#47; asynchronous value. If that&#39;s all you are doing, just don&#39;t pass a synchronous function&#10; &#47;&#47;&#47; in the first place.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;cacheResult&#34;&#62;Whether the result should be cached once the computation is&#10; &#47;&#47;&#47; complete.&#60;&#47;param&#62;&#10; public AsyncLazy&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, Func&#60;CancellationToken, T&#62; synchronousComputeFunction, bool cacheResult&#41;&#10; &#123;&#10; this.asynchronousComputeFunction &#61; asynchronousComputeFunction&#59;&#10; this.synchronousComputeFunction &#61; synchronousComputeFunction&#59;&#10; this.cacheResult &#61; cacheResult&#59;&#10; &#125;&#10;&#10; &#35;region Lock Wrapper for Invariant Checking&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Takes the lock for this object and if acquired validates the invariants of this class.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private WaitThatValidatesInvariants TakeLock&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; gate.Wait&#40;cancellationToken&#41;&#59;&#10; AssertInvariants_NoLock&#40;&#41;&#59;&#10; return new WaitThatValidatesInvariants&#40;this&#41;&#59;&#10; &#125;&#10;&#10; private struct WaitThatValidatesInvariants &#58; IDisposable&#10; &#123;&#10; private readonly AsyncLazy&#60;T&#62; asyncLazy&#59;&#10;&#10; public WaitThatValidatesInvariants&#40;AsyncLazy&#60;T&#62; asyncLazy&#41;&#10; &#123;&#10; this.asyncLazy &#61; asyncLazy&#59;&#10; &#125;&#10;&#10; public void Dispose&#40;&#41;&#10; &#123;&#10; asyncLazy.AssertInvariants_NoLock&#40;&#41;&#59;&#10; gate.Release&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void AssertInvariants_NoLock&#40;&#41;&#10; &#123;&#10;&#10; &#125;&#10;&#10; &#35;endregion&#10;&#10; public bool TryGetValue&#40;out T result&#41;&#10; &#123;&#10; &#47;&#47; No need to lock here since this is only a fast check to &#10; &#47;&#47; see if the result is already computed.&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; result &#61; cachedResult.Result&#59;&#10; return true&#59;&#10; &#125;&#10;&#10; result &#61; default&#40;T&#41;&#59;&#10; return false&#59;&#10; &#125;&#10;&#10; public T GetValue&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; Request request &#61; null&#59;&#10; AsynchronousComputationToStart&#63; newAsynchronousComputation &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If cached, get immediately&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return cachedResult.Result&#59;&#10; &#125;&#10;&#10; &#47;&#47; If there is an existing computation active, we&#39;ll just create another request&#10; if &#40;computationActive&#41;&#10; &#123;&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10; &#125;&#10; else if &#40;synchronousComputeFunction &#61;&#61; null&#41;&#10; &#123;&#10; &#47;&#47; A synchronous request, but we have no synchronous function. Start off the async work&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10;&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; else&#10; &#123;&#10; &#47;&#47; We will do the computation here&#10; this.computationActive &#61; true&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; If we simply created a new asynchronous request, so wait for it. Yes, we&#39;re blocking the thread&#10; &#47;&#47; but we don&#39;t want multiple threads attempting to compute the same thing.&#10; if &#40;request &#33;&#61; null&#41;&#10; &#123;&#10; request.RegisterForCancellation&#40;OnAsynchronousRequestCancelled, cancellationToken&#41;&#59;&#10;&#10; &#47;&#47; Since we already registered for cancellation, it&#39;s possible that the registration has&#10; &#47;&#47; cancelled this new computation if we were the only requestor.&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; request&#41;&#59;&#10; &#125;&#10;&#10; return WaitAndGetResult&#40;request.Task, cancellationToken&#41;&#59;&#10; &#125;&#10; else&#10; &#123;&#10; T result&#59;&#10;&#10; &#47;&#47; We are the active computation, so let&#39;s go ahead and compute.&#10; try&#10; &#123;&#10; result &#61; synchronousComputeFunction&#40;cancellationToken&#41;&#59;&#10; &#125;&#10; catch &#40;OperationCanceledException&#41;&#10; &#123;&#10; &#47;&#47; This cancelled for some reason. We don&#39;t care why, but&#10; &#47;&#47; it means anybody else waiting for this result isn&#39;t going to get it&#10; &#47;&#47; from us.&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; this.computationActive &#61; false&#59;&#10;&#10; if &#40;requests &#33;&#61; null&#41;&#10; &#123;&#10; &#47;&#47; There&#39;s a possible improvement here&#58; there might be another synchronous caller who&#10; &#47;&#47; also wants the value. We might consider stealing their thread rather than punting&#10; &#47;&#47; to the thread pool.&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; null&#41;&#59;&#10; &#125;&#10;&#10; throw&#59;&#10; &#125;&#10; catch &#40;Exception ex&#41;&#10; &#123;&#10; &#47;&#47; We faulted for some unknown reason. We should simply fault everything.&#10; TaskCompletionSource&#60;T&#62; tcs &#61; new TaskCompletionSource&#60;T&#62;&#40;&#41;&#59;&#10; tcs.SetException&#40;ex&#41;&#59;&#10; CompleteWithTask&#40;tcs.Task, CancellationToken.None&#41;&#59;&#10;&#10; throw&#59;&#10; &#125;&#10;&#10; &#47;&#47; We have a value, so complete&#10; CompleteWithTask&#40;new Task&#60;T&#62;&#40;&#40;&#41;&#61;&#62; result&#41;, CancellationToken.None&#41;&#59;&#10;&#10; return result&#59;&#10; &#125;&#10; &#125;&#10; &#10; private T WaitAndGetResult&#40;Task&#60;T&#62; task, CancellationToken cancellationToken&#41;&#10; &#123;&#10; task.Wait&#40;cancellationToken&#41;&#59;&#10; return task.Result&#59;&#10; &#125;&#10;&#10; private Request CreateNewRequest_NoLock&#40;&#41;&#10; &#123;&#10; if &#40;this.requests &#61;&#61; null&#41;&#10; &#123;&#10; this.requests &#61; new HashSet&#60;Request&#62;&#40;&#41;&#59;&#10; &#125;&#10;&#10; Request request &#61; new Request&#40;&#41;&#59;&#10; this.requests.Add&#40;request&#41;&#59;&#10; return request&#59;&#10; &#125;&#10;&#10; public Task&#60;T&#62; GetValueAsync&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; &#47;&#47; Optimization&#58; if we&#39;re already cancelled, do not pass go&#10; if &#40;cancellationToken.IsCancellationRequested&#41;&#10; &#123;&#10; return new Task&#60;T&#62;&#40;&#40;&#41; &#61;&#62; default&#40;T&#41;, cancellationToken&#41;&#59;&#10; &#125;&#10;&#10; Request request&#59;&#10; AsynchronousComputationToStart&#63; newAsynchronousComputation &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If cached, get immediately&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return cachedResult&#59;&#10; &#125;&#10;&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10;&#10; &#47;&#47; If we have either synchronous or asynchronous work current in flight, we don&#39;t need to do anything.&#10; &#47;&#47; Otherwise, we shall start an asynchronous computation for this&#10; if &#40;&#33;computationActive&#41;&#10; &#123;&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; We now have the request counted for, register for cancellation. It is critical this is&#10; &#47;&#47; done outside the lock, as our reigstration may immediately fire and we want to avoid the&#10; &#47;&#47; reentrancy&#10; request.RegisterForCancellation&#40;OnAsynchronousRequestCancelled, cancellationToken&#41;&#59;&#10;&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; request&#41;&#59;&#10; &#125;&#10;&#10; return request.Task&#59;&#10; &#125;&#10;&#10; private AsynchronousComputationToStart RegisterAsynchronousComputation_NoLock&#40;&#41;&#10; &#123;&#10; this.asynchronousComputationCancellationSource &#61; new CancellationTokenSource&#40;&#41;&#59;&#10; this.computationActive &#61; true&#59;&#10;&#10; return new AsynchronousComputationToStart&#40;this.asynchronousComputeFunction, this.asynchronousComputationCancellationSource&#41;&#59;&#10; &#125;&#10;&#10; private struct AsynchronousComputationToStart&#10; &#123;&#10; public readonly Func&#60;CancellationToken, Task&#60;T&#62;&#62; AsynchronousComputeFunction&#59;&#10; public readonly CancellationTokenSource CancellationTokenSource&#59;&#10;&#10; public AsynchronousComputationToStart&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, CancellationTokenSource cancellationTokenSource&#41;&#10; &#123;&#10; this.AsynchronousComputeFunction &#61; asynchronousComputeFunction&#59;&#10; this.CancellationTokenSource &#61; cancellationTokenSource&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void StartAsynchronousComputation&#40;AsynchronousComputationToStart computationToStart, Request requestToCompleteSynchronously&#41;&#10; &#123;&#10; var cancellationToken &#61; computationToStart.CancellationTokenSource.Token&#59;&#10;&#10; try&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; DO NOT ACCESS ANY FIELDS OR STATE BEYOND THIS POINT. Since this function&#10; &#47;&#47; runs unsynchronized, it&#39;s possible that during this function this request&#10; &#47;&#47; might be cancelled, and then a whole additional request might start and&#10; &#47;&#47; complete inline, and cache the result. By grabbing state before we check&#10; &#47;&#47; the cancellation token, we can be assured that we are only operating on&#10; &#47;&#47; a state that was complete.&#10;&#10;&#10; &#47;&#47; We avoid creating a full closure just to pass the token along&#10; &#47;&#47; Also, use TaskContinuationOptions.ExecuteSynchronously so that we inline &#10; &#47;&#47; the continuation if asynchronousComputeFunction completes synchronously&#10; var task &#61; computationToStart.AsynchronousComputeFunction&#40;cancellationToken&#41;&#59;&#10;&#10; task.ContinueWith&#40;&#10; &#40;t, s&#41; &#61;&#62; CompleteWithTask&#40;t, &#40;&#40;CancellationTokenSource&#41;s&#41;.Token&#41;,&#10; computationToStart.CancellationTokenSource,&#10; cancellationToken,&#10; TaskContinuationOptions.ExecuteSynchronously,&#10; TaskScheduler.Default&#41;&#59;&#10;&#10; if &#40;requestToCompleteSynchronously &#33;&#61; null &#38;&#38; task.IsCompleted&#41;&#10; &#123;&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; task &#61; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;task&#41;&#59;&#10; &#125;&#10;&#10; requestToCompleteSynchronously.CompleteFromTaskSynchronously&#40;task&#41;&#59;&#10; &#125;&#10;&#10; &#125;&#10; catch &#40;OperationCanceledException oce&#41;&#10; &#123;&#10; &#47;&#47; As long as it&#39;s the right token, this means that our thread was the first thread&#10; &#47;&#47; to start an asynchronous computation, but the requestor cancelled as we were starting up&#10; &#47;&#47; the computation.&#10; if &#40;oce.CancellationToken &#33;&#61; cancellationToken&#41;&#10; &#123;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; private void CompleteWithTask&#40;Task&#60;T&#62; task, CancellationToken cancellationToken&#41;&#10; &#123;&#10; IEnumerable&#60;Request&#62; requestsToComplete&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If the underlying computation was cancelled, then all state was already updated in OnAsynchronousRequestCancelled&#10; &#47;&#47; and there is no new work to do here. We &#42;must&#42; use the local one since this completion may be running far after&#10; &#47;&#47; the background computation was cancelled and a new one might have already been enqueued. We must do this&#10; &#47;&#47; check here under the lock to ensure proper synchronization with OnAsynchronousRequestCancelled.&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; The computation is complete, so get all requests to complete and null out the list. We&#39;ll create another one&#10; &#47;&#47; later if it&#39;s needed&#10; requestsToComplete &#61; this.requests &#63;&#63; new List&#60;Request&#62;&#40;&#41; as IEnumerable&#60;Request&#62;&#59;&#10; this.requests &#61; null&#59;&#10;&#10; &#47;&#47; The computations are done&#10; this.asynchronousComputationCancellationSource &#61; null&#59;&#10; this.computationActive &#61; false&#59;&#10; task &#61; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;task&#41;&#59;&#10; &#125;&#10;&#10; foreach &#40;var requestToComplete in requestsToComplete&#41;&#10; &#123;&#10; requestToComplete.CompleteFromTaskAsynchronously&#40;task&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; private Task&#60;T&#62; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; if &#40;this.cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return this.cachedResult&#59;&#10; &#125;&#10; else&#10; &#123;&#10; if &#40;cacheResult &#38;&#38; task.Status &#61;&#61; TaskStatus.RanToCompletion&#41;&#10; &#123;&#10; &#47;&#47; Hold onto the completed task. We can get rid of the computation functions for good&#10; this.cachedResult &#61; task&#59;&#10; this.asynchronousComputeFunction &#61; null&#59;&#10; this.synchronousComputeFunction &#61; null&#59;&#10; &#125;&#10;&#10; return task&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void OnAsynchronousRequestCancelled&#40;object state&#41;&#10; &#123;&#10; var request &#61; &#40;Request&#41;state&#59;&#10; CancellationTokenSource cancellationTokenSource &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; &#47;&#47; Now try to remove it. It&#39;s possible that requests may already be null. You could&#10; &#47;&#47; imagine that cancellation was requested, but before we could aquire the lock&#10; &#47;&#47; here the computation completed and the entire CompleteWithTask synchronized&#10; &#47;&#47; block ran. In that case, the requests collection may already be null, or it&#10; &#47;&#47; &#40;even scarier&#33;&#41; may have been replaced with another collection because another&#10; &#47;&#47; computation has started.&#10; if &#40;this.requests &#33;&#61; null&#41;&#10; &#123;&#10; if &#40;this.requests.Remove&#40;request&#41;&#41;&#10; &#123;&#10; if &#40;this.requests.Count &#61;&#61; 0&#41;&#10; &#123;&#10; this.requests &#61; null&#59;&#10;&#10; if &#40;this.asynchronousComputationCancellationSource &#33;&#61; null&#41;&#10; &#123;&#10; cancellationTokenSource &#61; this.asynchronousComputationCancellationSource&#59;&#10; this.asynchronousComputationCancellationSource &#61; null&#59;&#10; this.computationActive &#61; false&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; request.CancelAsynchronously&#40;&#41;&#59;&#10;&#10; if &#40;cancellationTokenSource &#33;&#61; null&#41;&#10; &#123;&#10; cancellationTokenSource.Cancel&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; Using inheritance instead of wrapping a TaskCompletionSource to avoid a second allocation&#10; private class Request &#58; TaskCompletionSource&#60;T&#62;&#10; &#123;&#10; private CancellationTokenRegistration cancellationTokenRegistration&#59;&#10;&#10; public void RegisterForCancellation&#40;Action&#60;object&#62; callback, CancellationToken cancellationToken&#41;&#10; &#123;&#10; cancellationTokenRegistration &#61; cancellationToken.Register&#40;callback, this&#41;&#59;&#10; &#125;&#10;&#10; public void CompleteFromTaskAsynchronously&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; System.Threading.Tasks.Task.Factory.StartNew&#40;CompleteFromTaskSynchronouslyStub, task, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default&#41;&#59;&#10; &#125;&#10;&#10; private void CompleteFromTaskSynchronouslyStub&#40;object task&#41;&#10; &#123;&#10; CompleteFromTaskSynchronously&#40;&#40;Task&#60;T&#62;&#41;task&#41;&#59;&#10; &#125;&#10;&#10; public void CompleteFromTaskSynchronously&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; if &#40;task.Status &#61;&#61; TaskStatus.RanToCompletion&#41;&#10; &#123;&#10; if &#40;TrySetResult&#40;task.Result&#41;&#41;&#10; &#123;&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; else if &#40;task.Status &#61;&#61; TaskStatus.Faulted&#41;&#10; &#123;&#10; if &#40;TrySetException&#40;task.Exception&#41;&#41;&#10; &#123;&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; else&#10; &#123;&#10; CancelSynchronously&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; public void CancelAsynchronously&#40;&#41;&#10; &#123;&#10; &#47;&#47; Since there could be synchronous continuations on the TaskCancellationSource, we queue this to the threadpool&#10; &#47;&#47; to avoid inline running of other operations.&#10; System.Threading.Tasks.Task.Factory.StartNew&#40;CancelSynchronously, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default&#41;&#59;&#10; &#125;&#10;&#10; private void CancelSynchronously&#40;&#41;&#10; &#123;&#10; if &#40;TrySetCanceled&#40;&#41;&#41;&#10; &#123;&#10; &#47;&#47; Paranoia&#58; the only reason we should ever get here is if the CancellationToken that&#10; &#47;&#47; we registered against was cancelled, but just in case, dispose the registration&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; A lightweight mutual exclusion object which supports waiting with cancellation and prevents&#10; &#47;&#47;&#47; recursion &#40;i.e. you may not call Wait if you already hold the lock&#41;&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; The &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62; provides a lightweight mutual exclusion class that doesn&#39;t&#10; &#47;&#47;&#47; use Windows kernel synchronization primitives.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; The implementation is distilled from the workings of &#60;see cref&#61;&#34;T&#58;System.Threading.SemaphoreSlim&#34;&#47;&#62;&#10; &#47;&#47;&#47; The basic idea is that we use a regular sync object &#40;Monitor.Enter&#47;Exit&#41; to guard the setting&#10; &#47;&#47;&#47; of an &#39;owning thread&#39; field. If, during the Wait, we find the lock is held by someone else&#10; &#47;&#47;&#47; then we register a cancellation callback and enter a &#34;Monitor.Wait&#34; loop. If the cancellation&#10; &#47;&#47;&#47; callback fires, then it &#34;pulses&#34; all the waiters to wake them up and check for cancellation.&#10; &#47;&#47;&#47; Waiters are also &#34;pulsed&#34; when leaving the lock.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; All public members of &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62; are thread-safe and may be used concurrently&#10; &#47;&#47;&#47; from multiple threads.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; internal sealed class NonReentrantLock&#10; &#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; A synchronization object to protect access to the &#60;see cref&#61;&#34;F&#58;owningThread&#34;&#47;&#62; field and to be pulsed&#10; &#47;&#47;&#47; when &#60;see cref&#61;&#34;M&#58;Release&#34;&#47;&#62; is called and during cancellation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private readonly object syncLock&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Indicates which thread currently holds the lock. If null, then the lock is available.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private volatile Thread owningThread&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Constructor.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;useThisInstanceForSynchronization&#34;&#62;If false &#40;the default&#41;, then the class&#10; &#47;&#47;&#47; allocates an internal object to be used as a sync lock.&#10; &#47;&#47;&#47; If true, then the sync lock object will be the NonReentrantLock instance itself. This&#10; &#47;&#47;&#47; saves an allocation but a client may not safely further use this instance in a call to&#10; &#47;&#47;&#47; Monitor.Enter&#47;Exit or in a &#34;lock&#34; statement.&#10; &#47;&#47;&#47; &#60;&#47;param&#62;&#10; public NonReentrantLock&#40;bool useThisInstanceForSynchronization &#61; false&#41;&#10; &#123;&#10; this.syncLock &#61; useThisInstanceForSynchronization &#63; this &#58; new object&#40;&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Shared factory for use in lazy initialization.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public static readonly Func&#60;NonReentrantLock&#62; Factory &#61; &#40;&#41; &#61;&#62; new NonReentrantLock&#40;useThisInstanceForSynchronization&#58; true&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Blocks the current thread until it can enter the &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62;, while observing a&#10; &#47;&#47;&#47; &#60;see cref&#61;&#34;T&#58;System.Threading.CancellationToken&#34;&#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; Recursive locking is not supported. i.e. A thread may not call Wait successfully twice without an&#10; &#47;&#47;&#47; intervening &#60;see cref&#61;&#34;Release&#34;&#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;cancellationToken&#34;&#62;The &#60;see cref&#61;&#34;T&#58;System.Threading.CancellationToken&#34;&#47;&#62; token to&#10; &#47;&#47;&#47; observe.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;System.OperationCanceledException&#34;&#62;&#60;paramref name&#61;&#34;cancellationToken&#34;&#47;&#62; was&#10; &#47;&#47;&#47; canceled.&#60;&#47;exception&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;System.LockRecursionException&#34;&#62;The caller already holds the lock&#60;&#47;exception&#62;&#10; public void Wait&#40;CancellationToken cancellationToken &#61; default&#40;CancellationToken&#41;&#41;&#10; &#123;&#10; if &#40;this.IsOwnedByMe&#41;&#10; &#123;&#10; throw new LockRecursionException&#40;&#41;&#59;&#10; &#125;&#10;&#10; CancellationTokenRegistration cancellationTokenRegistration &#61; default&#40;CancellationTokenRegistration&#41;&#59;&#10;&#10; if &#40;cancellationToken.CanBeCanceled&#41;&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; Fast path to try and avoid allocations in callback registration.&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; if &#40;&#33;this.IsLocked&#41;&#10; &#123;&#10; this.TakeOwnership&#40;&#41;&#59;&#10; return&#59;&#10; &#125;&#10; &#125;&#10;&#10; cancellationTokenRegistration &#61; cancellationToken.Register&#40;cancellationTokenCanceledEventHandler, this.syncLock, useSynchronizationContext&#58; false&#41;&#59;&#10; &#125;&#10;&#10; using &#40;cancellationTokenRegistration&#41;&#10; &#123;&#10; &#47;&#47; PERF&#58; First spin wait for the lock to become available, but only up to the first planned yield.&#10; &#47;&#47; This additional amount of spinwaiting was inherited from SemaphoreSlim&#39;s implementation where&#10; &#47;&#47; it showed measurable perf gains in test scenarios.&#10; SpinWait spin &#61; new SpinWait&#40;&#41;&#59;&#10; while &#40;this.IsLocked &#38;&#38; &#33;spin.NextSpinWillYield&#41;&#10; &#123;&#10; spin.SpinOnce&#40;&#41;&#59;&#10; &#125;&#10;&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; while &#40;this.IsLocked&#41;&#10; &#123;&#10; &#47;&#47; If cancelled, we throw. Trying to wait could lead to deadlock.&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; using &#40;Logger.LogBlock&#40;FeatureId.WaitIndicator, FunctionId.Utilities_NonReentrantLock_BlockingWait, cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; Another thread holds the lock. Wait until we get awoken either&#10; &#47;&#47; by some code calling &#34;Release&#34; or by cancellation.&#10; Monitor.Wait&#40;this.syncLock&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; We now hold the lock&#10; this.TakeOwnership&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Exit the mutual exclusion.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; The calling thread must currently hold the lock.&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;Roslyn.Utilities.ContractFailureException&#34;&#62;The lock is not currently held by the calling thread.&#60;&#47;exception&#62;&#10; public void Release&#40;&#41;&#10; &#123;&#10; AssertHasLock&#40;&#41;&#59;&#10;&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; this.ReleaseOwnership&#40;&#41;&#59;&#10;&#10; &#47;&#47; Release one waiter&#10; Monitor.Pulse&#40;this.syncLock&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Determine if the lock is currently held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;returns&#62;True if the lock is currently held by the calling thread.&#60;&#47;returns&#62;&#10; public bool LockHeldByMe&#40;&#41;&#10; &#123;&#10; return this.IsOwnedByMe&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Throw an exception if the lock is not held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;Roslyn.Utilities.ContractFailureException&#34;&#62;The lock is not currently held by the calling thread.&#60;&#47;exception&#62;&#10; public void AssertHasLock&#40;&#41;&#10; &#123;&#10;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Checks if the lock is currently held.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool IsLocked&#10; &#123;&#10; get&#10; &#123;&#10; return this.owningThread &#33;&#61; null&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Checks if the lock is currently held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool IsOwnedByMe&#10; &#123;&#10; get&#10; &#123;&#10; return this.owningThread &#61;&#61; Thread.CurrentThread&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Take ownership of the lock &#40;by the calling thread&#41;. The lock may not already&#10; &#47;&#47;&#47; be held by any other code.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private void TakeOwnership&#40;&#41;&#10;&#123;&#10; this.owningThread &#61; Thread.CurrentThread&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Release ownership of the lock. The lock must already be held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private void ReleaseOwnership&#40;&#41;&#10; &#123;&#10;&#10; this.owningThread &#61; null&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Action object passed to a cancellation token registration.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private static readonly Action&#60;object&#62; cancellationTokenCanceledEventHandler &#61; new Action&#60;object&#62;&#40;CancellationTokenCanceledEventHandler&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Callback executed when a cancellation token is canceled during a Wait.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;obj&#34;&#62;The syncLock that protects a &#60;see cref&#61;&#34;NonReentrantLock &#34;&#47;&#62; instance.&#60;&#47;param&#62;&#10; private static void CancellationTokenCanceledEventHandler&#40;object obj&#41;&#10; &#123;&#10; lock &#40;obj&#41;&#10; &#123;&#10; &#47;&#47; Release all waiters to check their cancellation tokens.&#10; Monitor.PulseAll&#40;obj&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#125;</pre>2013-05-23T06:05:29-07:00mfhttp://rise4fun.com/Seal/mfSeal/mf<pre>using System&#59;&#10;class Test &#123;&#10; private int f &#61; 0&#59; &#10; public void C&#40;Test inst3&#41;&#123; &#10; A&#40;new Test&#40;&#41;&#41;&#59;&#10; &#47;&#47;var temp &#61; inst3.foo&#40;&#41;&#59;&#10; &#47;&#47;temp.f &#61; 3&#59;&#10; &#125; &#10; public void A&#40;Test inst&#41;&#123;&#10; inst.f &#61; 1&#59;&#10; &#125; &#10; public Test foo&#40;&#41;&#123;&#10; return this&#59;&#10; &#125;&#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00ghttp://rise4fun.com/Seal/gSeal/g<pre>using System&#59;&#10;using System.Collections.Generic&#59;&#10;using System.Linq&#59;&#10;&#10;public class LinqTest&#10;&#123;&#10; int f&#59;&#10; public void foo&#40;LinqTest param&#41;&#123;&#10; List&#60;int&#62; l &#61; new List&#60;int&#62;&#40;&#41;&#59;&#10; &#10; &#125;&#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00Fyhttp://rise4fun.com/Seal/FySeal/Fy<pre>using System&#59;&#13;&#10;using System.Collections.Generic&#59;&#13;&#10;using System.Linq&#59;&#13;&#10;&#13;&#10;public class LinqTest&#13;&#10;&#123;&#13;&#10; int f&#59;&#13;&#10; public void foo&#40;LinqTest param&#41;&#123;&#13;&#10; var col &#61; from i in IntegerGenerator&#40;param&#41;&#13;&#10; select i&#59;&#13;&#10; foreach &#40;var y in col&#41;&#13;&#10; &#123;&#13;&#10; y.f &#61; 1&#59;&#13;&#10; &#125;&#13;&#10; &#125;&#13;&#10;&#13;&#10; private IEnumerable&#60;LinqTest&#62; IntegerGenerator&#40;LinqTest r&#41; &#123;&#13;&#10; yield return r&#59;&#13;&#10; &#125;&#13;&#10;&#125;&#13;&#10;</pre>2013-05-23T06:05:29-07:00FShttp://rise4fun.com/Seal/FSSeal/FS<pre>He could say such sweet things in beetewn the coughs and as much as I wanted to ease him, still the wind grew colder around us. The dying of the season, ran its&#39; claws over us while raking him far more menacingly. Snow fell at first white and fluffy, to which he dropped something of his own, red and wet. Fluttering slowly, dripping heavily. I can&#39;t seem to remember what came first, but now it was his blood staining my clothes, my hands. They shook so hard too, dabbling at his lips.</pre>2013-05-23T06:05:29-07:00Fbhttp://rise4fun.com/Seal/FbSeal/Fb<pre>Все зависит как эти два браузера были открыты. Если оба через new IE &#40;&#41;&#59;то к ним можно обращаться через их переменные. Если они открываются по клику на какуюто линку, то можно атачиться например по свойству Find.By &#40; hwnd , значение &#41;.А можно не париться и собрать коллекцию всех открытих IE и обрабатывать их как душе угодно например&#58;&#91;STAThread&#93;static void Main &#40;string&#91;&#93; args&#41;&#123;IECollection coll &#61; IE.InternetExplorers &#40;&#41;&#59;coll&#91;0&#93;.Element &#40;Find.ByName &#40; smibut1 &#41;&#41;.Flash &#40;5&#41;&#59;coll&#91;1&#93;.Element &#40;Find.ByName &#40; smibut1 &#41;&#41;.Flash &#40;5&#41;&#59;&#125;</pre>2013-05-23T06:05:29-07:00eghttp://rise4fun.com/Seal/egSeal/eg<pre></pre>2013-05-23T06:05:29-07:00c4http://rise4fun.com/Seal/c4Seal/c4<pre>Could not find sample or program helloworld</pre>2013-05-23T06:05:29-07:00Chttp://rise4fun.com/Seal/CSeal/C<pre>using System&#59;&#10;class Test &#123;&#10; &#47;&#47;Object&#58;&#58;Equals&#40;&#41; method is a call-back which can potentially do anything&#10; public bool ConditionallyPureMethod&#40;Object param&#41;&#123;&#10; var temp &#61; new Test&#40;&#41;&#59;&#10; return param.Equals&#40;temp&#41;&#59; &#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:008jhttp://rise4fun.com/Seal/8jSeal/8j<pre>using System&#59;&#10;public class Foo &#123;&#10; int field&#59;&#10; &#10; void Bar&#40;Foo foo&#41; &#123;&#10; int i &#61; 0&#59;&#10; if &#40;i &#33;&#61; 0&#41;&#10; foo.field &#43;&#61; 1&#59;&#10; &#125;&#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:008http://rise4fun.com/Seal/8Seal/8<pre>using System&#59;&#10;class Test &#123;&#10; public Test f &#61; null&#59;&#10; public void PureMethod&#40;Test param&#41;&#123;&#10; var temp &#61; new Test&#40;&#41;&#59;&#10; temp.f &#61; param&#59; &#10; &#125;&#10;&#10; public void Plop&#40;Test param&#41; &#123;&#10; param.f &#61; param&#59;&#10; &#125;&#10; public void Plap&#40;Test param&#41; &#123;&#10; param.Plop&#40;new Test&#40;&#41;&#41;&#59;&#10; &#125;&#10; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00mZhttp://rise4fun.com/Seal/mZSeal/mZ<pre>using System&#59;&#10;class Test &#123;&#10; public Test f &#61; null&#59;&#10; public void ImpureMethod&#40;Test param&#41;&#123;&#10; var temp &#61; new Test&#40;&#41;&#59;&#10; temp.g &#61; &#34;Hello&#34;&#59; &#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:000http://rise4fun.com/Seal/0Seal/0<pre>using System&#59;&#10;class Test &#123;&#10; public Test f &#61; null&#59;&#10; public void PureMethod&#40;Test param&#41;&#123;&#10; var temp &#61; new Test&#40;&#41;&#59;&#10; Console.printLine&#40;&#34;Hello World&#34;&#41;&#59;&#10; temp.f &#61; param&#59; &#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00uohttp://rise4fun.com/Seal/uoSeal/uo<pre>&#13;&#10;</pre>2013-05-23T06:05:29-07:00ukhttp://rise4fun.com/Seal/ukSeal/uk<pre>Наверное код можно слегка оптимизировать&#58; &#60;xsl&#58;template name&#61;&#34;loop&#34;&#62;&#60;xsl&#58;param name&#61;&#34;i&#34;&#47;&#62;&#60;xsl&#58;param name&#61;&#34;count&#34;&#47;&#62;&#60;xsl&#58;if test&#61;&#34;&#36;count &#62;&#61; &#36;i&#34;&#62;&#60;option&#62;&#60;xsl&#58;value-of seeclt&#61;&#34;&#36;i&#34;&#47;&#62;&#60;&#47;option&#62;&#60;xsl&#58;call-template name&#61;&#34;loop&#34;&#62;&#60;xsl&#58;with-param name&#61;&#34;i&#34; seeclt&#61;&#34;&#36;i &#43; 1&#34;&#47;&#62;&#60;xsl&#58;with-param name&#61;&#34;count&#34; seeclt&#61;&#34;&#36;count&#34;&#47;&#62;&#60;&#47;xsl&#58;call-template&#62;&#60;&#47;xsl&#58;if&#62;&#60;&#47;xsl&#58;template&#62;</pre>2013-05-23T06:05:29-07:00UIhttp://rise4fun.com/Seal/UISeal/UI<pre>&#10;using System&#59;&#10;using System.Collections.Generic&#59;&#10;using System.Runtime.CompilerServices&#59;&#10;using System.Threading&#59;&#10;using System.Threading.Tasks&#59;&#10;using Microsoft.CodeAnalysis&#59;&#10;using Microsoft.CodeAnalysis.Text&#59;&#10;&#10;namespace Roslyn.Utilities&#10;&#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Represents a value that can be retrieved synchronously or asynchronously by many clients.&#10; &#47;&#47;&#47; The value will be computed on-demand the moment the first client asks for it. While being&#10; &#47;&#47;&#47; computed, more clients can request the value. As long as there are outstanding clients the&#10; &#47;&#47;&#47; underlying computation will proceed. If all outstanding clients cancel their request then&#10; &#47;&#47;&#47; the underlying value computation will be cancelled as well.&#10; &#47;&#47;&#47; &#10; &#47;&#47;&#47; Creators of an &#60;see cref&#61;&#34;AsyncLazy&#123;T&#125;&#34; &#47;&#62; can specify whether the result of the computation is&#10; &#47;&#47;&#47; cached for future requests or not. Choosing to not cache means the computation functions are kept&#10; &#47;&#47;&#47; alive, whereas caching means the value &#40;but not functions&#41; are kept alive once complete.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; internal sealed class AsyncLazy&#60;T&#62;&#10; &#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The underlying function that starts an asynchronous computation of the resulting value.&#10; &#47;&#47;&#47; Null&#39;ed out once we&#39;ve computed the result and we&#39;ve been asked to cache it. Otherwise,&#10; &#47;&#47;&#47; it is kept around in case the value needs to be computed again.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The underlying function that starts an synchronous computation of the resulting value.&#10; &#47;&#47;&#47; Null&#39;ed out once we&#39;ve computed the result and we&#39;ve been asked to cache it, or if we&#10; &#47;&#47;&#47; didn&#39;t get any synchronous function given to us in the first place.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Func&#60;CancellationToken, T&#62; synchronousComputeFunction&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Whether or not we should keep the value around once we&#39;ve computed it.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private readonly bool cacheResult&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The Task that holds the cached result.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Task&#60;T&#62; cachedResult&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Mutex used to protect reading and writing to all mutable objects and fields. Traces&#10; &#47;&#47;&#47; indicate that there&#39;s negligible contention on this lock, hence we can save some memory&#10; &#47;&#47;&#47; by using a single lock for all AsyncLazy instances. Only trivial and non-reentrant work&#10; &#47;&#47;&#47; should be done while holding the lock.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private static readonly NonReentrantLock gate &#61; new NonReentrantLock&#40;useThisInstanceForSynchronization&#58; true&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The hash set of all currently outstanding asynchronous requests. Null if there are no requests,&#10; &#47;&#47;&#47; and will never be empty.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private HashSet&#60;Request&#62; requests &#61; null&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; If an asynchronous request is active, the CancellationTokenSource that allows for&#10; &#47;&#47;&#47; cancelling the underlying computation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private CancellationTokenSource asynchronousComputationCancellationSource &#61; null&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Whether a computation is active or queued on any thread, whether synchronous or&#10; &#47;&#47;&#47; asynchronous.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool computationActive &#61; false&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Creates an AsyncLazy that always returns the value, analogous to &#60;see cref&#61;&#34;Task.FromResult&#123;T&#125;&#34; &#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public AsyncLazy&#40;T value&#41;&#10; &#123;&#10; this.cacheResult &#61; true&#59;&#10; this.cachedResult &#61; Task.FromResult&#40;value&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Important&#58; callers of this constructor should ensure that the compute function returns&#10; &#47;&#47;&#47; a task in a non-blocking fashion. i.e. the function should &#42;not&#42; synchronously compute&#10; &#47;&#47;&#47; a value and then return it using Task.FromResult. Instead, it should return an actual&#10; &#47;&#47;&#47; task that operates asynchronously. If this function synchronously computes a value&#10; &#47;&#47;&#47; then that will cause locks to be held in this type for excessive periods of time.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public AsyncLazy&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, bool cacheResult&#41;&#10; &#58; this&#40;asynchronousComputeFunction, synchronousComputeFunction&#58; null, cacheResult&#58; cacheResult&#41;&#10; &#123;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Creates an AsyncLazy that supports both asynchronous computation and inline synchronous&#10; &#47;&#47;&#47; computation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;asynchronousComputeFunction&#34;&#62;A function called to start the asynchronous&#10; &#47;&#47;&#47; computation. This function should be cheap and non-blocking.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;synchronousComputeFunction&#34;&#62;A function to do the work synchronously, which&#10; &#47;&#47;&#47; is allowed to block. This function should not be implemented by a simple Wait on the&#10; &#47;&#47;&#47; asynchronous value. If that&#39;s all you are doing, just don&#39;t pass a synchronous function&#10; &#47;&#47;&#47; in the first place.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;cacheResult&#34;&#62;Whether the result should be cached once the computation is&#10; &#47;&#47;&#47; complete.&#60;&#47;param&#62;&#10; public AsyncLazy&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, Func&#60;CancellationToken, T&#62; synchronousComputeFunction, bool cacheResult&#41;&#10; &#123;&#10; Contract.ThrowIfNull&#40;asynchronousComputeFunction&#41;&#59;&#10;&#10; this.asynchronousComputeFunction &#61; asynchronousComputeFunction&#59;&#10; this.synchronousComputeFunction &#61; synchronousComputeFunction&#59;&#10; this.cacheResult &#61; cacheResult&#59;&#10; &#125;&#10;&#10; &#35;region Lock Wrapper for Invariant Checking&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Takes the lock for this object and if acquired validates the invariants of this class.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private WaitThatValidatesInvariants TakeLock&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; gate.Wait&#40;cancellationToken&#41;&#59;&#10; AssertInvariants_NoLock&#40;&#41;&#59;&#10; return new WaitThatValidatesInvariants&#40;this&#41;&#59;&#10; &#125;&#10;&#10; private struct WaitThatValidatesInvariants &#58; IDisposable&#10; &#123;&#10; private readonly AsyncLazy&#60;T&#62; asyncLazy&#59;&#10;&#10; public WaitThatValidatesInvariants&#40;AsyncLazy&#60;T&#62; asyncLazy&#41;&#10; &#123;&#10; this.asyncLazy &#61; asyncLazy&#59;&#10; &#125;&#10;&#10; public void Dispose&#40;&#41;&#10; &#123;&#10; asyncLazy.AssertInvariants_NoLock&#40;&#41;&#59;&#10; gate.Release&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void AssertInvariants_NoLock&#40;&#41;&#10; &#123;&#10; &#47;&#47; Invariant &#35;1&#58; thou shalt never have an asynchronous computation running without it&#10; &#47;&#47; being considered a computation&#10; Contract.ThrowIfTrue&#40;this.asynchronousComputationCancellationSource &#33;&#61; null &#38;&#38;&#10; &#33;this.computationActive&#41;&#59;&#10;&#10; &#47;&#47; Invariant &#35;2&#58; thou shalt never waste memory holding onto empty HashSets&#10; Contract.ThrowIfTrue&#40;this.requests &#33;&#61; null &#38;&#38;&#10; this.requests.Count &#61;&#61; 0&#41;&#59;&#10;&#10; &#47;&#47; Invariant &#35;3&#58; thou shalt never have an request if there is not&#10; &#47;&#47; something trying to compute it&#10; Contract.ThrowIfTrue&#40;this.requests &#33;&#61; null &#38;&#38;&#10; &#33;this.computationActive&#41;&#59;&#10;&#10; &#47;&#47; Invariant &#35;4&#58; thou shalt never have a cached value and any computation function&#10; Contract.ThrowIfTrue&#40;this.cachedResult &#33;&#61; null &#38;&#38;&#10; &#40;this.synchronousComputeFunction &#33;&#61; null &#124;&#124; this.asynchronousComputeFunction &#33;&#61; null&#41;&#41;&#59;&#10;&#10; &#47;&#47; Invariant &#35;5&#58; thou shalt never have a synchronous computation function but not an&#10; &#47;&#47; asynchronous one&#10; Contract.ThrowIfTrue&#40;this.asynchronousComputeFunction &#61;&#61; null &#38;&#38; this.synchronousComputeFunction &#33;&#61; null&#41;&#59;&#10; &#125;&#10;&#10; &#35;endregion&#10;&#10; public bool TryGetValue&#40;out T result&#41;&#10; &#123;&#10; &#47;&#47; No need to lock here since this is only a fast check to &#10; &#47;&#47; see if the result is already computed.&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; result &#61; cachedResult.Result&#59;&#10; return true&#59;&#10; &#125;&#10;&#10; result &#61; default&#40;T&#41;&#59;&#10; return false&#59;&#10; &#125;&#10;&#10; public T GetValue&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; Request request &#61; null&#59;&#10; AsynchronousComputationToStart&#63; newAsynchronousComputation &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If cached, get immediately&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return cachedResult.Result&#59;&#10; &#125;&#10;&#10; &#47;&#47; If there is an existing computation active, we&#39;ll just create another request&#10; if &#40;computationActive&#41;&#10; &#123;&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10; &#125;&#10; else if &#40;synchronousComputeFunction &#61;&#61; null&#41;&#10; &#123;&#10; &#47;&#47; A synchronous request, but we have no synchronous function. Start off the async work&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10;&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; else&#10; &#123;&#10; &#47;&#47; We will do the computation here&#10; this.computationActive &#61; true&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; If we simply created a new asynchronous request, so wait for it. Yes, we&#39;re blocking the thread&#10; &#47;&#47; but we don&#39;t want multiple threads attempting to compute the same thing.&#10; if &#40;request &#33;&#61; null&#41;&#10; &#123;&#10; request.RegisterForCancellation&#40;OnAsynchronousRequestCancelled, cancellationToken&#41;&#59;&#10;&#10; &#47;&#47; Since we already registered for cancellation, it&#39;s possible that the registration has&#10; &#47;&#47; cancelled this new computation if we were the only requestor.&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; request&#41;&#59;&#10; &#125;&#10;&#10; return request.Task.WaitAndGetResult&#40;cancellationToken&#41;&#59;&#10; &#125;&#10; else&#10; &#123;&#10; T result&#59;&#10;&#10; &#47;&#47; We are the active computation, so let&#39;s go ahead and compute.&#10; try&#10; &#123;&#10; result &#61; synchronousComputeFunction&#40;cancellationToken&#41;&#59;&#10; &#125;&#10; catch &#40;OperationCanceledException&#41;&#10; &#123;&#10; &#47;&#47; This cancelled for some reason. We don&#39;t care why, but&#10; &#47;&#47; it means anybody else waiting for this result isn&#39;t going to get it&#10; &#47;&#47; from us.&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; this.computationActive &#61; false&#59;&#10;&#10; if &#40;requests &#33;&#61; null&#41;&#10; &#123;&#10; &#47;&#47; There&#39;s a possible improvement here&#58; there might be another synchronous caller who&#10; &#47;&#47; also wants the value. We might consider stealing their thread rather than punting&#10; &#47;&#47; to the thread pool.&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; null&#41;&#59;&#10; &#125;&#10;&#10; throw&#59;&#10; &#125;&#10; catch &#40;Exception ex&#41;&#10; &#123;&#10; &#47;&#47; We faulted for some unknown reason. We should simply fault everything.&#10; TaskCompletionSource&#60;T&#62; tcs &#61; new TaskCompletionSource&#60;T&#62;&#40;&#41;&#59;&#10; tcs.SetException&#40;ex&#41;&#59;&#10; CompleteWithTask&#40;tcs.Task, CancellationToken.None&#41;&#59;&#10;&#10; throw&#59;&#10; &#125;&#10;&#10; &#47;&#47; We have a value, so complete&#10; CompleteWithTask&#40;Task.FromResult&#40;result&#41;, CancellationToken.None&#41;&#59;&#10;&#10; return result&#59;&#10; &#125;&#10; &#125;&#10;&#10; private Request CreateNewRequest_NoLock&#40;&#41;&#10; &#123;&#10; if &#40;this.requests &#61;&#61; null&#41;&#10; &#123;&#10; this.requests &#61; new HashSet&#60;Request&#62;&#40;&#41;&#59;&#10; &#125;&#10;&#10; Request request &#61; new Request&#40;&#41;&#59;&#10; this.requests.Add&#40;request&#41;&#59;&#10; return request&#59;&#10; &#125;&#10;&#10; public Task&#60;T&#62; GetValueAsync&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; &#47;&#47; Optimization&#58; if we&#39;re already cancelled, do not pass go&#10; if &#40;cancellationToken.IsCancellationRequested&#41;&#10; &#123;&#10; return new Task&#60;T&#62;&#40;&#40;&#41; &#61;&#62; default&#40;T&#41;, cancellationToken&#41;&#59;&#10; &#125;&#10;&#10; Request request&#59;&#10; AsynchronousComputationToStart&#63; newAsynchronousComputation &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If cached, get immediately&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return cachedResult&#59;&#10; &#125;&#10;&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10;&#10; &#47;&#47; If we have either synchronous or asynchronous work current in flight, we don&#39;t need to do anything.&#10; &#47;&#47; Otherwise, we shall start an asynchronous computation for this&#10; if &#40;&#33;computationActive&#41;&#10; &#123;&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; We now have the request counted for, register for cancellation. It is critical this is&#10; &#47;&#47; done outside the lock, as our reigstration may immediately fire and we want to avoid the&#10; &#47;&#47; reentrancy&#10; request.RegisterForCancellation&#40;OnAsynchronousRequestCancelled, cancellationToken&#41;&#59;&#10;&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; request&#41;&#59;&#10; &#125;&#10;&#10; return request.Task&#59;&#10; &#125;&#10;&#10; private AsynchronousComputationToStart RegisterAsynchronousComputation_NoLock&#40;&#41;&#10; &#123;&#10; Contract.ThrowIfTrue&#40;this.computationActive&#41;&#59;&#10;&#10; this.asynchronousComputationCancellationSource &#61; new CancellationTokenSource&#40;&#41;&#59;&#10; this.computationActive &#61; true&#59;&#10;&#10; return new AsynchronousComputationToStart&#40;this.asynchronousComputeFunction, this.asynchronousComputationCancellationSource&#41;&#59;&#10; &#125;&#10;&#10; private struct AsynchronousComputationToStart&#10; &#123;&#10; public readonly Func&#60;CancellationToken, Task&#60;T&#62;&#62; AsynchronousComputeFunction&#59;&#10; public readonly CancellationTokenSource CancellationTokenSource&#59;&#10;&#10; public AsynchronousComputationToStart&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, CancellationTokenSource cancellationTokenSource&#41;&#10; &#123;&#10; this.AsynchronousComputeFunction &#61; asynchronousComputeFunction&#59;&#10; this.CancellationTokenSource &#61; cancellationTokenSource&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void StartAsynchronousComputation&#40;AsynchronousComputationToStart computationToStart, Request requestToCompleteSynchronously&#41;&#10; &#123;&#10; var cancellationToken &#61; computationToStart.CancellationTokenSource.Token&#59;&#10;&#10; try&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; DO NOT ACCESS ANY FIELDS OR STATE BEYOND THIS POINT. Since this function&#10; &#47;&#47; runs unsynchronized, it&#39;s possible that during this function this request&#10; &#47;&#47; might be cancelled, and then a whole additional request might start and&#10; &#47;&#47; complete inline, and cache the result. By grabbing state before we check&#10; &#47;&#47; the cancellation token, we can be assured that we are only operating on&#10; &#47;&#47; a state that was complete.&#10;&#10; ExceptionUtilities.ExecuteWithErrorReporting&#40;&#40;&#41; &#61;&#62;&#10; &#123;&#10; &#47;&#47; We avoid creating a full closure just to pass the token along&#10; &#47;&#47; Also, use TaskContinuationOptions.ExecuteSynchronously so that we inline &#10; &#47;&#47; the continuation if asynchronousComputeFunction completes synchronously&#10; var task &#61; computationToStart.AsynchronousComputeFunction&#40;cancellationToken&#41;&#59;&#10;&#10; task.ContinueWith&#40;&#10; &#40;t, s&#41; &#61;&#62; CompleteWithTask&#40;t, &#40;&#40;CancellationTokenSource&#41;s&#41;.Token&#41;,&#10; computationToStart.CancellationTokenSource,&#10; cancellationToken,&#10; TaskContinuationOptions.ExecuteSynchronously,&#10; TaskScheduler.Default&#41;&#59;&#10;&#10; if &#40;requestToCompleteSynchronously &#33;&#61; null &#38;&#38; task.IsCompleted&#41;&#10; &#123;&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; task &#61; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;task&#41;&#59;&#10; &#125;&#10;&#10; requestToCompleteSynchronously.CompleteFromTaskSynchronously&#40;task&#41;&#59;&#10; &#125;&#10; &#125;&#41;&#59;&#10; &#125;&#10; catch &#40;OperationCanceledException oce&#41;&#10; &#123;&#10; &#47;&#47; As long as it&#39;s the right token, this means that our thread was the first thread&#10; &#47;&#47; to start an asynchronous computation, but the requestor cancelled as we were starting up&#10; &#47;&#47; the computation.&#10; if &#40;oce.CancellationToken &#33;&#61; cancellationToken&#41;&#10; &#123;&#10; ExceptionUtilities.FailFast&#40;oce&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; private void CompleteWithTask&#40;Task&#60;T&#62; task, CancellationToken cancellationToken&#41;&#10; &#123;&#10; IEnumerable&#60;Request&#62; requestsToComplete&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If the underlying computation was cancelled, then all state was already updated in OnAsynchronousRequestCancelled&#10; &#47;&#47; and there is no new work to do here. We &#42;must&#42; use the local one since this completion may be running far after&#10; &#47;&#47; the background computation was cancelled and a new one might have already been enqueued. We must do this&#10; &#47;&#47; check here under the lock to ensure proper synchronization with OnAsynchronousRequestCancelled.&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; The computation is complete, so get all requests to complete and null out the list. We&#39;ll create another one&#10; &#47;&#47; later if it&#39;s needed&#10; requestsToComplete &#61; this.requests &#63;&#63; SpecializedCollections.EmptyEnumerable&#60;Request&#62;&#40;&#41;&#59;&#10; this.requests &#61; null&#59;&#10;&#10; &#47;&#47; The computations are done&#10; this.asynchronousComputationCancellationSource &#61; null&#59;&#10; this.computationActive &#61; false&#59;&#10; task &#61; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;task&#41;&#59;&#10; &#125;&#10;&#10; foreach &#40;var requestToComplete in requestsToComplete&#41;&#10; &#123;&#10; requestToComplete.CompleteFromTaskAsynchronously&#40;task&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; private Task&#60;T&#62; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; if &#40;this.cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return this.cachedResult&#59;&#10; &#125;&#10; else&#10; &#123;&#10; if &#40;cacheResult &#38;&#38; task.Status &#61;&#61; TaskStatus.RanToCompletion&#41;&#10; &#123;&#10; &#47;&#47; Hold onto the completed task. We can get rid of the computation functions for good&#10; this.cachedResult &#61; task&#59;&#10; this.asynchronousComputeFunction &#61; null&#59;&#10; this.synchronousComputeFunction &#61; null&#59;&#10; &#125;&#10;&#10; return task&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void OnAsynchronousRequestCancelled&#40;object state&#41;&#10; &#123;&#10; var request &#61; &#40;Request&#41;state&#59;&#10; CancellationTokenSource cancellationTokenSource &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; &#47;&#47; Now try to remove it. It&#39;s possible that requests may already be null. You could&#10; &#47;&#47; imagine that cancellation was requested, but before we could aquire the lock&#10; &#47;&#47; here the computation completed and the entire CompleteWithTask synchronized&#10; &#47;&#47; block ran. In that case, the requests collection may already be null, or it&#10; &#47;&#47; &#40;even scarier&#33;&#41; may have been replaced with another collection because another&#10; &#47;&#47; computation has started.&#10; if &#40;this.requests &#33;&#61; null&#41;&#10; &#123;&#10; if &#40;this.requests.Remove&#40;request&#41;&#41;&#10; &#123;&#10; if &#40;this.requests.Count &#61;&#61; 0&#41;&#10; &#123;&#10; this.requests &#61; null&#59;&#10;&#10; if &#40;this.asynchronousComputationCancellationSource &#33;&#61; null&#41;&#10; &#123;&#10; cancellationTokenSource &#61; this.asynchronousComputationCancellationSource&#59;&#10; this.asynchronousComputationCancellationSource &#61; null&#59;&#10; this.computationActive &#61; false&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; request.CancelAsynchronously&#40;&#41;&#59;&#10;&#10; if &#40;cancellationTokenSource &#33;&#61; null&#41;&#10; &#123;&#10; cancellationTokenSource.Cancel&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; Using inheritance instead of wrapping a TaskCompletionSource to avoid a second allocation&#10; private class Request &#58; TaskCompletionSource&#60;T&#62;&#10; &#123;&#10; private CancellationTokenRegistration cancellationTokenRegistration&#59;&#10;&#10; public void RegisterForCancellation&#40;Action&#60;object&#62; callback, CancellationToken cancellationToken&#41;&#10; &#123;&#10; cancellationTokenRegistration &#61; cancellationToken.Register&#40;callback, this&#41;&#59;&#10; &#125;&#10;&#10; public void CompleteFromTaskAsynchronously&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; System.Threading.Tasks.Task.Factory.StartNew&#40;CompleteFromTaskSynchronouslyStub, task, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default&#41;&#59;&#10; &#125;&#10;&#10; private void CompleteFromTaskSynchronouslyStub&#40;object task&#41;&#10; &#123;&#10; CompleteFromTaskSynchronously&#40;&#40;Task&#60;T&#62;&#41;task&#41;&#59;&#10; &#125;&#10;&#10; public void CompleteFromTaskSynchronously&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; if &#40;task.Status &#61;&#61; TaskStatus.RanToCompletion&#41;&#10; &#123;&#10; if &#40;TrySetResult&#40;task.Result&#41;&#41;&#10; &#123;&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; else if &#40;task.Status &#61;&#61; TaskStatus.Faulted&#41;&#10; &#123;&#10; if &#40;TrySetException&#40;task.Exception&#41;&#41;&#10; &#123;&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; else&#10; &#123;&#10; CancelSynchronously&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; public void CancelAsynchronously&#40;&#41;&#10; &#123;&#10; &#47;&#47; Since there could be synchronous continuations on the TaskCancellationSource, we queue this to the threadpool&#10; &#47;&#47; to avoid inline running of other operations.&#10; System.Threading.Tasks.Task.Factory.StartNew&#40;CancelSynchronously, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default&#41;&#59;&#10; &#125;&#10;&#10; private void CancelSynchronously&#40;&#41;&#10; &#123;&#10; if &#40;TrySetCanceled&#40;&#41;&#41;&#10; &#123;&#10; &#47;&#47; Paranoia&#58; the only reason we should ever get here is if the CancellationToken that&#10; &#47;&#47; we registered against was cancelled, but just in case, dispose the registration&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#125;</pre>2013-05-23T06:05:29-07:00XGhttp://rise4fun.com/Seal/XGSeal/XG<pre>using System&#59;&#13;&#10;class Test &#123;&#13;&#10; &#47;&#47;Object&#58;&#58;Equals&#40;&#41; method is a call-back which can potentially do anything&#13;&#10; public bool ConditionallyPureMethod&#40;Object param&#41;&#123;&#13;&#10; var temp &#61; new Test&#40;&#41;&#59;&#13;&#10; return param.Equals&#40;temp&#41;&#59; &#13;&#10; &#125; &#13;&#10;&#125;&#13;&#10;</pre>2013-05-23T06:05:29-07:00nNhttp://rise4fun.com/Seal/nNSeal/nN<pre>using System&#59;&#10;class Test &#123;&#10; Test f&#59;&#10; String g&#59;&#10; public void ImpureMethod&#40;Test param&#41;&#123; &#10; var x &#61; param.f&#59;&#10; while &#40;x &#33;&#61; null&#41;&#10; &#123;&#10; x.g &#61; &#34;Hello&#34;&#59;&#10; x &#61; x.f&#59;&#10; &#125; &#10; &#125; &#10;&#125;</pre>2013-05-23T06:05:29-07:00nMhttp://rise4fun.com/Seal/nMSeal/nM<pre>using System&#59;&#10;class Test &#123;&#10; public Test f &#61; null&#59;&#10; public void PureMethod&#40;Test param&#41;&#123;&#10; var temp &#61; new Test&#40;&#41;&#59;&#10; temp.f &#61; param&#59; &#10; f &#61; param&#59;&#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00NIhttp://rise4fun.com/Seal/NISeal/NI<pre>main&#40;&#41;&#123;&#10;&#125;</pre>2013-05-23T06:05:29-07:00NGhttp://rise4fun.com/Seal/NGSeal/NG<pre>using System&#59;&#10;class Test &#123;&#10; int x &#61; 13&#59;&#10; public Test f &#61; null&#59;&#10; public void PureMethod&#40;Test param&#41;&#123;&#10; var temp &#61; new Test&#40;&#41;&#59;&#10; temp.f &#61; param&#59; &#10; x &#43;&#61; 1&#59; &#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00hKahttp://rise4fun.com/Seal/hKaSeal/hKa<pre>я бы сделал через&#60;xsl&#58;apply-templates mode&#61;&#34;year&#34;&#47;&#62; и еще добавил бы &#60;xsl&#58;param name&#61;&#34;i&#34; seeclt&#61;&#34;1900&#34;&#47;&#62;&#60;xsl&#58;param name&#61;&#34;count&#34; seeclt&#61;&#34;2008&#34;&#47;&#62;</pre>2013-05-23T06:05:29-07:00hkhttp://rise4fun.com/Seal/hkSeal/hk<pre>using System&#59;&#10;class Test &#123;&#10; public Test f &#61; null&#59;&#10; public void PureMethod&#40;Test param&#41;&#123;&#10; var temp &#61; new Test&#40;&#41;&#59;&#10; temp.f &#61; param&#59;&#10;f &#61; param&#59; &#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00J3http://rise4fun.com/Seal/J3Seal/J3<pre>Whoa, thgins just got a whole lot easier.</pre>2013-05-23T06:05:29-07:00hE3http://rise4fun.com/Seal/hE3Seal/hE3<pre>using System&#59;&#10;class Test &#123;&#10; public Test f &#61; null&#59;&#10; public Test q &#61; null&#59;&#10; public void PureMethod&#40;Test param, Test param2&#41;&#123;&#10; var temp &#61; new Test&#40;&#41;&#59;&#10; temp.f &#61; param&#59;&#10; temp.q &#61; param2&#59; &#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00s6http://rise4fun.com/Seal/s6Seal/s6<pre>using System&#59;&#13;&#10;class Test &#123;&#13;&#10; public Test f &#61; null&#59;&#13;&#10; public void PureMethod&#40;Test param&#41;&#123;&#13;&#10; var temp &#61; new Test&#40;&#41;&#59;&#13;&#10; temp.f &#61; param&#59; &#13;&#10; &#125; &#13;&#10;&#125;&#13;&#10;</pre>2013-05-23T06:05:29-07:00czhttp://rise4fun.com/Seal/czSeal/cz<pre>using System&#59;&#10;class Test &#123;&#10; Test f&#59;&#10; String g&#59;&#10;public static param&#59;&#10; public void ImpureMethod&#40;&#41;&#123; &#10; var x &#61; param.f&#59;&#10; x.g &#61; &#34;Hello&#34;&#59;&#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00NYhttp://rise4fun.com/Seal/NYSeal/NY<pre>using System&#59;&#13;&#10;class TestInstanceDelegates&#123;&#13;&#10; TestInstanceDelegates f &#61; null&#59; &#13;&#10;&#13;&#10; void foo&#40;System.Func&#60;TestInstanceDelegates, int&#62; del&#41;&#123;&#13;&#10; del&#40;this&#41;&#59;&#13;&#10; &#125;&#13;&#10;&#13;&#10; public void start&#40;TestInstanceDelegates testDel1, TestInstanceDelegates testDel2&#41;&#123; &#13;&#10; testDel1.foo&#40;&#40;TestInstanceDelegates param&#41; &#61;&#62;&#13;&#10; &#123;&#13;&#10; testDel2.f &#61; new TestInstanceDelegates&#40;&#41;&#59;&#13;&#10; param.f &#61; testDel2.f&#59;&#13;&#10; return 0&#59;&#13;&#10; &#125;&#41;&#59;&#13;&#10; &#125;&#13;&#10;&#125;&#13;&#10;</pre>2013-05-23T06:05:29-07:008Vhttp://rise4fun.com/Seal/8VSeal/8V<pre>using System&#59;&#10;class Test &#123;&#10; Test f&#59;&#10; String g&#59;&#10; static Test param&#59;&#10; public void ImpureMethod&#40;&#41;&#123; &#10; var x &#61; param.f&#59;&#10; x.g &#61; &#34;Hello&#34;&#59;&#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00SRhttp://rise4fun.com/Seal/SRSeal/SR<pre>using System&#59;&#13;&#10;class Test &#123;&#13;&#10; public Test f &#61; null&#59;&#13;&#10; public void ImpureMethod&#40;Test param&#41;&#123;&#13;&#10; var temp &#61; new Test&#40;&#41;&#59;&#13;&#10; param.f &#61; temp&#59; &#13;&#10; &#125; &#13;&#10;&#125;&#13;&#10;</pre>2013-05-23T06:05:29-07:002Chttp://rise4fun.com/Seal/2CSeal/2C<pre>using System&#59;&#13;&#10;using System.Collections.Generic&#59;&#13;&#10;using System.Linq&#59;&#13;&#10;&#13;&#10;public class LinqTest&#13;&#10;&#123;&#13;&#10; int f&#59;&#13;&#10; public void foo&#40;LinqTest param&#41;&#123;&#13;&#10; var col &#61; from i in IntegerGenerator&#40;param&#41;&#13;&#10; select i&#59;&#13;&#10; foreach &#40;var y in col&#41;&#13;&#10; &#123;&#13;&#10; y.f &#61; 1&#59;&#13;&#10; &#125;&#13;&#10; &#125;&#13;&#10;&#13;&#10; private IEnumerable&#60;LinqTest&#62; IntegerGenerator&#40;LinqTest r&#41; &#123;&#13;&#10; yield return r&#59;&#13;&#10; &#125;&#13;&#10;&#125;&#13;&#10;bkj </pre>2013-05-23T06:05:29-07:0007http://rise4fun.com/Seal/07Seal/07<pre>using System&#59;&#10;class Test &#123;&#10; public Test f &#61; null&#59;&#10; public void PureMethod&#40;Test param&#41;&#123;&#10; var temp &#61; new Test&#40;&#41;&#59;&#10; temp.f &#61; param&#59; &#10; f &#61; param&#59; &#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00ZKhttp://rise4fun.com/Seal/ZKSeal/ZK<pre>using System&#59;&#10;using System.Collections.Generic&#59;&#10;using System.Runtime.CompilerServices&#59;&#10;using System.Threading&#59;&#10;using System.Threading.Tasks&#59;&#10;&#10;namespace Roslyn.Utilities&#10;&#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Represents a value that can be retrieved synchronously or asynchronously by many clients.&#10; &#47;&#47;&#47; The value will be computed on-demand the moment the first client asks for it. While being&#10; &#47;&#47;&#47; computed, more clients can request the value. As long as there are outstanding clients the&#10; &#47;&#47;&#47; underlying computation will proceed. If all outstanding clients cancel their request then&#10; &#47;&#47;&#47; the underlying value computation will be cancelled as well.&#10; &#47;&#47;&#47; &#10; &#47;&#47;&#47; Creators of an &#60;see cref&#61;&#34;AsyncLazy&#123;T&#125;&#34; &#47;&#62; can specify whether the result of the computation is&#10; &#47;&#47;&#47; cached for future requests or not. Choosing to not cache means the computation functions are kept&#10; &#47;&#47;&#47; alive, whereas caching means the value &#40;but not functions&#41; are kept alive once complete.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; internal sealed class AsyncLazy&#60;T&#62;&#10; &#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The underlying function that starts an asynchronous computation of the resulting value.&#10; &#47;&#47;&#47; Null&#39;ed out once we&#39;ve computed the result and we&#39;ve been asked to cache it. Otherwise,&#10; &#47;&#47;&#47; it is kept around in case the value needs to be computed again.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The underlying function that starts an synchronous computation of the resulting value.&#10; &#47;&#47;&#47; Null&#39;ed out once we&#39;ve computed the result and we&#39;ve been asked to cache it, or if we&#10; &#47;&#47;&#47; didn&#39;t get any synchronous function given to us in the first place.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Func&#60;CancellationToken, T&#62; synchronousComputeFunction&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Whether or not we should keep the value around once we&#39;ve computed it.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private readonly bool cacheResult&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The Task that holds the cached result.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Task&#60;T&#62; cachedResult&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Mutex used to protect reading and writing to all mutable objects and fields. Traces&#10; &#47;&#47;&#47; indicate that there&#39;s negligible contention on this lock, hence we can save some memory&#10; &#47;&#47;&#47; by using a single lock for all AsyncLazy instances. Only trivial and non-reentrant work&#10; &#47;&#47;&#47; should be done while holding the lock.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private static readonly NonReentrantLock gate &#61; new NonReentrantLock&#40;useThisInstanceForSynchronization&#58; true&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The hash set of all currently outstanding asynchronous requests. Null if there are no requests,&#10; &#47;&#47;&#47; and will never be empty.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private HashSet&#60;Request&#62; requests &#61; null&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; If an asynchronous request is active, the CancellationTokenSource that allows for&#10; &#47;&#47;&#47; cancelling the underlying computation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private CancellationTokenSource asynchronousComputationCancellationSource &#61; null&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Whether a computation is active or queued on any thread, whether synchronous or&#10; &#47;&#47;&#47; asynchronous.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool computationActive &#61; false&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Creates an AsyncLazy that always returns the value, analogous to &#60;see cref&#61;&#34;Task.FromResult&#123;T&#125;&#34; &#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public AsyncLazy&#40;T value&#41;&#10; &#123;&#10; this.cacheResult &#61; true&#59;&#10; this.cachedResult &#61; new Task&#60;T&#62;&#40;&#40;&#41; &#61;&#62; &#123;return value&#59;&#125;&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Important&#58; callers of this constructor should ensure that the compute function returns&#10; &#47;&#47;&#47; a task in a non-blocking fashion. i.e. the function should &#42;not&#42; synchronously compute&#10; &#47;&#47;&#47; a value and then return it using Task.FromResult. Instead, it should return an actual&#10; &#47;&#47;&#47; task that operates asynchronously. If this function synchronously computes a value&#10; &#47;&#47;&#47; then that will cause locks to be held in this type for excessive periods of time.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public AsyncLazy&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, bool cacheResult&#41;&#10; &#58; this&#40;asynchronousComputeFunction, synchronousComputeFunction&#58; null, cacheResult&#58; cacheResult&#41;&#10; &#123;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Creates an AsyncLazy that supports both asynchronous computation and inline synchronous&#10; &#47;&#47;&#47; computation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;asynchronousComputeFunction&#34;&#62;A function called to start the asynchronous&#10; &#47;&#47;&#47; computation. This function should be cheap and non-blocking.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;synchronousComputeFunction&#34;&#62;A function to do the work synchronously, which&#10; &#47;&#47;&#47; is allowed to block. This function should not be implemented by a simple Wait on the&#10; &#47;&#47;&#47; asynchronous value. If that&#39;s all you are doing, just don&#39;t pass a synchronous function&#10; &#47;&#47;&#47; in the first place.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;cacheResult&#34;&#62;Whether the result should be cached once the computation is&#10; &#47;&#47;&#47; complete.&#60;&#47;param&#62;&#10; public AsyncLazy&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, Func&#60;CancellationToken, T&#62; synchronousComputeFunction, bool cacheResult&#41;&#10; &#123;&#10; this.asynchronousComputeFunction &#61; asynchronousComputeFunction&#59;&#10; this.synchronousComputeFunction &#61; synchronousComputeFunction&#59;&#10; this.cacheResult &#61; cacheResult&#59;&#10; &#125;&#10;&#10; &#35;region Lock Wrapper for Invariant Checking&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Takes the lock for this object and if acquired validates the invariants of this class.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private WaitThatValidatesInvariants TakeLock&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; gate.Wait&#40;cancellationToken&#41;&#59;&#10; AssertInvariants_NoLock&#40;&#41;&#59;&#10; return new WaitThatValidatesInvariants&#40;this&#41;&#59;&#10; &#125;&#10;&#10; private struct WaitThatValidatesInvariants &#58; IDisposable&#10; &#123;&#10; private readonly AsyncLazy&#60;T&#62; asyncLazy&#59;&#10;&#10; public WaitThatValidatesInvariants&#40;AsyncLazy&#60;T&#62; asyncLazy&#41;&#10; &#123;&#10; this.asyncLazy &#61; asyncLazy&#59;&#10; &#125;&#10;&#10; public void Dispose&#40;&#41;&#10; &#123;&#10; asyncLazy.AssertInvariants_NoLock&#40;&#41;&#59;&#10; gate.Release&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void AssertInvariants_NoLock&#40;&#41;&#10; &#123;&#10;&#10; &#125;&#10;&#10; &#35;endregion&#10;&#10; public bool TryGetValue&#40;out T result&#41;&#10; &#123;&#10; &#47;&#47; No need to lock here since this is only a fast check to &#10; &#47;&#47; see if the result is already computed.&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; result &#61; cachedResult.Result&#59;&#10; return true&#59;&#10; &#125;&#10;&#10; result &#61; default&#40;T&#41;&#59;&#10; return false&#59;&#10; &#125;&#10;&#10; public T GetValue&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; Request request &#61; null&#59;&#10; AsynchronousComputationToStart&#63; newAsynchronousComputation &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If cached, get immediately&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return cachedResult.Result&#59;&#10; &#125;&#10;&#10; &#47;&#47; If there is an existing computation active, we&#39;ll just create another request&#10; if &#40;computationActive&#41;&#10; &#123;&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10; &#125;&#10; else if &#40;synchronousComputeFunction &#61;&#61; null&#41;&#10; &#123;&#10; &#47;&#47; A synchronous request, but we have no synchronous function. Start off the async work&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10;&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; else&#10; &#123;&#10; &#47;&#47; We will do the computation here&#10; this.computationActive &#61; true&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; If we simply created a new asynchronous request, so wait for it. Yes, we&#39;re blocking the thread&#10; &#47;&#47; but we don&#39;t want multiple threads attempting to compute the same thing.&#10; if &#40;request &#33;&#61; null&#41;&#10; &#123;&#10; request.RegisterForCancellation&#40;OnAsynchronousRequestCancelled, cancellationToken&#41;&#59;&#10;&#10; &#47;&#47; Since we already registered for cancellation, it&#39;s possible that the registration has&#10; &#47;&#47; cancelled this new computation if we were the only requestor.&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; request&#41;&#59;&#10; &#125;&#10;&#10; return WaitAndGetResult&#40;request.Task, cancellationToken&#41;&#59;&#10; &#125;&#10; else&#10; &#123;&#10; T result&#59;&#10;&#10; &#47;&#47; We are the active computation, so let&#39;s go ahead and compute.&#10; try&#10; &#123;&#10; result &#61; synchronousComputeFunction&#40;cancellationToken&#41;&#59;&#10; &#125;&#10; catch &#40;OperationCanceledException&#41;&#10; &#123;&#10; &#47;&#47; This cancelled for some reason. We don&#39;t care why, but&#10; &#47;&#47; it means anybody else waiting for this result isn&#39;t going to get it&#10; &#47;&#47; from us.&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; this.computationActive &#61; false&#59;&#10;&#10; if &#40;requests &#33;&#61; null&#41;&#10; &#123;&#10; &#47;&#47; There&#39;s a possible improvement here&#58; there might be another synchronous caller who&#10; &#47;&#47; also wants the value. We might consider stealing their thread rather than punting&#10; &#47;&#47; to the thread pool.&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; null&#41;&#59;&#10; &#125;&#10;&#10; throw&#59;&#10; &#125;&#10; catch &#40;Exception ex&#41;&#10; &#123;&#10; &#47;&#47; We faulted for some unknown reason. We should simply fault everything.&#10; TaskCompletionSource&#60;T&#62; tcs &#61; new TaskCompletionSource&#60;T&#62;&#40;&#41;&#59;&#10; tcs.SetException&#40;ex&#41;&#59;&#10; CompleteWithTask&#40;tcs.Task, CancellationToken.None&#41;&#59;&#10;&#10; throw&#59;&#10; &#125;&#10;&#10; &#47;&#47; We have a value, so complete&#10; CompleteWithTask&#40;new Task&#60;T&#62;&#40;&#40;&#41;&#61;&#62; result&#41;, CancellationToken.None&#41;&#59;&#10;&#10; return result&#59;&#10; &#125;&#10; &#125;&#10; &#10; private T WaitAndGetResult&#40;Task&#60;T&#62; task, CancellationToken cancellationToken&#41;&#10; &#123;&#10; task.Wait&#40;cancellationToken&#41;&#59;&#10; return task.Result&#59;&#10; &#125;&#10;&#10; private Request CreateNewRequest_NoLock&#40;&#41;&#10; &#123;&#10; if &#40;this.requests &#61;&#61; null&#41;&#10; &#123;&#10; this.requests &#61; new HashSet&#60;Request&#62;&#40;&#41;&#59;&#10; &#125;&#10;&#10; Request request &#61; new Request&#40;&#41;&#59;&#10; this.requests.Add&#40;request&#41;&#59;&#10; return request&#59;&#10; &#125;&#10;&#10; public Task&#60;T&#62; GetValueAsync&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; &#47;&#47; Optimization&#58; if we&#39;re already cancelled, do not pass go&#10; if &#40;cancellationToken.IsCancellationRequested&#41;&#10; &#123;&#10; return new Task&#60;T&#62;&#40;&#40;&#41; &#61;&#62; default&#40;T&#41;, cancellationToken&#41;&#59;&#10; &#125;&#10;&#10; Request request&#59;&#10; AsynchronousComputationToStart&#63; newAsynchronousComputation &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If cached, get immediately&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return cachedResult&#59;&#10; &#125;&#10;&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10;&#10; &#47;&#47; If we have either synchronous or asynchronous work current in flight, we don&#39;t need to do anything.&#10; &#47;&#47; Otherwise, we shall start an asynchronous computation for this&#10; if &#40;&#33;computationActive&#41;&#10; &#123;&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; We now have the request counted for, register for cancellation. It is critical this is&#10; &#47;&#47; done outside the lock, as our reigstration may immediately fire and we want to avoid the&#10; &#47;&#47; reentrancy&#10; request.RegisterForCancellation&#40;OnAsynchronousRequestCancelled, cancellationToken&#41;&#59;&#10;&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; request&#41;&#59;&#10; &#125;&#10;&#10; return request.Task&#59;&#10; &#125;&#10;&#10; private AsynchronousComputationToStart RegisterAsynchronousComputation_NoLock&#40;&#41;&#10; &#123;&#10; this.asynchronousComputationCancellationSource &#61; new CancellationTokenSource&#40;&#41;&#59;&#10; this.computationActive &#61; true&#59;&#10;&#10; return new AsynchronousComputationToStart&#40;this.asynchronousComputeFunction, this.asynchronousComputationCancellationSource&#41;&#59;&#10; &#125;&#10;&#10; private struct AsynchronousComputationToStart&#10; &#123;&#10; public readonly Func&#60;CancellationToken, Task&#60;T&#62;&#62; AsynchronousComputeFunction&#59;&#10; public readonly CancellationTokenSource CancellationTokenSource&#59;&#10;&#10; public AsynchronousComputationToStart&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, CancellationTokenSource cancellationTokenSource&#41;&#10; &#123;&#10; this.AsynchronousComputeFunction &#61; asynchronousComputeFunction&#59;&#10; this.CancellationTokenSource &#61; cancellationTokenSource&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void StartAsynchronousComputation&#40;AsynchronousComputationToStart computationToStart, Request requestToCompleteSynchronously&#41;&#10; &#123;&#10; var cancellationToken &#61; computationToStart.CancellationTokenSource.Token&#59;&#10;&#10; try&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; DO NOT ACCESS ANY FIELDS OR STATE BEYOND THIS POINT. Since this function&#10; &#47;&#47; runs unsynchronized, it&#39;s possible that during this function this request&#10; &#47;&#47; might be cancelled, and then a whole additional request might start and&#10; &#47;&#47; complete inline, and cache the result. By grabbing state before we check&#10; &#47;&#47; the cancellation token, we can be assured that we are only operating on&#10; &#47;&#47; a state that was complete.&#10;&#10; ExceptionUtilities.ExecuteWithErrorReporting&#40;&#40;&#41; &#61;&#62;&#10; &#123;&#10; &#47;&#47; We avoid creating a full closure just to pass the token along&#10; &#47;&#47; Also, use TaskContinuationOptions.ExecuteSynchronously so that we inline &#10; &#47;&#47; the continuation if asynchronousComputeFunction completes synchronously&#10; var task &#61; computationToStart.AsynchronousComputeFunction&#40;cancellationToken&#41;&#59;&#10;&#10; task.ContinueWith&#40;&#10; &#40;t, s&#41; &#61;&#62; CompleteWithTask&#40;t, &#40;&#40;CancellationTokenSource&#41;s&#41;.Token&#41;,&#10; computationToStart.CancellationTokenSource,&#10; cancellationToken,&#10; TaskContinuationOptions.ExecuteSynchronously,&#10; TaskScheduler.Default&#41;&#59;&#10;&#10; if &#40;requestToCompleteSynchronously &#33;&#61; null &#38;&#38; task.IsCompleted&#41;&#10; &#123;&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; task &#61; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;task&#41;&#59;&#10; &#125;&#10;&#10; requestToCompleteSynchronously.CompleteFromTaskSynchronously&#40;task&#41;&#59;&#10; &#125;&#10; &#125;&#41;&#59;&#10; &#125;&#10; catch &#40;OperationCanceledException oce&#41;&#10; &#123;&#10; &#47;&#47; As long as it&#39;s the right token, this means that our thread was the first thread&#10; &#47;&#47; to start an asynchronous computation, but the requestor cancelled as we were starting up&#10; &#47;&#47; the computation.&#10; if &#40;oce.CancellationToken &#33;&#61; cancellationToken&#41;&#10; &#123;&#10; ExceptionUtilities.FailFast&#40;oce&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; private void CompleteWithTask&#40;Task&#60;T&#62; task, CancellationToken cancellationToken&#41;&#10; &#123;&#10; IEnumerable&#60;Request&#62; requestsToComplete&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If the underlying computation was cancelled, then all state was already updated in OnAsynchronousRequestCancelled&#10; &#47;&#47; and there is no new work to do here. We &#42;must&#42; use the local one since this completion may be running far after&#10; &#47;&#47; the background computation was cancelled and a new one might have already been enqueued. We must do this&#10; &#47;&#47; check here under the lock to ensure proper synchronization with OnAsynchronousRequestCancelled.&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; The computation is complete, so get all requests to complete and null out the list. We&#39;ll create another one&#10; &#47;&#47; later if it&#39;s needed&#10; requestsToComplete &#61; this.requests &#63;&#63; SpecializedCollections.EmptyEnumerable&#60;Request&#62;&#40;&#41;&#59;&#10; this.requests &#61; null&#59;&#10;&#10; &#47;&#47; The computations are done&#10; this.asynchronousComputationCancellationSource &#61; null&#59;&#10; this.computationActive &#61; false&#59;&#10; task &#61; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;task&#41;&#59;&#10; &#125;&#10;&#10; foreach &#40;var requestToComplete in requestsToComplete&#41;&#10; &#123;&#10; requestToComplete.CompleteFromTaskAsynchronously&#40;task&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; private Task&#60;T&#62; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; if &#40;this.cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return this.cachedResult&#59;&#10; &#125;&#10; else&#10; &#123;&#10; if &#40;cacheResult &#38;&#38; task.Status &#61;&#61; TaskStatus.RanToCompletion&#41;&#10; &#123;&#10; &#47;&#47; Hold onto the completed task. We can get rid of the computation functions for good&#10; this.cachedResult &#61; task&#59;&#10; this.asynchronousComputeFunction &#61; null&#59;&#10; this.synchronousComputeFunction &#61; null&#59;&#10; &#125;&#10;&#10; return task&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void OnAsynchronousRequestCancelled&#40;object state&#41;&#10; &#123;&#10; var request &#61; &#40;Request&#41;state&#59;&#10; CancellationTokenSource cancellationTokenSource &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; &#47;&#47; Now try to remove it. It&#39;s possible that requests may already be null. You could&#10; &#47;&#47; imagine that cancellation was requested, but before we could aquire the lock&#10; &#47;&#47; here the computation completed and the entire CompleteWithTask synchronized&#10; &#47;&#47; block ran. In that case, the requests collection may already be null, or it&#10; &#47;&#47; &#40;even scarier&#33;&#41; may have been replaced with another collection because another&#10; &#47;&#47; computation has started.&#10; if &#40;this.requests &#33;&#61; null&#41;&#10; &#123;&#10; if &#40;this.requests.Remove&#40;request&#41;&#41;&#10; &#123;&#10; if &#40;this.requests.Count &#61;&#61; 0&#41;&#10; &#123;&#10; this.requests &#61; null&#59;&#10;&#10; if &#40;this.asynchronousComputationCancellationSource &#33;&#61; null&#41;&#10; &#123;&#10; cancellationTokenSource &#61; this.asynchronousComputationCancellationSource&#59;&#10; this.asynchronousComputationCancellationSource &#61; null&#59;&#10; this.computationActive &#61; false&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; request.CancelAsynchronously&#40;&#41;&#59;&#10;&#10; if &#40;cancellationTokenSource &#33;&#61; null&#41;&#10; &#123;&#10; cancellationTokenSource.Cancel&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; Using inheritance instead of wrapping a TaskCompletionSource to avoid a second allocation&#10; private class Request &#58; TaskCompletionSource&#60;T&#62;&#10; &#123;&#10; private CancellationTokenRegistration cancellationTokenRegistration&#59;&#10;&#10; public void RegisterForCancellation&#40;Action&#60;object&#62; callback, CancellationToken cancellationToken&#41;&#10; &#123;&#10; cancellationTokenRegistration &#61; cancellationToken.Register&#40;callback, this&#41;&#59;&#10; &#125;&#10;&#10; public void CompleteFromTaskAsynchronously&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; System.Threading.Tasks.Task.Factory.StartNew&#40;CompleteFromTaskSynchronouslyStub, task, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default&#41;&#59;&#10; &#125;&#10;&#10; private void CompleteFromTaskSynchronouslyStub&#40;object task&#41;&#10; &#123;&#10; CompleteFromTaskSynchronously&#40;&#40;Task&#60;T&#62;&#41;task&#41;&#59;&#10; &#125;&#10;&#10; public void CompleteFromTaskSynchronously&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; if &#40;task.Status &#61;&#61; TaskStatus.RanToCompletion&#41;&#10; &#123;&#10; if &#40;TrySetResult&#40;task.Result&#41;&#41;&#10; &#123;&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; else if &#40;task.Status &#61;&#61; TaskStatus.Faulted&#41;&#10; &#123;&#10; if &#40;TrySetException&#40;task.Exception&#41;&#41;&#10; &#123;&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; else&#10; &#123;&#10; CancelSynchronously&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; public void CancelAsynchronously&#40;&#41;&#10; &#123;&#10; &#47;&#47; Since there could be synchronous continuations on the TaskCancellationSource, we queue this to the threadpool&#10; &#47;&#47; to avoid inline running of other operations.&#10; System.Threading.Tasks.Task.Factory.StartNew&#40;CancelSynchronously, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default&#41;&#59;&#10; &#125;&#10;&#10; private void CancelSynchronously&#40;&#41;&#10; &#123;&#10; if &#40;TrySetCanceled&#40;&#41;&#41;&#10; &#123;&#10; &#47;&#47; Paranoia&#58; the only reason we should ever get here is if the CancellationToken that&#10; &#47;&#47; we registered against was cancelled, but just in case, dispose the registration&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; A lightweight mutual exclusion object which supports waiting with cancellation and prevents&#10; &#47;&#47;&#47; recursion &#40;i.e. you may not call Wait if you already hold the lock&#41;&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; The &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62; provides a lightweight mutual exclusion class that doesn&#39;t&#10; &#47;&#47;&#47; use Windows kernel synchronization primitives.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; The implementation is distilled from the workings of &#60;see cref&#61;&#34;T&#58;System.Threading.SemaphoreSlim&#34;&#47;&#62;&#10; &#47;&#47;&#47; The basic idea is that we use a regular sync object &#40;Monitor.Enter&#47;Exit&#41; to guard the setting&#10; &#47;&#47;&#47; of an &#39;owning thread&#39; field. If, during the Wait, we find the lock is held by someone else&#10; &#47;&#47;&#47; then we register a cancellation callback and enter a &#34;Monitor.Wait&#34; loop. If the cancellation&#10; &#47;&#47;&#47; callback fires, then it &#34;pulses&#34; all the waiters to wake them up and check for cancellation.&#10; &#47;&#47;&#47; Waiters are also &#34;pulsed&#34; when leaving the lock.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; All public members of &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62; are thread-safe and may be used concurrently&#10; &#47;&#47;&#47; from multiple threads.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; internal sealed class NonReentrantLock&#10; &#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; A synchronization object to protect access to the &#60;see cref&#61;&#34;F&#58;owningThread&#34;&#47;&#62; field and to be pulsed&#10; &#47;&#47;&#47; when &#60;see cref&#61;&#34;M&#58;Release&#34;&#47;&#62; is called and during cancellation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private readonly object syncLock&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Indicates which thread currently holds the lock. If null, then the lock is available.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private volatile Thread owningThread&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Constructor.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;useThisInstanceForSynchronization&#34;&#62;If false &#40;the default&#41;, then the class&#10; &#47;&#47;&#47; allocates an internal object to be used as a sync lock.&#10; &#47;&#47;&#47; If true, then the sync lock object will be the NonReentrantLock instance itself. This&#10; &#47;&#47;&#47; saves an allocation but a client may not safely further use this instance in a call to&#10; &#47;&#47;&#47; Monitor.Enter&#47;Exit or in a &#34;lock&#34; statement.&#10; &#47;&#47;&#47; &#60;&#47;param&#62;&#10; public NonReentrantLock&#40;bool useThisInstanceForSynchronization &#61; false&#41;&#10; &#123;&#10; this.syncLock &#61; useThisInstanceForSynchronization &#63; this &#58; new object&#40;&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Shared factory for use in lazy initialization.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public static readonly Func&#60;NonReentrantLock&#62; Factory &#61; &#40;&#41; &#61;&#62; new NonReentrantLock&#40;useThisInstanceForSynchronization&#58; true&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Blocks the current thread until it can enter the &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62;, while observing a&#10; &#47;&#47;&#47; &#60;see cref&#61;&#34;T&#58;System.Threading.CancellationToken&#34;&#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; Recursive locking is not supported. i.e. A thread may not call Wait successfully twice without an&#10; &#47;&#47;&#47; intervening &#60;see cref&#61;&#34;Release&#34;&#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;cancellationToken&#34;&#62;The &#60;see cref&#61;&#34;T&#58;System.Threading.CancellationToken&#34;&#47;&#62; token to&#10; &#47;&#47;&#47; observe.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;System.OperationCanceledException&#34;&#62;&#60;paramref name&#61;&#34;cancellationToken&#34;&#47;&#62; was&#10; &#47;&#47;&#47; canceled.&#60;&#47;exception&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;System.LockRecursionException&#34;&#62;The caller already holds the lock&#60;&#47;exception&#62;&#10; public void Wait&#40;CancellationToken cancellationToken &#61; default&#40;CancellationToken&#41;&#41;&#10; &#123;&#10; if &#40;this.IsOwnedByMe&#41;&#10; &#123;&#10; throw new LockRecursionException&#40;&#41;&#59;&#10; &#125;&#10;&#10; CancellationTokenRegistration cancellationTokenRegistration &#61; default&#40;CancellationTokenRegistration&#41;&#59;&#10;&#10; if &#40;cancellationToken.CanBeCanceled&#41;&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; Fast path to try and avoid allocations in callback registration.&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; if &#40;&#33;this.IsLocked&#41;&#10; &#123;&#10; this.TakeOwnership&#40;&#41;&#59;&#10; return&#59;&#10; &#125;&#10; &#125;&#10;&#10; cancellationTokenRegistration &#61; cancellationToken.Register&#40;cancellationTokenCanceledEventHandler, this.syncLock, useSynchronizationContext&#58; false&#41;&#59;&#10; &#125;&#10;&#10; using &#40;cancellationTokenRegistration&#41;&#10; &#123;&#10; &#47;&#47; PERF&#58; First spin wait for the lock to become available, but only up to the first planned yield.&#10; &#47;&#47; This additional amount of spinwaiting was inherited from SemaphoreSlim&#39;s implementation where&#10; &#47;&#47; it showed measurable perf gains in test scenarios.&#10; SpinWait spin &#61; new SpinWait&#40;&#41;&#59;&#10; while &#40;this.IsLocked &#38;&#38; &#33;spin.NextSpinWillYield&#41;&#10; &#123;&#10; spin.SpinOnce&#40;&#41;&#59;&#10; &#125;&#10;&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; while &#40;this.IsLocked&#41;&#10; &#123;&#10; &#47;&#47; If cancelled, we throw. Trying to wait could lead to deadlock.&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; using &#40;Logger.LogBlock&#40;FeatureId.WaitIndicator, FunctionId.Utilities_NonReentrantLock_BlockingWait, cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; Another thread holds the lock. Wait until we get awoken either&#10; &#47;&#47; by some code calling &#34;Release&#34; or by cancellation.&#10; Monitor.Wait&#40;this.syncLock&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; We now hold the lock&#10; this.TakeOwnership&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Exit the mutual exclusion.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; The calling thread must currently hold the lock.&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;Roslyn.Utilities.ContractFailureException&#34;&#62;The lock is not currently held by the calling thread.&#60;&#47;exception&#62;&#10; public void Release&#40;&#41;&#10; &#123;&#10; AssertHasLock&#40;&#41;&#59;&#10;&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; this.ReleaseOwnership&#40;&#41;&#59;&#10;&#10; &#47;&#47; Release one waiter&#10; Monitor.Pulse&#40;this.syncLock&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Determine if the lock is currently held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;returns&#62;True if the lock is currently held by the calling thread.&#60;&#47;returns&#62;&#10; public bool LockHeldByMe&#40;&#41;&#10; &#123;&#10; return this.IsOwnedByMe&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Throw an exception if the lock is not held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;Roslyn.Utilities.ContractFailureException&#34;&#62;The lock is not currently held by the calling thread.&#60;&#47;exception&#62;&#10; public void AssertHasLock&#40;&#41;&#10; &#123;&#10; Contract.ThrowIfFalse&#40;LockHeldByMe&#40;&#41;&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Checks if the lock is currently held.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool IsLocked&#10; &#123;&#10; get&#10; &#123;&#10; return this.owningThread &#33;&#61; null&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Checks if the lock is currently held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool IsOwnedByMe&#10; &#123;&#10; get&#10; &#123;&#10; return this.owningThread &#61;&#61; Thread.CurrentThread&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Take ownership of the lock &#40;by the calling thread&#41;. The lock may not already&#10; &#47;&#47;&#47; be held by any other code.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private void TakeOwnership&#40;&#41;&#10; &#123;&#10; Contract.Assert&#40;&#33;this.IsLocked&#41;&#59;&#10; this.owningThread &#61; Thread.CurrentThread&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Release ownership of the lock. The lock must already be held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private void ReleaseOwnership&#40;&#41;&#10; &#123;&#10; Contract.Assert&#40;this.IsOwnedByMe&#41;&#59;&#10; this.owningThread &#61; null&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Action object passed to a cancellation token registration.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private static readonly Action&#60;object&#62; cancellationTokenCanceledEventHandler &#61; new Action&#60;object&#62;&#40;CancellationTokenCanceledEventHandler&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Callback executed when a cancellation token is canceled during a Wait.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;obj&#34;&#62;The syncLock that protects a &#60;see cref&#61;&#34;NonReentrantLock &#34;&#47;&#62; instance.&#60;&#47;param&#62;&#10; private static void CancellationTokenCanceledEventHandler&#40;object obj&#41;&#10; &#123;&#10; lock &#40;obj&#41;&#10; &#123;&#10; &#47;&#47; Release all waiters to check their cancellation tokens.&#10; Monitor.PulseAll&#40;obj&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#125;</pre>2013-05-23T06:05:29-07:00Z1http://rise4fun.com/Seal/Z1Seal/Z1<pre>&#10;using System&#59;&#10;using System.Collections.Generic&#59;&#10;using System.Runtime.CompilerServices&#59;&#10;using System.Threading&#59;&#10;using System.Threading.Tasks&#59;&#10;&#10;namespace Roslyn.Utilities&#10;&#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Represents a value that can be retrieved synchronously or asynchronously by many clients.&#10; &#47;&#47;&#47; The value will be computed on-demand the moment the first client asks for it. While being&#10; &#47;&#47;&#47; computed, more clients can request the value. As long as there are outstanding clients the&#10; &#47;&#47;&#47; underlying computation will proceed. If all outstanding clients cancel their request then&#10; &#47;&#47;&#47; the underlying value computation will be cancelled as well.&#10; &#47;&#47;&#47; &#10; &#47;&#47;&#47; Creators of an &#60;see cref&#61;&#34;AsyncLazy&#123;T&#125;&#34; &#47;&#62; can specify whether the result of the computation is&#10; &#47;&#47;&#47; cached for future requests or not. Choosing to not cache means the computation functions are kept&#10; &#47;&#47;&#47; alive, whereas caching means the value &#40;but not functions&#41; are kept alive once complete.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; internal sealed class AsyncLazy&#60;T&#62;&#10; &#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The underlying function that starts an asynchronous computation of the resulting value.&#10; &#47;&#47;&#47; Null&#39;ed out once we&#39;ve computed the result and we&#39;ve been asked to cache it. Otherwise,&#10; &#47;&#47;&#47; it is kept around in case the value needs to be computed again.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The underlying function that starts an synchronous computation of the resulting value.&#10; &#47;&#47;&#47; Null&#39;ed out once we&#39;ve computed the result and we&#39;ve been asked to cache it, or if we&#10; &#47;&#47;&#47; didn&#39;t get any synchronous function given to us in the first place.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Func&#60;CancellationToken, T&#62; synchronousComputeFunction&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Whether or not we should keep the value around once we&#39;ve computed it.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private readonly bool cacheResult&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The Task that holds the cached result.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Task&#60;T&#62; cachedResult&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Mutex used to protect reading and writing to all mutable objects and fields. Traces&#10; &#47;&#47;&#47; indicate that there&#39;s negligible contention on this lock, hence we can save some memory&#10; &#47;&#47;&#47; by using a single lock for all AsyncLazy instances. Only trivial and non-reentrant work&#10; &#47;&#47;&#47; should be done while holding the lock.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private static readonly NonReentrantLock gate &#61; new NonReentrantLock&#40;useThisInstanceForSynchronization&#58; true&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The hash set of all currently outstanding asynchronous requests. Null if there are no requests,&#10; &#47;&#47;&#47; and will never be empty.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private HashSet&#60;Request&#62; requests &#61; null&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; If an asynchronous request is active, the CancellationTokenSource that allows for&#10; &#47;&#47;&#47; cancelling the underlying computation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private CancellationTokenSource asynchronousComputationCancellationSource &#61; null&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Whether a computation is active or queued on any thread, whether synchronous or&#10; &#47;&#47;&#47; asynchronous.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool computationActive &#61; false&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Creates an AsyncLazy that always returns the value, analogous to &#60;see cref&#61;&#34;Task.FromResult&#123;T&#125;&#34; &#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public AsyncLazy&#40;T value&#41;&#10; &#123;&#10; this.cacheResult &#61; true&#59;&#10; this.cachedResult &#61; Task.FromResult&#40;value&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Important&#58; callers of this constructor should ensure that the compute function returns&#10; &#47;&#47;&#47; a task in a non-blocking fashion. i.e. the function should &#42;not&#42; synchronously compute&#10; &#47;&#47;&#47; a value and then return it using Task.FromResult. Instead, it should return an actual&#10; &#47;&#47;&#47; task that operates asynchronously. If this function synchronously computes a value&#10; &#47;&#47;&#47; then that will cause locks to be held in this type for excessive periods of time.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public AsyncLazy&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, bool cacheResult&#41;&#10; &#58; this&#40;asynchronousComputeFunction, synchronousComputeFunction&#58; null, cacheResult&#58; cacheResult&#41;&#10; &#123;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Creates an AsyncLazy that supports both asynchronous computation and inline synchronous&#10; &#47;&#47;&#47; computation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;asynchronousComputeFunction&#34;&#62;A function called to start the asynchronous&#10; &#47;&#47;&#47; computation. This function should be cheap and non-blocking.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;synchronousComputeFunction&#34;&#62;A function to do the work synchronously, which&#10; &#47;&#47;&#47; is allowed to block. This function should not be implemented by a simple Wait on the&#10; &#47;&#47;&#47; asynchronous value. If that&#39;s all you are doing, just don&#39;t pass a synchronous function&#10; &#47;&#47;&#47; in the first place.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;cacheResult&#34;&#62;Whether the result should be cached once the computation is&#10; &#47;&#47;&#47; complete.&#60;&#47;param&#62;&#10; public AsyncLazy&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, Func&#60;CancellationToken, T&#62; synchronousComputeFunction, bool cacheResult&#41;&#10; &#123;&#10; Contract.ThrowIfNull&#40;asynchronousComputeFunction&#41;&#59;&#10;&#10; this.asynchronousComputeFunction &#61; asynchronousComputeFunction&#59;&#10; this.synchronousComputeFunction &#61; synchronousComputeFunction&#59;&#10; this.cacheResult &#61; cacheResult&#59;&#10; &#125;&#10;&#10; &#35;region Lock Wrapper for Invariant Checking&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Takes the lock for this object and if acquired validates the invariants of this class.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private WaitThatValidatesInvariants TakeLock&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; gate.Wait&#40;cancellationToken&#41;&#59;&#10; AssertInvariants_NoLock&#40;&#41;&#59;&#10; return new WaitThatValidatesInvariants&#40;this&#41;&#59;&#10; &#125;&#10;&#10; private struct WaitThatValidatesInvariants &#58; IDisposable&#10; &#123;&#10; private readonly AsyncLazy&#60;T&#62; asyncLazy&#59;&#10;&#10; public WaitThatValidatesInvariants&#40;AsyncLazy&#60;T&#62; asyncLazy&#41;&#10; &#123;&#10; this.asyncLazy &#61; asyncLazy&#59;&#10; &#125;&#10;&#10; public void Dispose&#40;&#41;&#10; &#123;&#10; asyncLazy.AssertInvariants_NoLock&#40;&#41;&#59;&#10; gate.Release&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void AssertInvariants_NoLock&#40;&#41;&#10; &#123;&#10; &#47;&#47; Invariant &#35;1&#58; thou shalt never have an asynchronous computation running without it&#10; &#47;&#47; being considered a computation&#10; Contract.ThrowIfTrue&#40;this.asynchronousComputationCancellationSource &#33;&#61; null &#38;&#38;&#10; &#33;this.computationActive&#41;&#59;&#10;&#10; &#47;&#47; Invariant &#35;2&#58; thou shalt never waste memory holding onto empty HashSets&#10; Contract.ThrowIfTrue&#40;this.requests &#33;&#61; null &#38;&#38;&#10; this.requests.Count &#61;&#61; 0&#41;&#59;&#10;&#10; &#47;&#47; Invariant &#35;3&#58; thou shalt never have an request if there is not&#10; &#47;&#47; something trying to compute it&#10; Contract.ThrowIfTrue&#40;this.requests &#33;&#61; null &#38;&#38;&#10; &#33;this.computationActive&#41;&#59;&#10;&#10; &#47;&#47; Invariant &#35;4&#58; thou shalt never have a cached value and any computation function&#10; Contract.ThrowIfTrue&#40;this.cachedResult &#33;&#61; null &#38;&#38;&#10; &#40;this.synchronousComputeFunction &#33;&#61; null &#124;&#124; this.asynchronousComputeFunction &#33;&#61; null&#41;&#41;&#59;&#10;&#10; &#47;&#47; Invariant &#35;5&#58; thou shalt never have a synchronous computation function but not an&#10; &#47;&#47; asynchronous one&#10; Contract.ThrowIfTrue&#40;this.asynchronousComputeFunction &#61;&#61; null &#38;&#38; this.synchronousComputeFunction &#33;&#61; null&#41;&#59;&#10; &#125;&#10;&#10; &#35;endregion&#10;&#10; public bool TryGetValue&#40;out T result&#41;&#10; &#123;&#10; &#47;&#47; No need to lock here since this is only a fast check to &#10; &#47;&#47; see if the result is already computed.&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; result &#61; cachedResult.Result&#59;&#10; return true&#59;&#10; &#125;&#10;&#10; result &#61; default&#40;T&#41;&#59;&#10; return false&#59;&#10; &#125;&#10;&#10; public T GetValue&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; Request request &#61; null&#59;&#10; AsynchronousComputationToStart&#63; newAsynchronousComputation &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If cached, get immediately&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return cachedResult.Result&#59;&#10; &#125;&#10;&#10; &#47;&#47; If there is an existing computation active, we&#39;ll just create another request&#10; if &#40;computationActive&#41;&#10; &#123;&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10; &#125;&#10; else if &#40;synchronousComputeFunction &#61;&#61; null&#41;&#10; &#123;&#10; &#47;&#47; A synchronous request, but we have no synchronous function. Start off the async work&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10;&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; else&#10; &#123;&#10; &#47;&#47; We will do the computation here&#10; this.computationActive &#61; true&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; If we simply created a new asynchronous request, so wait for it. Yes, we&#39;re blocking the thread&#10; &#47;&#47; but we don&#39;t want multiple threads attempting to compute the same thing.&#10; if &#40;request &#33;&#61; null&#41;&#10; &#123;&#10; request.RegisterForCancellation&#40;OnAsynchronousRequestCancelled, cancellationToken&#41;&#59;&#10;&#10; &#47;&#47; Since we already registered for cancellation, it&#39;s possible that the registration has&#10; &#47;&#47; cancelled this new computation if we were the only requestor.&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; request&#41;&#59;&#10; &#125;&#10;&#10; return request.Task.WaitAndGetResult&#40;cancellationToken&#41;&#59;&#10; &#125;&#10; else&#10; &#123;&#10; T result&#59;&#10;&#10; &#47;&#47; We are the active computation, so let&#39;s go ahead and compute.&#10; try&#10; &#123;&#10; result &#61; synchronousComputeFunction&#40;cancellationToken&#41;&#59;&#10; &#125;&#10; catch &#40;OperationCanceledException&#41;&#10; &#123;&#10; &#47;&#47; This cancelled for some reason. We don&#39;t care why, but&#10; &#47;&#47; it means anybody else waiting for this result isn&#39;t going to get it&#10; &#47;&#47; from us.&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; this.computationActive &#61; false&#59;&#10;&#10; if &#40;requests &#33;&#61; null&#41;&#10; &#123;&#10; &#47;&#47; There&#39;s a possible improvement here&#58; there might be another synchronous caller who&#10; &#47;&#47; also wants the value. We might consider stealing their thread rather than punting&#10; &#47;&#47; to the thread pool.&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; null&#41;&#59;&#10; &#125;&#10;&#10; throw&#59;&#10; &#125;&#10; catch &#40;Exception ex&#41;&#10; &#123;&#10; &#47;&#47; We faulted for some unknown reason. We should simply fault everything.&#10; TaskCompletionSource&#60;T&#62; tcs &#61; new TaskCompletionSource&#60;T&#62;&#40;&#41;&#59;&#10; tcs.SetException&#40;ex&#41;&#59;&#10; CompleteWithTask&#40;tcs.Task, CancellationToken.None&#41;&#59;&#10;&#10; throw&#59;&#10; &#125;&#10;&#10; &#47;&#47; We have a value, so complete&#10; CompleteWithTask&#40;Task.FromResult&#40;result&#41;, CancellationToken.None&#41;&#59;&#10;&#10; return result&#59;&#10; &#125;&#10; &#125;&#10;&#10; private Request CreateNewRequest_NoLock&#40;&#41;&#10; &#123;&#10; if &#40;this.requests &#61;&#61; null&#41;&#10; &#123;&#10; this.requests &#61; new HashSet&#60;Request&#62;&#40;&#41;&#59;&#10; &#125;&#10;&#10; Request request &#61; new Request&#40;&#41;&#59;&#10; this.requests.Add&#40;request&#41;&#59;&#10; return request&#59;&#10; &#125;&#10;&#10; public Task&#60;T&#62; GetValueAsync&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; &#47;&#47; Optimization&#58; if we&#39;re already cancelled, do not pass go&#10; if &#40;cancellationToken.IsCancellationRequested&#41;&#10; &#123;&#10; return new Task&#60;T&#62;&#40;&#40;&#41; &#61;&#62; default&#40;T&#41;, cancellationToken&#41;&#59;&#10; &#125;&#10;&#10; Request request&#59;&#10; AsynchronousComputationToStart&#63; newAsynchronousComputation &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If cached, get immediately&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return cachedResult&#59;&#10; &#125;&#10;&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10;&#10; &#47;&#47; If we have either synchronous or asynchronous work current in flight, we don&#39;t need to do anything.&#10; &#47;&#47; Otherwise, we shall start an asynchronous computation for this&#10; if &#40;&#33;computationActive&#41;&#10; &#123;&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; We now have the request counted for, register for cancellation. It is critical this is&#10; &#47;&#47; done outside the lock, as our reigstration may immediately fire and we want to avoid the&#10; &#47;&#47; reentrancy&#10; request.RegisterForCancellation&#40;OnAsynchronousRequestCancelled, cancellationToken&#41;&#59;&#10;&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; request&#41;&#59;&#10; &#125;&#10;&#10; return request.Task&#59;&#10; &#125;&#10;&#10; private AsynchronousComputationToStart RegisterAsynchronousComputation_NoLock&#40;&#41;&#10; &#123;&#10; Contract.ThrowIfTrue&#40;this.computationActive&#41;&#59;&#10;&#10; this.asynchronousComputationCancellationSource &#61; new CancellationTokenSource&#40;&#41;&#59;&#10; this.computationActive &#61; true&#59;&#10;&#10; return new AsynchronousComputationToStart&#40;this.asynchronousComputeFunction, this.asynchronousComputationCancellationSource&#41;&#59;&#10; &#125;&#10;&#10; private struct AsynchronousComputationToStart&#10; &#123;&#10; public readonly Func&#60;CancellationToken, Task&#60;T&#62;&#62; AsynchronousComputeFunction&#59;&#10; public readonly CancellationTokenSource CancellationTokenSource&#59;&#10;&#10; public AsynchronousComputationToStart&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, CancellationTokenSource cancellationTokenSource&#41;&#10; &#123;&#10; this.AsynchronousComputeFunction &#61; asynchronousComputeFunction&#59;&#10; this.CancellationTokenSource &#61; cancellationTokenSource&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void StartAsynchronousComputation&#40;AsynchronousComputationToStart computationToStart, Request requestToCompleteSynchronously&#41;&#10; &#123;&#10; var cancellationToken &#61; computationToStart.CancellationTokenSource.Token&#59;&#10;&#10; try&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; DO NOT ACCESS ANY FIELDS OR STATE BEYOND THIS POINT. Since this function&#10; &#47;&#47; runs unsynchronized, it&#39;s possible that during this function this request&#10; &#47;&#47; might be cancelled, and then a whole additional request might start and&#10; &#47;&#47; complete inline, and cache the result. By grabbing state before we check&#10; &#47;&#47; the cancellation token, we can be assured that we are only operating on&#10; &#47;&#47; a state that was complete.&#10;&#10; ExceptionUtilities.ExecuteWithErrorReporting&#40;&#40;&#41; &#61;&#62;&#10; &#123;&#10; &#47;&#47; We avoid creating a full closure just to pass the token along&#10; &#47;&#47; Also, use TaskContinuationOptions.ExecuteSynchronously so that we inline &#10; &#47;&#47; the continuation if asynchronousComputeFunction completes synchronously&#10; var task &#61; computationToStart.AsynchronousComputeFunction&#40;cancellationToken&#41;&#59;&#10;&#10; task.ContinueWith&#40;&#10; &#40;t, s&#41; &#61;&#62; CompleteWithTask&#40;t, &#40;&#40;CancellationTokenSource&#41;s&#41;.Token&#41;,&#10; computationToStart.CancellationTokenSource,&#10; cancellationToken,&#10; TaskContinuationOptions.ExecuteSynchronously,&#10; TaskScheduler.Default&#41;&#59;&#10;&#10; if &#40;requestToCompleteSynchronously &#33;&#61; null &#38;&#38; task.IsCompleted&#41;&#10; &#123;&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; task &#61; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;task&#41;&#59;&#10; &#125;&#10;&#10; requestToCompleteSynchronously.CompleteFromTaskSynchronously&#40;task&#41;&#59;&#10; &#125;&#10; &#125;&#41;&#59;&#10; &#125;&#10; catch &#40;OperationCanceledException oce&#41;&#10; &#123;&#10; &#47;&#47; As long as it&#39;s the right token, this means that our thread was the first thread&#10; &#47;&#47; to start an asynchronous computation, but the requestor cancelled as we were starting up&#10; &#47;&#47; the computation.&#10; if &#40;oce.CancellationToken &#33;&#61; cancellationToken&#41;&#10; &#123;&#10; ExceptionUtilities.FailFast&#40;oce&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; private void CompleteWithTask&#40;Task&#60;T&#62; task, CancellationToken cancellationToken&#41;&#10; &#123;&#10; IEnumerable&#60;Request&#62; requestsToComplete&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If the underlying computation was cancelled, then all state was already updated in OnAsynchronousRequestCancelled&#10; &#47;&#47; and there is no new work to do here. We &#42;must&#42; use the local one since this completion may be running far after&#10; &#47;&#47; the background computation was cancelled and a new one might have already been enqueued. We must do this&#10; &#47;&#47; check here under the lock to ensure proper synchronization with OnAsynchronousRequestCancelled.&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; The computation is complete, so get all requests to complete and null out the list. We&#39;ll create another one&#10; &#47;&#47; later if it&#39;s needed&#10; requestsToComplete &#61; this.requests &#63;&#63; SpecializedCollections.EmptyEnumerable&#60;Request&#62;&#40;&#41;&#59;&#10; this.requests &#61; null&#59;&#10;&#10; &#47;&#47; The computations are done&#10; this.asynchronousComputationCancellationSource &#61; null&#59;&#10; this.computationActive &#61; false&#59;&#10; task &#61; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;task&#41;&#59;&#10; &#125;&#10;&#10; foreach &#40;var requestToComplete in requestsToComplete&#41;&#10; &#123;&#10; requestToComplete.CompleteFromTaskAsynchronously&#40;task&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; private Task&#60;T&#62; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; if &#40;this.cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return this.cachedResult&#59;&#10; &#125;&#10; else&#10; &#123;&#10; if &#40;cacheResult &#38;&#38; task.Status &#61;&#61; TaskStatus.RanToCompletion&#41;&#10; &#123;&#10; &#47;&#47; Hold onto the completed task. We can get rid of the computation functions for good&#10; this.cachedResult &#61; task&#59;&#10; this.asynchronousComputeFunction &#61; null&#59;&#10; this.synchronousComputeFunction &#61; null&#59;&#10; &#125;&#10;&#10; return task&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void OnAsynchronousRequestCancelled&#40;object state&#41;&#10; &#123;&#10; var request &#61; &#40;Request&#41;state&#59;&#10; CancellationTokenSource cancellationTokenSource &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; &#47;&#47; Now try to remove it. It&#39;s possible that requests may already be null. You could&#10; &#47;&#47; imagine that cancellation was requested, but before we could aquire the lock&#10; &#47;&#47; here the computation completed and the entire CompleteWithTask synchronized&#10; &#47;&#47; block ran. In that case, the requests collection may already be null, or it&#10; &#47;&#47; &#40;even scarier&#33;&#41; may have been replaced with another collection because another&#10; &#47;&#47; computation has started.&#10; if &#40;this.requests &#33;&#61; null&#41;&#10; &#123;&#10; if &#40;this.requests.Remove&#40;request&#41;&#41;&#10; &#123;&#10; if &#40;this.requests.Count &#61;&#61; 0&#41;&#10; &#123;&#10; this.requests &#61; null&#59;&#10;&#10; if &#40;this.asynchronousComputationCancellationSource &#33;&#61; null&#41;&#10; &#123;&#10; cancellationTokenSource &#61; this.asynchronousComputationCancellationSource&#59;&#10; this.asynchronousComputationCancellationSource &#61; null&#59;&#10; this.computationActive &#61; false&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; request.CancelAsynchronously&#40;&#41;&#59;&#10;&#10; if &#40;cancellationTokenSource &#33;&#61; null&#41;&#10; &#123;&#10; cancellationTokenSource.Cancel&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; Using inheritance instead of wrapping a TaskCompletionSource to avoid a second allocation&#10; private class Request &#58; TaskCompletionSource&#60;T&#62;&#10; &#123;&#10; private CancellationTokenRegistration cancellationTokenRegistration&#59;&#10;&#10; public void RegisterForCancellation&#40;Action&#60;object&#62; callback, CancellationToken cancellationToken&#41;&#10; &#123;&#10; cancellationTokenRegistration &#61; cancellationToken.Register&#40;callback, this&#41;&#59;&#10; &#125;&#10;&#10; public void CompleteFromTaskAsynchronously&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; System.Threading.Tasks.Task.Factory.StartNew&#40;CompleteFromTaskSynchronouslyStub, task, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default&#41;&#59;&#10; &#125;&#10;&#10; private void CompleteFromTaskSynchronouslyStub&#40;object task&#41;&#10; &#123;&#10; CompleteFromTaskSynchronously&#40;&#40;Task&#60;T&#62;&#41;task&#41;&#59;&#10; &#125;&#10;&#10; public void CompleteFromTaskSynchronously&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; if &#40;task.Status &#61;&#61; TaskStatus.RanToCompletion&#41;&#10; &#123;&#10; if &#40;TrySetResult&#40;task.Result&#41;&#41;&#10; &#123;&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; else if &#40;task.Status &#61;&#61; TaskStatus.Faulted&#41;&#10; &#123;&#10; if &#40;TrySetException&#40;task.Exception&#41;&#41;&#10; &#123;&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; else&#10; &#123;&#10; CancelSynchronously&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; public void CancelAsynchronously&#40;&#41;&#10; &#123;&#10; &#47;&#47; Since there could be synchronous continuations on the TaskCancellationSource, we queue this to the threadpool&#10; &#47;&#47; to avoid inline running of other operations.&#10; System.Threading.Tasks.Task.Factory.StartNew&#40;CancelSynchronously, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default&#41;&#59;&#10; &#125;&#10;&#10; private void CancelSynchronously&#40;&#41;&#10; &#123;&#10; if &#40;TrySetCanceled&#40;&#41;&#41;&#10; &#123;&#10; &#47;&#47; Paranoia&#58; the only reason we should ever get here is if the CancellationToken that&#10; &#47;&#47; we registered against was cancelled, but just in case, dispose the registration&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#125;</pre>2013-05-23T06:05:29-07:00YWthttp://rise4fun.com/Seal/YWtSeal/YWt<pre>using System&#59;&#10;using System.Collections.Generic&#59;&#10;using System.Runtime.CompilerServices&#59;&#10;using System.Threading&#59;&#10;using System.Threading.Tasks&#59;&#10;using Microsoft.CodeAnalysis&#59;&#10;using Microsoft.CodeAnalysis.Text&#59;&#10;&#10;namespace Roslyn.Utilities&#10;&#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Represents a value that can be retrieved synchronously or asynchronously by many clients.&#10; &#47;&#47;&#47; The value will be computed on-demand the moment the first client asks for it. While being&#10; &#47;&#47;&#47; computed, more clients can request the value. As long as there are outstanding clients the&#10; &#47;&#47;&#47; underlying computation will proceed. If all outstanding clients cancel their request then&#10; &#47;&#47;&#47; the underlying value computation will be cancelled as well.&#10; &#47;&#47;&#47; &#10; &#47;&#47;&#47; Creators of an &#60;see cref&#61;&#34;AsyncLazy&#123;T&#125;&#34; &#47;&#62; can specify whether the result of the computation is&#10; &#47;&#47;&#47; cached for future requests or not. Choosing to not cache means the computation functions are kept&#10; &#47;&#47;&#47; alive, whereas caching means the value &#40;but not functions&#41; are kept alive once complete.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; internal sealed class AsyncLazy&#60;T&#62;&#10; &#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The underlying function that starts an asynchronous computation of the resulting value.&#10; &#47;&#47;&#47; Null&#39;ed out once we&#39;ve computed the result and we&#39;ve been asked to cache it. Otherwise,&#10; &#47;&#47;&#47; it is kept around in case the value needs to be computed again.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The underlying function that starts an synchronous computation of the resulting value.&#10; &#47;&#47;&#47; Null&#39;ed out once we&#39;ve computed the result and we&#39;ve been asked to cache it, or if we&#10; &#47;&#47;&#47; didn&#39;t get any synchronous function given to us in the first place.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Func&#60;CancellationToken, T&#62; synchronousComputeFunction&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Whether or not we should keep the value around once we&#39;ve computed it.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private readonly bool cacheResult&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The Task that holds the cached result.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Task&#60;T&#62; cachedResult&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Mutex used to protect reading and writing to all mutable objects and fields. Traces&#10; &#47;&#47;&#47; indicate that there&#39;s negligible contention on this lock, hence we can save some memory&#10; &#47;&#47;&#47; by using a single lock for all AsyncLazy instances. Only trivial and non-reentrant work&#10; &#47;&#47;&#47; should be done while holding the lock.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private static readonly NonReentrantLock gate &#61; new NonReentrantLock&#40;useThisInstanceForSynchronization&#58; true&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The hash set of all currently outstanding asynchronous requests. Null if there are no requests,&#10; &#47;&#47;&#47; and will never be empty.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private HashSet&#60;Request&#62; requests &#61; null&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; If an asynchronous request is active, the CancellationTokenSource that allows for&#10; &#47;&#47;&#47; cancelling the underlying computation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private CancellationTokenSource asynchronousComputationCancellationSource &#61; null&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Whether a computation is active or queued on any thread, whether synchronous or&#10; &#47;&#47;&#47; asynchronous.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool computationActive &#61; false&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Creates an AsyncLazy that always returns the value, analogous to &#60;see cref&#61;&#34;Task.FromResult&#123;T&#125;&#34; &#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public AsyncLazy&#40;T value&#41;&#10; &#123;&#10; this.cacheResult &#61; true&#59;&#10; this.cachedResult &#61; Task.FromResult&#40;value&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Important&#58; callers of this constructor should ensure that the compute function returns&#10; &#47;&#47;&#47; a task in a non-blocking fashion. i.e. the function should &#42;not&#42; synchronously compute&#10; &#47;&#47;&#47; a value and then return it using Task.FromResult. Instead, it should return an actual&#10; &#47;&#47;&#47; task that operates asynchronously. If this function synchronously computes a value&#10; &#47;&#47;&#47; then that will cause locks to be held in this type for excessive periods of time.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public AsyncLazy&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, bool cacheResult&#41;&#10; &#58; this&#40;asynchronousComputeFunction, synchronousComputeFunction&#58; null, cacheResult&#58; cacheResult&#41;&#10; &#123;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Creates an AsyncLazy that supports both asynchronous computation and inline synchronous&#10; &#47;&#47;&#47; computation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;asynchronousComputeFunction&#34;&#62;A function called to start the asynchronous&#10; &#47;&#47;&#47; computation. This function should be cheap and non-blocking.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;synchronousComputeFunction&#34;&#62;A function to do the work synchronously, which&#10; &#47;&#47;&#47; is allowed to block. This function should not be implemented by a simple Wait on the&#10; &#47;&#47;&#47; asynchronous value. If that&#39;s all you are doing, just don&#39;t pass a synchronous function&#10; &#47;&#47;&#47; in the first place.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;cacheResult&#34;&#62;Whether the result should be cached once the computation is&#10; &#47;&#47;&#47; complete.&#60;&#47;param&#62;&#10; public AsyncLazy&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, Func&#60;CancellationToken, T&#62; synchronousComputeFunction, bool cacheResult&#41;&#10; &#123;&#10; Contract.ThrowIfNull&#40;asynchronousComputeFunction&#41;&#59;&#10;&#10; this.asynchronousComputeFunction &#61; asynchronousComputeFunction&#59;&#10; this.synchronousComputeFunction &#61; synchronousComputeFunction&#59;&#10; this.cacheResult &#61; cacheResult&#59;&#10; &#125;&#10;&#10; &#35;region Lock Wrapper for Invariant Checking&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Takes the lock for this object and if acquired validates the invariants of this class.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private WaitThatValidatesInvariants TakeLock&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; gate.Wait&#40;cancellationToken&#41;&#59;&#10; AssertInvariants_NoLock&#40;&#41;&#59;&#10; return new WaitThatValidatesInvariants&#40;this&#41;&#59;&#10; &#125;&#10;&#10; private struct WaitThatValidatesInvariants &#58; IDisposable&#10; &#123;&#10; private readonly AsyncLazy&#60;T&#62; asyncLazy&#59;&#10;&#10; public WaitThatValidatesInvariants&#40;AsyncLazy&#60;T&#62; asyncLazy&#41;&#10; &#123;&#10; this.asyncLazy &#61; asyncLazy&#59;&#10; &#125;&#10;&#10; public void Dispose&#40;&#41;&#10; &#123;&#10; asyncLazy.AssertInvariants_NoLock&#40;&#41;&#59;&#10; gate.Release&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void AssertInvariants_NoLock&#40;&#41;&#10; &#123;&#10; &#47;&#47; Invariant &#35;1&#58; thou shalt never have an asynchronous computation running without it&#10; &#47;&#47; being considered a computation&#10; Contract.ThrowIfTrue&#40;this.asynchronousComputationCancellationSource &#33;&#61; null &#38;&#38;&#10; &#33;this.computationActive&#41;&#59;&#10;&#10; &#47;&#47; Invariant &#35;2&#58; thou shalt never waste memory holding onto empty HashSets&#10; Contract.ThrowIfTrue&#40;this.requests &#33;&#61; null &#38;&#38;&#10; this.requests.Count &#61;&#61; 0&#41;&#59;&#10;&#10; &#47;&#47; Invariant &#35;3&#58; thou shalt never have an request if there is not&#10; &#47;&#47; something trying to compute it&#10; Contract.ThrowIfTrue&#40;this.requests &#33;&#61; null &#38;&#38;&#10; &#33;this.computationActive&#41;&#59;&#10;&#10; &#47;&#47; Invariant &#35;4&#58; thou shalt never have a cached value and any computation function&#10; Contract.ThrowIfTrue&#40;this.cachedResult &#33;&#61; null &#38;&#38;&#10; &#40;this.synchronousComputeFunction &#33;&#61; null &#124;&#124; this.asynchronousComputeFunction &#33;&#61; null&#41;&#41;&#59;&#10;&#10; &#47;&#47; Invariant &#35;5&#58; thou shalt never have a synchronous computation function but not an&#10; &#47;&#47; asynchronous one&#10; Contract.ThrowIfTrue&#40;this.asynchronousComputeFunction &#61;&#61; null &#38;&#38; this.synchronousComputeFunction &#33;&#61; null&#41;&#59;&#10; &#125;&#10;&#10; &#35;endregion&#10;&#10; public bool TryGetValue&#40;out T result&#41;&#10; &#123;&#10; &#47;&#47; No need to lock here since this is only a fast check to &#10; &#47;&#47; see if the result is already computed.&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; result &#61; cachedResult.Result&#59;&#10; return true&#59;&#10; &#125;&#10;&#10; result &#61; default&#40;T&#41;&#59;&#10; return false&#59;&#10; &#125;&#10;&#10; public T GetValue&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; Request request &#61; null&#59;&#10; AsynchronousComputationToStart&#63; newAsynchronousComputation &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If cached, get immediately&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return cachedResult.Result&#59;&#10; &#125;&#10;&#10; &#47;&#47; If there is an existing computation active, we&#39;ll just create another request&#10; if &#40;computationActive&#41;&#10; &#123;&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10; &#125;&#10; else if &#40;synchronousComputeFunction &#61;&#61; null&#41;&#10; &#123;&#10; &#47;&#47; A synchronous request, but we have no synchronous function. Start off the async work&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10;&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; else&#10; &#123;&#10; &#47;&#47; We will do the computation here&#10; this.computationActive &#61; true&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; If we simply created a new asynchronous request, so wait for it. Yes, we&#39;re blocking the thread&#10; &#47;&#47; but we don&#39;t want multiple threads attempting to compute the same thing.&#10; if &#40;request &#33;&#61; null&#41;&#10; &#123;&#10; request.RegisterForCancellation&#40;OnAsynchronousRequestCancelled, cancellationToken&#41;&#59;&#10;&#10; &#47;&#47; Since we already registered for cancellation, it&#39;s possible that the registration has&#10; &#47;&#47; cancelled this new computation if we were the only requestor.&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; request&#41;&#59;&#10; &#125;&#10;&#10; return request.Task.WaitAndGetResult&#40;cancellationToken&#41;&#59;&#10; &#125;&#10; else&#10; &#123;&#10; T result&#59;&#10;&#10; &#47;&#47; We are the active computation, so let&#39;s go ahead and compute.&#10; try&#10; &#123;&#10; result &#61; synchronousComputeFunction&#40;cancellationToken&#41;&#59;&#10; &#125;&#10; catch &#40;OperationCanceledException&#41;&#10; &#123;&#10; &#47;&#47; This cancelled for some reason. We don&#39;t care why, but&#10; &#47;&#47; it means anybody else waiting for this result isn&#39;t going to get it&#10; &#47;&#47; from us.&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; this.computationActive &#61; false&#59;&#10;&#10; if &#40;requests &#33;&#61; null&#41;&#10; &#123;&#10; &#47;&#47; There&#39;s a possible improvement here&#58; there might be another synchronous caller who&#10; &#47;&#47; also wants the value. We might consider stealing their thread rather than punting&#10; &#47;&#47; to the thread pool.&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; null&#41;&#59;&#10; &#125;&#10;&#10; throw&#59;&#10; &#125;&#10; catch &#40;Exception ex&#41;&#10; &#123;&#10; &#47;&#47; We faulted for some unknown reason. We should simply fault everything.&#10; TaskCompletionSource&#60;T&#62; tcs &#61; new TaskCompletionSource&#60;T&#62;&#40;&#41;&#59;&#10; tcs.SetException&#40;ex&#41;&#59;&#10; CompleteWithTask&#40;tcs.Task, CancellationToken.None&#41;&#59;&#10;&#10; throw&#59;&#10; &#125;&#10;&#10; &#47;&#47; We have a value, so complete&#10; CompleteWithTask&#40;Task.FromResult&#40;result&#41;, CancellationToken.None&#41;&#59;&#10;&#10; return result&#59;&#10; &#125;&#10; &#125;&#10;&#10; private Request CreateNewRequest_NoLock&#40;&#41;&#10; &#123;&#10; if &#40;this.requests &#61;&#61; null&#41;&#10; &#123;&#10; this.requests &#61; new HashSet&#60;Request&#62;&#40;&#41;&#59;&#10; &#125;&#10;&#10; Request request &#61; new Request&#40;&#41;&#59;&#10; this.requests.Add&#40;request&#41;&#59;&#10; return request&#59;&#10; &#125;&#10;&#10; public Task&#60;T&#62; GetValueAsync&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; &#47;&#47; Optimization&#58; if we&#39;re already cancelled, do not pass go&#10; if &#40;cancellationToken.IsCancellationRequested&#41;&#10; &#123;&#10; return new Task&#60;T&#62;&#40;&#40;&#41; &#61;&#62; default&#40;T&#41;, cancellationToken&#41;&#59;&#10; &#125;&#10;&#10; Request request&#59;&#10; AsynchronousComputationToStart&#63; newAsynchronousComputation &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If cached, get immediately&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return cachedResult&#59;&#10; &#125;&#10;&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10;&#10; &#47;&#47; If we have either synchronous or asynchronous work current in flight, we don&#39;t need to do anything.&#10; &#47;&#47; Otherwise, we shall start an asynchronous computation for this&#10; if &#40;&#33;computationActive&#41;&#10; &#123;&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; We now have the request counted for, register for cancellation. It is critical this is&#10; &#47;&#47; done outside the lock, as our reigstration may immediately fire and we want to avoid the&#10; &#47;&#47; reentrancy&#10; request.RegisterForCancellation&#40;OnAsynchronousRequestCancelled, cancellationToken&#41;&#59;&#10;&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; request&#41;&#59;&#10; &#125;&#10;&#10; return request.Task&#59;&#10; &#125;&#10;&#10; private AsynchronousComputationToStart RegisterAsynchronousComputation_NoLock&#40;&#41;&#10; &#123;&#10; Contract.ThrowIfTrue&#40;this.computationActive&#41;&#59;&#10;&#10; this.asynchronousComputationCancellationSource &#61; new CancellationTokenSource&#40;&#41;&#59;&#10; this.computationActive &#61; true&#59;&#10;&#10; return new AsynchronousComputationToStart&#40;this.asynchronousComputeFunction, this.asynchronousComputationCancellationSource&#41;&#59;&#10; &#125;&#10;&#10; private struct AsynchronousComputationToStart&#10; &#123;&#10; public readonly Func&#60;CancellationToken, Task&#60;T&#62;&#62; AsynchronousComputeFunction&#59;&#10; public readonly CancellationTokenSource CancellationTokenSource&#59;&#10;&#10; public AsynchronousComputationToStart&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, CancellationTokenSource cancellationTokenSource&#41;&#10; &#123;&#10; this.AsynchronousComputeFunction &#61; asynchronousComputeFunction&#59;&#10; this.CancellationTokenSource &#61; cancellationTokenSource&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void StartAsynchronousComputation&#40;AsynchronousComputationToStart computationToStart, Request requestToCompleteSynchronously&#41;&#10; &#123;&#10; var cancellationToken &#61; computationToStart.CancellationTokenSource.Token&#59;&#10;&#10; try&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; DO NOT ACCESS ANY FIELDS OR STATE BEYOND THIS POINT. Since this function&#10; &#47;&#47; runs unsynchronized, it&#39;s possible that during this function this request&#10; &#47;&#47; might be cancelled, and then a whole additional request might start and&#10; &#47;&#47; complete inline, and cache the result. By grabbing state before we check&#10; &#47;&#47; the cancellation token, we can be assured that we are only operating on&#10; &#47;&#47; a state that was complete.&#10;&#10; ExceptionUtilities.ExecuteWithErrorReporting&#40;&#40;&#41; &#61;&#62;&#10; &#123;&#10; &#47;&#47; We avoid creating a full closure just to pass the token along&#10; &#47;&#47; Also, use TaskContinuationOptions.ExecuteSynchronously so that we inline &#10; &#47;&#47; the continuation if asynchronousComputeFunction completes synchronously&#10; var task &#61; computationToStart.AsynchronousComputeFunction&#40;cancellationToken&#41;&#59;&#10;&#10; task.ContinueWith&#40;&#10; &#40;t, s&#41; &#61;&#62; CompleteWithTask&#40;t, &#40;&#40;CancellationTokenSource&#41;s&#41;.Token&#41;,&#10; computationToStart.CancellationTokenSource,&#10; cancellationToken,&#10; TaskContinuationOptions.ExecuteSynchronously,&#10; TaskScheduler.Default&#41;&#59;&#10;&#10; if &#40;requestToCompleteSynchronously &#33;&#61; null &#38;&#38; task.IsCompleted&#41;&#10; &#123;&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; task &#61; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;task&#41;&#59;&#10; &#125;&#10;&#10; requestToCompleteSynchronously.CompleteFromTaskSynchronously&#40;task&#41;&#59;&#10; &#125;&#10; &#125;&#41;&#59;&#10; &#125;&#10; catch &#40;OperationCanceledException oce&#41;&#10; &#123;&#10; &#47;&#47; As long as it&#39;s the right token, this means that our thread was the first thread&#10; &#47;&#47; to start an asynchronous computation, but the requestor cancelled as we were starting up&#10; &#47;&#47; the computation.&#10; if &#40;oce.CancellationToken &#33;&#61; cancellationToken&#41;&#10; &#123;&#10; ExceptionUtilities.FailFast&#40;oce&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; private void CompleteWithTask&#40;Task&#60;T&#62; task, CancellationToken cancellationToken&#41;&#10; &#123;&#10; IEnumerable&#60;Request&#62; requestsToComplete&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If the underlying computation was cancelled, then all state was already updated in OnAsynchronousRequestCancelled&#10; &#47;&#47; and there is no new work to do here. We &#42;must&#42; use the local one since this completion may be running far after&#10; &#47;&#47; the background computation was cancelled and a new one might have already been enqueued. We must do this&#10; &#47;&#47; check here under the lock to ensure proper synchronization with OnAsynchronousRequestCancelled.&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; The computation is complete, so get all requests to complete and null out the list. We&#39;ll create another one&#10; &#47;&#47; later if it&#39;s needed&#10; requestsToComplete &#61; this.requests &#63;&#63; SpecializedCollections.EmptyEnumerable&#60;Request&#62;&#40;&#41;&#59;&#10; this.requests &#61; null&#59;&#10;&#10; &#47;&#47; The computations are done&#10; this.asynchronousComputationCancellationSource &#61; null&#59;&#10; this.computationActive &#61; false&#59;&#10; task &#61; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;task&#41;&#59;&#10; &#125;&#10;&#10; foreach &#40;var requestToComplete in requestsToComplete&#41;&#10; &#123;&#10; requestToComplete.CompleteFromTaskAsynchronously&#40;task&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; private Task&#60;T&#62; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; if &#40;this.cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return this.cachedResult&#59;&#10; &#125;&#10; else&#10; &#123;&#10; if &#40;cacheResult &#38;&#38; task.Status &#61;&#61; TaskStatus.RanToCompletion&#41;&#10; &#123;&#10; &#47;&#47; Hold onto the completed task. We can get rid of the computation functions for good&#10; this.cachedResult &#61; task&#59;&#10; this.asynchronousComputeFunction &#61; null&#59;&#10; this.synchronousComputeFunction &#61; null&#59;&#10; &#125;&#10;&#10; return task&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void OnAsynchronousRequestCancelled&#40;object state&#41;&#10; &#123;&#10; var request &#61; &#40;Request&#41;state&#59;&#10; CancellationTokenSource cancellationTokenSource &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; &#47;&#47; Now try to remove it. It&#39;s possible that requests may already be null. You could&#10; &#47;&#47; imagine that cancellation was requested, but before we could aquire the lock&#10; &#47;&#47; here the computation completed and the entire CompleteWithTask synchronized&#10; &#47;&#47; block ran. In that case, the requests collection may already be null, or it&#10; &#47;&#47; &#40;even scarier&#33;&#41; may have been replaced with another collection because another&#10; &#47;&#47; computation has started.&#10; if &#40;this.requests &#33;&#61; null&#41;&#10; &#123;&#10; if &#40;this.requests.Remove&#40;request&#41;&#41;&#10; &#123;&#10; if &#40;this.requests.Count &#61;&#61; 0&#41;&#10; &#123;&#10; this.requests &#61; null&#59;&#10;&#10; if &#40;this.asynchronousComputationCancellationSource &#33;&#61; null&#41;&#10; &#123;&#10; cancellationTokenSource &#61; this.asynchronousComputationCancellationSource&#59;&#10; this.asynchronousComputationCancellationSource &#61; null&#59;&#10; this.computationActive &#61; false&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; request.CancelAsynchronously&#40;&#41;&#59;&#10;&#10; if &#40;cancellationTokenSource &#33;&#61; null&#41;&#10; &#123;&#10; cancellationTokenSource.Cancel&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; Using inheritance instead of wrapping a TaskCompletionSource to avoid a second allocation&#10; private class Request &#58; TaskCompletionSource&#60;T&#62;&#10; &#123;&#10; private CancellationTokenRegistration cancellationTokenRegistration&#59;&#10;&#10; public void RegisterForCancellation&#40;Action&#60;object&#62; callback, CancellationToken cancellationToken&#41;&#10; &#123;&#10; cancellationTokenRegistration &#61; cancellationToken.Register&#40;callback, this&#41;&#59;&#10; &#125;&#10;&#10; public void CompleteFromTaskAsynchronously&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; System.Threading.Tasks.Task.Factory.StartNew&#40;CompleteFromTaskSynchronouslyStub, task, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default&#41;&#59;&#10; &#125;&#10;&#10; private void CompleteFromTaskSynchronouslyStub&#40;object task&#41;&#10; &#123;&#10; CompleteFromTaskSynchronously&#40;&#40;Task&#60;T&#62;&#41;task&#41;&#59;&#10; &#125;&#10;&#10; public void CompleteFromTaskSynchronously&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; if &#40;task.Status &#61;&#61; TaskStatus.RanToCompletion&#41;&#10; &#123;&#10; if &#40;TrySetResult&#40;task.Result&#41;&#41;&#10; &#123;&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; else if &#40;task.Status &#61;&#61; TaskStatus.Faulted&#41;&#10; &#123;&#10; if &#40;TrySetException&#40;task.Exception&#41;&#41;&#10; &#123;&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; else&#10; &#123;&#10; CancelSynchronously&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; public void CancelAsynchronously&#40;&#41;&#10; &#123;&#10; &#47;&#47; Since there could be synchronous continuations on the TaskCancellationSource, we queue this to the threadpool&#10; &#47;&#47; to avoid inline running of other operations.&#10; System.Threading.Tasks.Task.Factory.StartNew&#40;CancelSynchronously, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default&#41;&#59;&#10; &#125;&#10;&#10; private void CancelSynchronously&#40;&#41;&#10; &#123;&#10; if &#40;TrySetCanceled&#40;&#41;&#41;&#10; &#123;&#10; &#47;&#47; Paranoia&#58; the only reason we should ever get here is if the CancellationToken that&#10; &#47;&#47; we registered against was cancelled, but just in case, dispose the registration&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#125;</pre>2013-05-23T06:05:29-07:00QShttp://rise4fun.com/Seal/QSSeal/QS<pre>using System&#59;&#10;using System.Collections.Generic&#59;&#10;using System.Runtime.CompilerServices&#59;&#10;using System.Threading&#59;&#10;using System.Threading.Tasks&#59;&#10;&#10;namespace Roslyn.Utilities&#10;&#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Represents a value that can be retrieved synchronously or asynchronously by many clients.&#10; &#47;&#47;&#47; The value will be computed on-demand the moment the first client asks for it. While being&#10; &#47;&#47;&#47; computed, more clients can request the value. As long as there are outstanding clients the&#10; &#47;&#47;&#47; underlying computation will proceed. If all outstanding clients cancel their request then&#10; &#47;&#47;&#47; the underlying value computation will be cancelled as well.&#10; &#47;&#47;&#47; &#10; &#47;&#47;&#47; Creators of an &#60;see cref&#61;&#34;AsyncLazy&#123;T&#125;&#34; &#47;&#62; can specify whether the result of the computation is&#10; &#47;&#47;&#47; cached for future requests or not. Choosing to not cache means the computation functions are kept&#10; &#47;&#47;&#47; alive, whereas caching means the value &#40;but not functions&#41; are kept alive once complete.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; internal sealed class AsyncLazy&#60;T&#62;&#10; &#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The underlying function that starts an asynchronous computation of the resulting value.&#10; &#47;&#47;&#47; Null&#39;ed out once we&#39;ve computed the result and we&#39;ve been asked to cache it. Otherwise,&#10; &#47;&#47;&#47; it is kept around in case the value needs to be computed again.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The underlying function that starts an synchronous computation of the resulting value.&#10; &#47;&#47;&#47; Null&#39;ed out once we&#39;ve computed the result and we&#39;ve been asked to cache it, or if we&#10; &#47;&#47;&#47; didn&#39;t get any synchronous function given to us in the first place.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Func&#60;CancellationToken, T&#62; synchronousComputeFunction&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Whether or not we should keep the value around once we&#39;ve computed it.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private readonly bool cacheResult&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The Task that holds the cached result.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Task&#60;T&#62; cachedResult&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Mutex used to protect reading and writing to all mutable objects and fields. Traces&#10; &#47;&#47;&#47; indicate that there&#39;s negligible contention on this lock, hence we can save some memory&#10; &#47;&#47;&#47; by using a single lock for all AsyncLazy instances. Only trivial and non-reentrant work&#10; &#47;&#47;&#47; should be done while holding the lock.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private static readonly NonReentrantLock gate &#61; new NonReentrantLock&#40;useThisInstanceForSynchronization&#58; true&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The hash set of all currently outstanding asynchronous requests. Null if there are no requests,&#10; &#47;&#47;&#47; and will never be empty.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private HashSet&#60;Request&#62; requests &#61; null&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; If an asynchronous request is active, the CancellationTokenSource that allows for&#10; &#47;&#47;&#47; cancelling the underlying computation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private CancellationTokenSource asynchronousComputationCancellationSource &#61; null&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Whether a computation is active or queued on any thread, whether synchronous or&#10; &#47;&#47;&#47; asynchronous.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool computationActive &#61; false&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Creates an AsyncLazy that always returns the value, analogous to &#60;see cref&#61;&#34;Task.FromResult&#123;T&#125;&#34; &#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public AsyncLazy&#40;T value&#41;&#10; &#123;&#10; this.cacheResult &#61; true&#59;&#10; this.cachedResult &#61; new Task&#60;T&#62;&#40;&#40;&#41; &#61;&#62; &#123;return value&#59;&#125;&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Important&#58; callers of this constructor should ensure that the compute function returns&#10; &#47;&#47;&#47; a task in a non-blocking fashion. i.e. the function should &#42;not&#42; synchronously compute&#10; &#47;&#47;&#47; a value and then return it using Task.FromResult. Instead, it should return an actual&#10; &#47;&#47;&#47; task that operates asynchronously. If this function synchronously computes a value&#10; &#47;&#47;&#47; then that will cause locks to be held in this type for excessive periods of time.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public AsyncLazy&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, bool cacheResult&#41;&#10; &#58; this&#40;asynchronousComputeFunction, synchronousComputeFunction&#58; null, cacheResult&#58; cacheResult&#41;&#10; &#123;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Creates an AsyncLazy that supports both asynchronous computation and inline synchronous&#10; &#47;&#47;&#47; computation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;asynchronousComputeFunction&#34;&#62;A function called to start the asynchronous&#10; &#47;&#47;&#47; computation. This function should be cheap and non-blocking.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;synchronousComputeFunction&#34;&#62;A function to do the work synchronously, which&#10; &#47;&#47;&#47; is allowed to block. This function should not be implemented by a simple Wait on the&#10; &#47;&#47;&#47; asynchronous value. If that&#39;s all you are doing, just don&#39;t pass a synchronous function&#10; &#47;&#47;&#47; in the first place.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;cacheResult&#34;&#62;Whether the result should be cached once the computation is&#10; &#47;&#47;&#47; complete.&#60;&#47;param&#62;&#10; public AsyncLazy&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, Func&#60;CancellationToken, T&#62; synchronousComputeFunction, bool cacheResult&#41;&#10; &#123;&#10; Contract.ThrowIfNull&#40;asynchronousComputeFunction&#41;&#59;&#10;&#10; this.asynchronousComputeFunction &#61; asynchronousComputeFunction&#59;&#10; this.synchronousComputeFunction &#61; synchronousComputeFunction&#59;&#10; this.cacheResult &#61; cacheResult&#59;&#10; &#125;&#10;&#10; &#35;region Lock Wrapper for Invariant Checking&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Takes the lock for this object and if acquired validates the invariants of this class.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private WaitThatValidatesInvariants TakeLock&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; gate.Wait&#40;cancellationToken&#41;&#59;&#10; AssertInvariants_NoLock&#40;&#41;&#59;&#10; return new WaitThatValidatesInvariants&#40;this&#41;&#59;&#10; &#125;&#10;&#10; private struct WaitThatValidatesInvariants &#58; IDisposable&#10; &#123;&#10; private readonly AsyncLazy&#60;T&#62; asyncLazy&#59;&#10;&#10; public WaitThatValidatesInvariants&#40;AsyncLazy&#60;T&#62; asyncLazy&#41;&#10; &#123;&#10; this.asyncLazy &#61; asyncLazy&#59;&#10; &#125;&#10;&#10; public void Dispose&#40;&#41;&#10; &#123;&#10; asyncLazy.AssertInvariants_NoLock&#40;&#41;&#59;&#10; gate.Release&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void AssertInvariants_NoLock&#40;&#41;&#10; &#123;&#10; &#47;&#47; Invariant &#35;1&#58; thou shalt never have an asynchronous computation running without it&#10; &#47;&#47; being considered a computation&#10; Contract.ThrowIfTrue&#40;this.asynchronousComputationCancellationSource &#33;&#61; null &#38;&#38;&#10; &#33;this.computationActive&#41;&#59;&#10;&#10; &#47;&#47; Invariant &#35;2&#58; thou shalt never waste memory holding onto empty HashSets&#10; Contract.ThrowIfTrue&#40;this.requests &#33;&#61; null &#38;&#38;&#10; this.requests.Count &#61;&#61; 0&#41;&#59;&#10;&#10; &#47;&#47; Invariant &#35;3&#58; thou shalt never have an request if there is not&#10; &#47;&#47; something trying to compute it&#10; Contract.ThrowIfTrue&#40;this.requests &#33;&#61; null &#38;&#38;&#10; &#33;this.computationActive&#41;&#59;&#10;&#10; &#47;&#47; Invariant &#35;4&#58; thou shalt never have a cached value and any computation function&#10; Contract.ThrowIfTrue&#40;this.cachedResult &#33;&#61; null &#38;&#38;&#10; &#40;this.synchronousComputeFunction &#33;&#61; null &#124;&#124; this.asynchronousComputeFunction &#33;&#61; null&#41;&#41;&#59;&#10;&#10; &#47;&#47; Invariant &#35;5&#58; thou shalt never have a synchronous computation function but not an&#10; &#47;&#47; asynchronous one&#10; Contract.ThrowIfTrue&#40;this.asynchronousComputeFunction &#61;&#61; null &#38;&#38; this.synchronousComputeFunction &#33;&#61; null&#41;&#59;&#10; &#125;&#10;&#10; &#35;endregion&#10;&#10; public bool TryGetValue&#40;out T result&#41;&#10; &#123;&#10; &#47;&#47; No need to lock here since this is only a fast check to &#10; &#47;&#47; see if the result is already computed.&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; result &#61; cachedResult.Result&#59;&#10; return true&#59;&#10; &#125;&#10;&#10; result &#61; default&#40;T&#41;&#59;&#10; return false&#59;&#10; &#125;&#10;&#10; public T GetValue&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; Request request &#61; null&#59;&#10; AsynchronousComputationToStart&#63; newAsynchronousComputation &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If cached, get immediately&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return cachedResult.Result&#59;&#10; &#125;&#10;&#10; &#47;&#47; If there is an existing computation active, we&#39;ll just create another request&#10; if &#40;computationActive&#41;&#10; &#123;&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10; &#125;&#10; else if &#40;synchronousComputeFunction &#61;&#61; null&#41;&#10; &#123;&#10; &#47;&#47; A synchronous request, but we have no synchronous function. Start off the async work&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10;&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; else&#10; &#123;&#10; &#47;&#47; We will do the computation here&#10; this.computationActive &#61; true&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; If we simply created a new asynchronous request, so wait for it. Yes, we&#39;re blocking the thread&#10; &#47;&#47; but we don&#39;t want multiple threads attempting to compute the same thing.&#10; if &#40;request &#33;&#61; null&#41;&#10; &#123;&#10; request.RegisterForCancellation&#40;OnAsynchronousRequestCancelled, cancellationToken&#41;&#59;&#10;&#10; &#47;&#47; Since we already registered for cancellation, it&#39;s possible that the registration has&#10; &#47;&#47; cancelled this new computation if we were the only requestor.&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; request&#41;&#59;&#10; &#125;&#10;&#10; return request.Task.WaitAndGetResult&#40;cancellationToken&#41;&#59;&#10; &#125;&#10; else&#10; &#123;&#10; T result&#59;&#10;&#10; &#47;&#47; We are the active computation, so let&#39;s go ahead and compute.&#10; try&#10; &#123;&#10; result &#61; synchronousComputeFunction&#40;cancellationToken&#41;&#59;&#10; &#125;&#10; catch &#40;OperationCanceledException&#41;&#10; &#123;&#10; &#47;&#47; This cancelled for some reason. We don&#39;t care why, but&#10; &#47;&#47; it means anybody else waiting for this result isn&#39;t going to get it&#10; &#47;&#47; from us.&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; this.computationActive &#61; false&#59;&#10;&#10; if &#40;requests &#33;&#61; null&#41;&#10; &#123;&#10; &#47;&#47; There&#39;s a possible improvement here&#58; there might be another synchronous caller who&#10; &#47;&#47; also wants the value. We might consider stealing their thread rather than punting&#10; &#47;&#47; to the thread pool.&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; null&#41;&#59;&#10; &#125;&#10;&#10; throw&#59;&#10; &#125;&#10; catch &#40;Exception ex&#41;&#10; &#123;&#10; &#47;&#47; We faulted for some unknown reason. We should simply fault everything.&#10; TaskCompletionSource&#60;T&#62; tcs &#61; new TaskCompletionSource&#60;T&#62;&#40;&#41;&#59;&#10; tcs.SetException&#40;ex&#41;&#59;&#10; CompleteWithTask&#40;tcs.Task, CancellationToken.None&#41;&#59;&#10;&#10; throw&#59;&#10; &#125;&#10;&#10; &#47;&#47; We have a value, so complete&#10; CompleteWithTask&#40;Task.FromResult&#40;result&#41;, CancellationToken.None&#41;&#59;&#10;&#10; return result&#59;&#10; &#125;&#10; &#125;&#10;&#10; private Request CreateNewRequest_NoLock&#40;&#41;&#10; &#123;&#10; if &#40;this.requests &#61;&#61; null&#41;&#10; &#123;&#10; this.requests &#61; new HashSet&#60;Request&#62;&#40;&#41;&#59;&#10; &#125;&#10;&#10; Request request &#61; new Request&#40;&#41;&#59;&#10; this.requests.Add&#40;request&#41;&#59;&#10; return request&#59;&#10; &#125;&#10;&#10; public Task&#60;T&#62; GetValueAsync&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; &#47;&#47; Optimization&#58; if we&#39;re already cancelled, do not pass go&#10; if &#40;cancellationToken.IsCancellationRequested&#41;&#10; &#123;&#10; return new Task&#60;T&#62;&#40;&#40;&#41; &#61;&#62; default&#40;T&#41;, cancellationToken&#41;&#59;&#10; &#125;&#10;&#10; Request request&#59;&#10; AsynchronousComputationToStart&#63; newAsynchronousComputation &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If cached, get immediately&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return cachedResult&#59;&#10; &#125;&#10;&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10;&#10; &#47;&#47; If we have either synchronous or asynchronous work current in flight, we don&#39;t need to do anything.&#10; &#47;&#47; Otherwise, we shall start an asynchronous computation for this&#10; if &#40;&#33;computationActive&#41;&#10; &#123;&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; We now have the request counted for, register for cancellation. It is critical this is&#10; &#47;&#47; done outside the lock, as our reigstration may immediately fire and we want to avoid the&#10; &#47;&#47; reentrancy&#10; request.RegisterForCancellation&#40;OnAsynchronousRequestCancelled, cancellationToken&#41;&#59;&#10;&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; request&#41;&#59;&#10; &#125;&#10;&#10; return request.Task&#59;&#10; &#125;&#10;&#10; private AsynchronousComputationToStart RegisterAsynchronousComputation_NoLock&#40;&#41;&#10; &#123;&#10; Contract.ThrowIfTrue&#40;this.computationActive&#41;&#59;&#10;&#10; this.asynchronousComputationCancellationSource &#61; new CancellationTokenSource&#40;&#41;&#59;&#10; this.computationActive &#61; true&#59;&#10;&#10; return new AsynchronousComputationToStart&#40;this.asynchronousComputeFunction, this.asynchronousComputationCancellationSource&#41;&#59;&#10; &#125;&#10;&#10; private struct AsynchronousComputationToStart&#10; &#123;&#10; public readonly Func&#60;CancellationToken, Task&#60;T&#62;&#62; AsynchronousComputeFunction&#59;&#10; public readonly CancellationTokenSource CancellationTokenSource&#59;&#10;&#10; public AsynchronousComputationToStart&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, CancellationTokenSource cancellationTokenSource&#41;&#10; &#123;&#10; this.AsynchronousComputeFunction &#61; asynchronousComputeFunction&#59;&#10; this.CancellationTokenSource &#61; cancellationTokenSource&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void StartAsynchronousComputation&#40;AsynchronousComputationToStart computationToStart, Request requestToCompleteSynchronously&#41;&#10; &#123;&#10; var cancellationToken &#61; computationToStart.CancellationTokenSource.Token&#59;&#10;&#10; try&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; DO NOT ACCESS ANY FIELDS OR STATE BEYOND THIS POINT. Since this function&#10; &#47;&#47; runs unsynchronized, it&#39;s possible that during this function this request&#10; &#47;&#47; might be cancelled, and then a whole additional request might start and&#10; &#47;&#47; complete inline, and cache the result. By grabbing state before we check&#10; &#47;&#47; the cancellation token, we can be assured that we are only operating on&#10; &#47;&#47; a state that was complete.&#10;&#10; ExceptionUtilities.ExecuteWithErrorReporting&#40;&#40;&#41; &#61;&#62;&#10; &#123;&#10; &#47;&#47; We avoid creating a full closure just to pass the token along&#10; &#47;&#47; Also, use TaskContinuationOptions.ExecuteSynchronously so that we inline &#10; &#47;&#47; the continuation if asynchronousComputeFunction completes synchronously&#10; var task &#61; computationToStart.AsynchronousComputeFunction&#40;cancellationToken&#41;&#59;&#10;&#10; task.ContinueWith&#40;&#10; &#40;t, s&#41; &#61;&#62; CompleteWithTask&#40;t, &#40;&#40;CancellationTokenSource&#41;s&#41;.Token&#41;,&#10; computationToStart.CancellationTokenSource,&#10; cancellationToken,&#10; TaskContinuationOptions.ExecuteSynchronously,&#10; TaskScheduler.Default&#41;&#59;&#10;&#10; if &#40;requestToCompleteSynchronously &#33;&#61; null &#38;&#38; task.IsCompleted&#41;&#10; &#123;&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; task &#61; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;task&#41;&#59;&#10; &#125;&#10;&#10; requestToCompleteSynchronously.CompleteFromTaskSynchronously&#40;task&#41;&#59;&#10; &#125;&#10; &#125;&#41;&#59;&#10; &#125;&#10; catch &#40;OperationCanceledException oce&#41;&#10; &#123;&#10; &#47;&#47; As long as it&#39;s the right token, this means that our thread was the first thread&#10; &#47;&#47; to start an asynchronous computation, but the requestor cancelled as we were starting up&#10; &#47;&#47; the computation.&#10; if &#40;oce.CancellationToken &#33;&#61; cancellationToken&#41;&#10; &#123;&#10; ExceptionUtilities.FailFast&#40;oce&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; private void CompleteWithTask&#40;Task&#60;T&#62; task, CancellationToken cancellationToken&#41;&#10; &#123;&#10; IEnumerable&#60;Request&#62; requestsToComplete&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If the underlying computation was cancelled, then all state was already updated in OnAsynchronousRequestCancelled&#10; &#47;&#47; and there is no new work to do here. We &#42;must&#42; use the local one since this completion may be running far after&#10; &#47;&#47; the background computation was cancelled and a new one might have already been enqueued. We must do this&#10; &#47;&#47; check here under the lock to ensure proper synchronization with OnAsynchronousRequestCancelled.&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; The computation is complete, so get all requests to complete and null out the list. We&#39;ll create another one&#10; &#47;&#47; later if it&#39;s needed&#10; requestsToComplete &#61; this.requests &#63;&#63; SpecializedCollections.EmptyEnumerable&#60;Request&#62;&#40;&#41;&#59;&#10; this.requests &#61; null&#59;&#10;&#10; &#47;&#47; The computations are done&#10; this.asynchronousComputationCancellationSource &#61; null&#59;&#10; this.computationActive &#61; false&#59;&#10; task &#61; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;task&#41;&#59;&#10; &#125;&#10;&#10; foreach &#40;var requestToComplete in requestsToComplete&#41;&#10; &#123;&#10; requestToComplete.CompleteFromTaskAsynchronously&#40;task&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; private Task&#60;T&#62; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; if &#40;this.cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return this.cachedResult&#59;&#10; &#125;&#10; else&#10; &#123;&#10; if &#40;cacheResult &#38;&#38; task.Status &#61;&#61; TaskStatus.RanToCompletion&#41;&#10; &#123;&#10; &#47;&#47; Hold onto the completed task. We can get rid of the computation functions for good&#10; this.cachedResult &#61; task&#59;&#10; this.asynchronousComputeFunction &#61; null&#59;&#10; this.synchronousComputeFunction &#61; null&#59;&#10; &#125;&#10;&#10; return task&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void OnAsynchronousRequestCancelled&#40;object state&#41;&#10; &#123;&#10; var request &#61; &#40;Request&#41;state&#59;&#10; CancellationTokenSource cancellationTokenSource &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; &#47;&#47; Now try to remove it. It&#39;s possible that requests may already be null. You could&#10; &#47;&#47; imagine that cancellation was requested, but before we could aquire the lock&#10; &#47;&#47; here the computation completed and the entire CompleteWithTask synchronized&#10; &#47;&#47; block ran. In that case, the requests collection may already be null, or it&#10; &#47;&#47; &#40;even scarier&#33;&#41; may have been replaced with another collection because another&#10; &#47;&#47; computation has started.&#10; if &#40;this.requests &#33;&#61; null&#41;&#10; &#123;&#10; if &#40;this.requests.Remove&#40;request&#41;&#41;&#10; &#123;&#10; if &#40;this.requests.Count &#61;&#61; 0&#41;&#10; &#123;&#10; this.requests &#61; null&#59;&#10;&#10; if &#40;this.asynchronousComputationCancellationSource &#33;&#61; null&#41;&#10; &#123;&#10; cancellationTokenSource &#61; this.asynchronousComputationCancellationSource&#59;&#10; this.asynchronousComputationCancellationSource &#61; null&#59;&#10; this.computationActive &#61; false&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; request.CancelAsynchronously&#40;&#41;&#59;&#10;&#10; if &#40;cancellationTokenSource &#33;&#61; null&#41;&#10; &#123;&#10; cancellationTokenSource.Cancel&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; Using inheritance instead of wrapping a TaskCompletionSource to avoid a second allocation&#10; private class Request &#58; TaskCompletionSource&#60;T&#62;&#10; &#123;&#10; private CancellationTokenRegistration cancellationTokenRegistration&#59;&#10;&#10; public void RegisterForCancellation&#40;Action&#60;object&#62; callback, CancellationToken cancellationToken&#41;&#10; &#123;&#10; cancellationTokenRegistration &#61; cancellationToken.Register&#40;callback, this&#41;&#59;&#10; &#125;&#10;&#10; public void CompleteFromTaskAsynchronously&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; System.Threading.Tasks.Task.Factory.StartNew&#40;CompleteFromTaskSynchronouslyStub, task, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default&#41;&#59;&#10; &#125;&#10;&#10; private void CompleteFromTaskSynchronouslyStub&#40;object task&#41;&#10; &#123;&#10; CompleteFromTaskSynchronously&#40;&#40;Task&#60;T&#62;&#41;task&#41;&#59;&#10; &#125;&#10;&#10; public void CompleteFromTaskSynchronously&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; if &#40;task.Status &#61;&#61; TaskStatus.RanToCompletion&#41;&#10; &#123;&#10; if &#40;TrySetResult&#40;task.Result&#41;&#41;&#10; &#123;&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; else if &#40;task.Status &#61;&#61; TaskStatus.Faulted&#41;&#10; &#123;&#10; if &#40;TrySetException&#40;task.Exception&#41;&#41;&#10; &#123;&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; else&#10; &#123;&#10; CancelSynchronously&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; public void CancelAsynchronously&#40;&#41;&#10; &#123;&#10; &#47;&#47; Since there could be synchronous continuations on the TaskCancellationSource, we queue this to the threadpool&#10; &#47;&#47; to avoid inline running of other operations.&#10; System.Threading.Tasks.Task.Factory.StartNew&#40;CancelSynchronously, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default&#41;&#59;&#10; &#125;&#10;&#10; private void CancelSynchronously&#40;&#41;&#10; &#123;&#10; if &#40;TrySetCanceled&#40;&#41;&#41;&#10; &#123;&#10; &#47;&#47; Paranoia&#58; the only reason we should ever get here is if the CancellationToken that&#10; &#47;&#47; we registered against was cancelled, but just in case, dispose the registration&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; A lightweight mutual exclusion object which supports waiting with cancellation and prevents&#10; &#47;&#47;&#47; recursion &#40;i.e. you may not call Wait if you already hold the lock&#41;&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; The &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62; provides a lightweight mutual exclusion class that doesn&#39;t&#10; &#47;&#47;&#47; use Windows kernel synchronization primitives.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; The implementation is distilled from the workings of &#60;see cref&#61;&#34;T&#58;System.Threading.SemaphoreSlim&#34;&#47;&#62;&#10; &#47;&#47;&#47; The basic idea is that we use a regular sync object &#40;Monitor.Enter&#47;Exit&#41; to guard the setting&#10; &#47;&#47;&#47; of an &#39;owning thread&#39; field. If, during the Wait, we find the lock is held by someone else&#10; &#47;&#47;&#47; then we register a cancellation callback and enter a &#34;Monitor.Wait&#34; loop. If the cancellation&#10; &#47;&#47;&#47; callback fires, then it &#34;pulses&#34; all the waiters to wake them up and check for cancellation.&#10; &#47;&#47;&#47; Waiters are also &#34;pulsed&#34; when leaving the lock.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; All public members of &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62; are thread-safe and may be used concurrently&#10; &#47;&#47;&#47; from multiple threads.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; internal sealed class NonReentrantLock&#10; &#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; A synchronization object to protect access to the &#60;see cref&#61;&#34;F&#58;owningThread&#34;&#47;&#62; field and to be pulsed&#10; &#47;&#47;&#47; when &#60;see cref&#61;&#34;M&#58;Release&#34;&#47;&#62; is called and during cancellation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private readonly object syncLock&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Indicates which thread currently holds the lock. If null, then the lock is available.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private volatile Thread owningThread&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Constructor.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;useThisInstanceForSynchronization&#34;&#62;If false &#40;the default&#41;, then the class&#10; &#47;&#47;&#47; allocates an internal object to be used as a sync lock.&#10; &#47;&#47;&#47; If true, then the sync lock object will be the NonReentrantLock instance itself. This&#10; &#47;&#47;&#47; saves an allocation but a client may not safely further use this instance in a call to&#10; &#47;&#47;&#47; Monitor.Enter&#47;Exit or in a &#34;lock&#34; statement.&#10; &#47;&#47;&#47; &#60;&#47;param&#62;&#10; public NonReentrantLock&#40;bool useThisInstanceForSynchronization &#61; false&#41;&#10; &#123;&#10; this.syncLock &#61; useThisInstanceForSynchronization &#63; this &#58; new object&#40;&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Shared factory for use in lazy initialization.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public static readonly Func&#60;NonReentrantLock&#62; Factory &#61; &#40;&#41; &#61;&#62; new NonReentrantLock&#40;useThisInstanceForSynchronization&#58; true&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Blocks the current thread until it can enter the &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62;, while observing a&#10; &#47;&#47;&#47; &#60;see cref&#61;&#34;T&#58;System.Threading.CancellationToken&#34;&#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; Recursive locking is not supported. i.e. A thread may not call Wait successfully twice without an&#10; &#47;&#47;&#47; intervening &#60;see cref&#61;&#34;Release&#34;&#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;cancellationToken&#34;&#62;The &#60;see cref&#61;&#34;T&#58;System.Threading.CancellationToken&#34;&#47;&#62; token to&#10; &#47;&#47;&#47; observe.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;System.OperationCanceledException&#34;&#62;&#60;paramref name&#61;&#34;cancellationToken&#34;&#47;&#62; was&#10; &#47;&#47;&#47; canceled.&#60;&#47;exception&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;System.LockRecursionException&#34;&#62;The caller already holds the lock&#60;&#47;exception&#62;&#10; public void Wait&#40;CancellationToken cancellationToken &#61; default&#40;CancellationToken&#41;&#41;&#10; &#123;&#10; if &#40;this.IsOwnedByMe&#41;&#10; &#123;&#10; throw new LockRecursionException&#40;&#41;&#59;&#10; &#125;&#10;&#10; CancellationTokenRegistration cancellationTokenRegistration &#61; default&#40;CancellationTokenRegistration&#41;&#59;&#10;&#10; if &#40;cancellationToken.CanBeCanceled&#41;&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; Fast path to try and avoid allocations in callback registration.&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; if &#40;&#33;this.IsLocked&#41;&#10; &#123;&#10; this.TakeOwnership&#40;&#41;&#59;&#10; return&#59;&#10; &#125;&#10; &#125;&#10;&#10; cancellationTokenRegistration &#61; cancellationToken.Register&#40;cancellationTokenCanceledEventHandler, this.syncLock, useSynchronizationContext&#58; false&#41;&#59;&#10; &#125;&#10;&#10; using &#40;cancellationTokenRegistration&#41;&#10; &#123;&#10; &#47;&#47; PERF&#58; First spin wait for the lock to become available, but only up to the first planned yield.&#10; &#47;&#47; This additional amount of spinwaiting was inherited from SemaphoreSlim&#39;s implementation where&#10; &#47;&#47; it showed measurable perf gains in test scenarios.&#10; SpinWait spin &#61; new SpinWait&#40;&#41;&#59;&#10; while &#40;this.IsLocked &#38;&#38; &#33;spin.NextSpinWillYield&#41;&#10; &#123;&#10; spin.SpinOnce&#40;&#41;&#59;&#10; &#125;&#10;&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; while &#40;this.IsLocked&#41;&#10; &#123;&#10; &#47;&#47; If cancelled, we throw. Trying to wait could lead to deadlock.&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; using &#40;Logger.LogBlock&#40;FeatureId.WaitIndicator, FunctionId.Utilities_NonReentrantLock_BlockingWait, cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; Another thread holds the lock. Wait until we get awoken either&#10; &#47;&#47; by some code calling &#34;Release&#34; or by cancellation.&#10; Monitor.Wait&#40;this.syncLock&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; We now hold the lock&#10; this.TakeOwnership&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Exit the mutual exclusion.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; The calling thread must currently hold the lock.&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;Roslyn.Utilities.ContractFailureException&#34;&#62;The lock is not currently held by the calling thread.&#60;&#47;exception&#62;&#10; public void Release&#40;&#41;&#10; &#123;&#10; AssertHasLock&#40;&#41;&#59;&#10;&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; this.ReleaseOwnership&#40;&#41;&#59;&#10;&#10; &#47;&#47; Release one waiter&#10; Monitor.Pulse&#40;this.syncLock&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Determine if the lock is currently held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;returns&#62;True if the lock is currently held by the calling thread.&#60;&#47;returns&#62;&#10; public bool LockHeldByMe&#40;&#41;&#10; &#123;&#10; return this.IsOwnedByMe&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Throw an exception if the lock is not held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;Roslyn.Utilities.ContractFailureException&#34;&#62;The lock is not currently held by the calling thread.&#60;&#47;exception&#62;&#10; public void AssertHasLock&#40;&#41;&#10; &#123;&#10; Contract.ThrowIfFalse&#40;LockHeldByMe&#40;&#41;&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Checks if the lock is currently held.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool IsLocked&#10; &#123;&#10; get&#10; &#123;&#10; return this.owningThread &#33;&#61; null&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Checks if the lock is currently held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool IsOwnedByMe&#10; &#123;&#10; get&#10; &#123;&#10; return this.owningThread &#61;&#61; Thread.CurrentThread&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Take ownership of the lock &#40;by the calling thread&#41;. The lock may not already&#10; &#47;&#47;&#47; be held by any other code.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private void TakeOwnership&#40;&#41;&#10; &#123;&#10; Contract.Assert&#40;&#33;this.IsLocked&#41;&#59;&#10; this.owningThread &#61; Thread.CurrentThread&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Release ownership of the lock. The lock must already be held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private void ReleaseOwnership&#40;&#41;&#10; &#123;&#10; Contract.Assert&#40;this.IsOwnedByMe&#41;&#59;&#10; this.owningThread &#61; null&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Action object passed to a cancellation token registration.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private static readonly Action&#60;object&#62; cancellationTokenCanceledEventHandler &#61; new Action&#60;object&#62;&#40;CancellationTokenCanceledEventHandler&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Callback executed when a cancellation token is canceled during a Wait.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;obj&#34;&#62;The syncLock that protects a &#60;see cref&#61;&#34;NonReentrantLock &#34;&#47;&#62; instance.&#60;&#47;param&#62;&#10; private static void CancellationTokenCanceledEventHandler&#40;object obj&#41;&#10; &#123;&#10; lock &#40;obj&#41;&#10; &#123;&#10; &#47;&#47; Release all waiters to check their cancellation tokens.&#10; Monitor.PulseAll&#40;obj&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#125;</pre>2013-05-23T06:05:29-07:00Qlhttp://rise4fun.com/Seal/QlSeal/Ql<pre>using System&#59;&#10;class Test &#123;&#10; public Test f &#61; null&#59;&#10; public void PureMethod&#40;Test param&#41;&#123;&#10; var temp &#61; new Test&#40;&#41;&#59;&#10; temp.f &#61; param&#59; &#10; &#47;&#47;Console.WriteLine&#40;&#34;Test&#34;&#41;&#59;&#10; f &#61; temp&#59; &#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00Qfhttp://rise4fun.com/Seal/QfSeal/Qf<pre>using System&#59;&#10;class Test &#123;&#10; public Test f &#61; null&#59;&#10; public Test PureMethod&#40;Test param&#41;&#123;&#10; var temp &#61; new Test&#40;&#41;&#59;&#10; temp &#61; param&#59;&#10; return temp&#59; &#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00pMhttp://rise4fun.com/Seal/pMSeal/pM<pre>&#10;using System&#59;&#10;using System.Collections.Generic&#59;&#10;using System.Runtime.CompilerServices&#59;&#10;using System.Threading&#59;&#10;using System.Threading.Tasks&#59;&#10;&#10;namespace Roslyn.Utilities&#10;&#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Represents a value that can be retrieved synchronously or asynchronously by many clients.&#10; &#47;&#47;&#47; The value will be computed on-demand the moment the first client asks for it. While being&#10; &#47;&#47;&#47; computed, more clients can request the value. As long as there are outstanding clients the&#10; &#47;&#47;&#47; underlying computation will proceed. If all outstanding clients cancel their request then&#10; &#47;&#47;&#47; the underlying value computation will be cancelled as well.&#10; &#47;&#47;&#47; &#10; &#47;&#47;&#47; Creators of an &#60;see cref&#61;&#34;AsyncLazy&#123;T&#125;&#34; &#47;&#62; can specify whether the result of the computation is&#10; &#47;&#47;&#47; cached for future requests or not. Choosing to not cache means the computation functions are kept&#10; &#47;&#47;&#47; alive, whereas caching means the value &#40;but not functions&#41; are kept alive once complete.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; internal sealed class AsyncLazy&#60;T&#62;&#10; &#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The underlying function that starts an asynchronous computation of the resulting value.&#10; &#47;&#47;&#47; Null&#39;ed out once we&#39;ve computed the result and we&#39;ve been asked to cache it. Otherwise,&#10; &#47;&#47;&#47; it is kept around in case the value needs to be computed again.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The underlying function that starts an synchronous computation of the resulting value.&#10; &#47;&#47;&#47; Null&#39;ed out once we&#39;ve computed the result and we&#39;ve been asked to cache it, or if we&#10; &#47;&#47;&#47; didn&#39;t get any synchronous function given to us in the first place.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Func&#60;CancellationToken, T&#62; synchronousComputeFunction&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Whether or not we should keep the value around once we&#39;ve computed it.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private readonly bool cacheResult&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The Task that holds the cached result.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Task&#60;T&#62; cachedResult&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Mutex used to protect reading and writing to all mutable objects and fields. Traces&#10; &#47;&#47;&#47; indicate that there&#39;s negligible contention on this lock, hence we can save some memory&#10; &#47;&#47;&#47; by using a single lock for all AsyncLazy instances. Only trivial and non-reentrant work&#10; &#47;&#47;&#47; should be done while holding the lock.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private static readonly NonReentrantLock gate &#61; new NonReentrantLock&#40;useThisInstanceForSynchronization&#58; true&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The hash set of all currently outstanding asynchronous requests. Null if there are no requests,&#10; &#47;&#47;&#47; and will never be empty.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private HashSet&#60;Request&#62; requests &#61; null&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; If an asynchronous request is active, the CancellationTokenSource that allows for&#10; &#47;&#47;&#47; cancelling the underlying computation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private CancellationTokenSource asynchronousComputationCancellationSource &#61; null&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Whether a computation is active or queued on any thread, whether synchronous or&#10; &#47;&#47;&#47; asynchronous.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool computationActive &#61; false&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Creates an AsyncLazy that always returns the value, analogous to &#60;see cref&#61;&#34;Task.FromResult&#123;T&#125;&#34; &#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public AsyncLazy&#40;T value&#41;&#10; &#123;&#10; this.cacheResult &#61; true&#59;&#10; this.cachedResult &#61; new Task&#40;&#40;&#41; &#61;&#62; value&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Important&#58; callers of this constructor should ensure that the compute function returns&#10; &#47;&#47;&#47; a task in a non-blocking fashion. i.e. the function should &#42;not&#42; synchronously compute&#10; &#47;&#47;&#47; a value and then return it using Task.FromResult. Instead, it should return an actual&#10; &#47;&#47;&#47; task that operates asynchronously. If this function synchronously computes a value&#10; &#47;&#47;&#47; then that will cause locks to be held in this type for excessive periods of time.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public AsyncLazy&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, bool cacheResult&#41;&#10; &#58; this&#40;asynchronousComputeFunction, synchronousComputeFunction&#58; null, cacheResult&#58; cacheResult&#41;&#10; &#123;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Creates an AsyncLazy that supports both asynchronous computation and inline synchronous&#10; &#47;&#47;&#47; computation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;asynchronousComputeFunction&#34;&#62;A function called to start the asynchronous&#10; &#47;&#47;&#47; computation. This function should be cheap and non-blocking.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;synchronousComputeFunction&#34;&#62;A function to do the work synchronously, which&#10; &#47;&#47;&#47; is allowed to block. This function should not be implemented by a simple Wait on the&#10; &#47;&#47;&#47; asynchronous value. If that&#39;s all you are doing, just don&#39;t pass a synchronous function&#10; &#47;&#47;&#47; in the first place.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;cacheResult&#34;&#62;Whether the result should be cached once the computation is&#10; &#47;&#47;&#47; complete.&#60;&#47;param&#62;&#10; public AsyncLazy&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, Func&#60;CancellationToken, T&#62; synchronousComputeFunction, bool cacheResult&#41;&#10; &#123;&#10; this.asynchronousComputeFunction &#61; asynchronousComputeFunction&#59;&#10; this.synchronousComputeFunction &#61; synchronousComputeFunction&#59;&#10; this.cacheResult &#61; cacheResult&#59;&#10; &#125;&#10;&#10; &#35;region Lock Wrapper for Invariant Checking&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Takes the lock for this object and if acquired validates the invariants of this class.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private WaitThatValidatesInvariants TakeLock&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; gate.Wait&#40;cancellationToken&#41;&#59;&#10; AssertInvariants_NoLock&#40;&#41;&#59;&#10; return new WaitThatValidatesInvariants&#40;this&#41;&#59;&#10; &#125;&#10;&#10; private struct WaitThatValidatesInvariants &#58; IDisposable&#10; &#123;&#10; private readonly AsyncLazy&#60;T&#62; asyncLazy&#59;&#10;&#10; public WaitThatValidatesInvariants&#40;AsyncLazy&#60;T&#62; asyncLazy&#41;&#10; &#123;&#10; this.asyncLazy &#61; asyncLazy&#59;&#10; &#125;&#10;&#10; public void Dispose&#40;&#41;&#10; &#123;&#10; asyncLazy.AssertInvariants_NoLock&#40;&#41;&#59;&#10; gate.Release&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void AssertInvariants_NoLock&#40;&#41;&#10; &#123;&#10; &#47;&#10; &#125;&#10;&#10; &#35;endregion&#10;&#10; public bool TryGetValue&#40;out T result&#41;&#10; &#123;&#10; &#47;&#47; No need to lock here since this is only a fast check to &#10; &#47;&#47; see if the result is already computed.&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; result &#61; cachedResult.Result&#59;&#10; return true&#59;&#10; &#125;&#10;&#10; result &#61; default&#40;T&#41;&#59;&#10; return false&#59;&#10; &#125;&#10;&#10; public T GetValue&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; Request request &#61; null&#59;&#10; AsynchronousComputationToStart&#63; newAsynchronousComputation &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If cached, get immediately&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return cachedResult.Result&#59;&#10; &#125;&#10;&#10; &#47;&#47; If there is an existing computation active, we&#39;ll just create another request&#10; if &#40;computationActive&#41;&#10; &#123;&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10; &#125;&#10; else if &#40;synchronousComputeFunction &#61;&#61; null&#41;&#10; &#123;&#10; &#47;&#47; A synchronous request, but we have no synchronous function. Start off the async work&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10;&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; else&#10; &#123;&#10; &#47;&#47; We will do the computation here&#10; this.computationActive &#61; true&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; If we simply created a new asynchronous request, so wait for it. Yes, we&#39;re blocking the thread&#10; &#47;&#47; but we don&#39;t want multiple threads attempting to compute the same thing.&#10; if &#40;request &#33;&#61; null&#41;&#10; &#123;&#10; request.RegisterForCancellation&#40;OnAsynchronousRequestCancelled, cancellationToken&#41;&#59;&#10;&#10; &#47;&#47; Since we already registered for cancellation, it&#39;s possible that the registration has&#10; &#47;&#47; cancelled this new computation if we were the only requestor.&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; request&#41;&#59;&#10; &#125;&#10;&#10; return request.Task.Result&#59;&#10; &#125;&#10; else&#10; &#123;&#10; T result&#59;&#10;&#10; &#47;&#47; We are the active computation, so let&#39;s go ahead and compute.&#10; try&#10; &#123;&#10; result &#61; synchronousComputeFunction&#40;cancellationToken&#41;&#59;&#10; &#125;&#10; catch &#40;OperationCanceledException&#41;&#10; &#123;&#10; &#47;&#47; This cancelled for some reason. We don&#39;t care why, but&#10; &#47;&#47; it means anybody else waiting for this result isn&#39;t going to get it&#10; &#47;&#47; from us.&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; this.computationActive &#61; false&#59;&#10;&#10; if &#40;requests &#33;&#61; null&#41;&#10; &#123;&#10; &#47;&#47; There&#39;s a possible improvement here&#58; there might be another synchronous caller who&#10; &#47;&#47; also wants the value. We might consider stealing their thread rather than punting&#10; &#47;&#47; to the thread pool.&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; null&#41;&#59;&#10; &#125;&#10;&#10; throw&#59;&#10; &#125;&#10; catch &#40;Exception ex&#41;&#10; &#123;&#10; &#47;&#47; We faulted for some unknown reason. We should simply fault everything.&#10; TaskCompletionSource&#60;T&#62; tcs &#61; new TaskCompletionSource&#60;T&#62;&#40;&#41;&#59;&#10; tcs.SetException&#40;ex&#41;&#59;&#10; CompleteWithTask&#40;tcs.Task, CancellationToken.None&#41;&#59;&#10;&#10; throw&#59;&#10; &#125;&#10;&#10; &#47;&#47; We have a value, so complete&#10; CompleteWithTask&#40;new Task&#40;&#40;&#41;&#61;&#62; result&#41;, CancellationToken.None&#41;&#59;&#10;&#10; return result&#59;&#10; &#125;&#10; &#125;&#10;&#10; private Request CreateNewRequest_NoLock&#40;&#41;&#10; &#123;&#10; if &#40;this.requests &#61;&#61; null&#41;&#10; &#123;&#10; this.requests &#61; new HashSet&#60;Request&#62;&#40;&#41;&#59;&#10; &#125;&#10;&#10; Request request &#61; new Request&#40;&#41;&#59;&#10; this.requests.Add&#40;request&#41;&#59;&#10; return request&#59;&#10; &#125;&#10;&#10; public Task&#60;T&#62; GetValueAsync&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; &#47;&#47; Optimization&#58; if we&#39;re already cancelled, do not pass go&#10; if &#40;cancellationToken.IsCancellationRequested&#41;&#10; &#123;&#10; return new Task&#60;T&#62;&#40;&#40;&#41; &#61;&#62; default&#40;T&#41;, cancellationToken&#41;&#59;&#10; &#125;&#10;&#10; Request request&#59;&#10; AsynchronousComputationToStart&#63; newAsynchronousComputation &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If cached, get immediately&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return cachedResult&#59;&#10; &#125;&#10;&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10;&#10; &#47;&#47; If we have either synchronous or asynchronous work current in flight, we don&#39;t need to do anything.&#10; &#47;&#47; Otherwise, we shall start an asynchronous computation for this&#10; if &#40;&#33;computationActive&#41;&#10; &#123;&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; We now have the request counted for, register for cancellation. It is critical this is&#10; &#47;&#47; done outside the lock, as our reigstration may immediately fire and we want to avoid the&#10; &#47;&#47; reentrancy&#10; request.RegisterForCancellation&#40;OnAsynchronousRequestCancelled, cancellationToken&#41;&#59;&#10;&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; request&#41;&#59;&#10; &#125;&#10;&#10; return request.Task&#59;&#10; &#125;&#10;&#10; private AsynchronousComputationToStart RegisterAsynchronousComputation_NoLock&#40;&#41;&#10; &#123;&#10; Contract.ThrowIfTrue&#40;this.computationActive&#41;&#59;&#10;&#10; this.asynchronousComputationCancellationSource &#61; new CancellationTokenSource&#40;&#41;&#59;&#10; this.computationActive &#61; true&#59;&#10;&#10; return new AsynchronousComputationToStart&#40;this.asynchronousComputeFunction, this.asynchronousComputationCancellationSource&#41;&#59;&#10; &#125;&#10;&#10; private struct AsynchronousComputationToStart&#10; &#123;&#10; public readonly Func&#60;CancellationToken, Task&#60;T&#62;&#62; AsynchronousComputeFunction&#59;&#10; public readonly CancellationTokenSource CancellationTokenSource&#59;&#10;&#10; public AsynchronousComputationToStart&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, CancellationTokenSource cancellationTokenSource&#41;&#10; &#123;&#10; this.AsynchronousComputeFunction &#61; asynchronousComputeFunction&#59;&#10; this.CancellationTokenSource &#61; cancellationTokenSource&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void StartAsynchronousComputation&#40;AsynchronousComputationToStart computationToStart, Request requestToCompleteSynchronously&#41;&#10; &#123;&#10; var cancellationToken &#61; computationToStart.CancellationTokenSource.Token&#59;&#10;&#10; try&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; DO NOT ACCESS ANY FIELDS OR STATE BEYOND THIS POINT. Since this function&#10; &#47;&#47; runs unsynchronized, it&#39;s possible that during this function this request&#10; &#47;&#47; might be cancelled, and then a whole additional request might start and&#10; &#47;&#47; complete inline, and cache the result. By grabbing state before we check&#10; &#47;&#47; the cancellation token, we can be assured that we are only operating on&#10; &#47;&#47; a state that was complete.&#10;&#10; ExceptionUtilities.ExecuteWithErrorReporting&#40;&#40;&#41; &#61;&#62;&#10; &#123;&#10; &#47;&#47; We avoid creating a full closure just to pass the token along&#10; &#47;&#47; Also, use TaskContinuationOptions.ExecuteSynchronously so that we inline &#10; &#47;&#47; the continuation if asynchronousComputeFunction completes synchronously&#10; var task &#61; computationToStart.AsynchronousComputeFunction&#40;cancellationToken&#41;&#59;&#10;&#10; task.ContinueWith&#40;&#10; &#40;t, s&#41; &#61;&#62; CompleteWithTask&#40;t, &#40;&#40;CancellationTokenSource&#41;s&#41;.Token&#41;,&#10; computationToStart.CancellationTokenSource,&#10; cancellationToken,&#10; TaskContinuationOptions.ExecuteSynchronously,&#10; TaskScheduler.Default&#41;&#59;&#10;&#10; if &#40;requestToCompleteSynchronously &#33;&#61; null &#38;&#38; task.IsCompleted&#41;&#10; &#123;&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; task &#61; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;task&#41;&#59;&#10; &#125;&#10;&#10; requestToCompleteSynchronously.CompleteFromTaskSynchronously&#40;task&#41;&#59;&#10; &#125;&#10; &#125;&#41;&#59;&#10; &#125;&#10; catch &#40;OperationCanceledException oce&#41;&#10; &#123;&#10; &#47;&#47; As long as it&#39;s the right token, this means that our thread was the first thread&#10; &#47;&#47; to start an asynchronous computation, but the requestor cancelled as we were starting up&#10; &#47;&#47; the computation.&#10; if &#40;oce.CancellationToken &#33;&#61; cancellationToken&#41;&#10; &#123;&#10; ExceptionUtilities.FailFast&#40;oce&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; private void CompleteWithTask&#40;Task&#60;T&#62; task, CancellationToken cancellationToken&#41;&#10; &#123;&#10; IEnumerable&#60;Request&#62; requestsToComplete&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If the underlying computation was cancelled, then all state was already updated in OnAsynchronousRequestCancelled&#10; &#47;&#47; and there is no new work to do here. We &#42;must&#42; use the local one since this completion may be running far after&#10; &#47;&#47; the background computation was cancelled and a new one might have already been enqueued. We must do this&#10; &#47;&#47; check here under the lock to ensure proper synchronization with OnAsynchronousRequestCancelled.&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; The computation is complete, so get all requests to complete and null out the list. We&#39;ll create another one&#10; &#47;&#47; later if it&#39;s needed&#10; requestsToComplete &#61; this.requests &#63;&#63; SpecializedCollections.EmptyEnumerable&#60;Request&#62;&#40;&#41;&#59;&#10; this.requests &#61; null&#59;&#10;&#10; &#47;&#47; The computations are done&#10; this.asynchronousComputationCancellationSource &#61; null&#59;&#10; this.computationActive &#61; false&#59;&#10; task &#61; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;task&#41;&#59;&#10; &#125;&#10;&#10; foreach &#40;var requestToComplete in requestsToComplete&#41;&#10; &#123;&#10; requestToComplete.CompleteFromTaskAsynchronously&#40;task&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; private Task&#60;T&#62; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; if &#40;this.cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return this.cachedResult&#59;&#10; &#125;&#10; else&#10; &#123;&#10; if &#40;cacheResult &#38;&#38; task.Status &#61;&#61; TaskStatus.RanToCompletion&#41;&#10; &#123;&#10; &#47;&#47; Hold onto the completed task. We can get rid of the computation functions for good&#10; this.cachedResult &#61; task&#59;&#10; this.asynchronousComputeFunction &#61; null&#59;&#10; this.synchronousComputeFunction &#61; null&#59;&#10; &#125;&#10;&#10; return task&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void OnAsynchronousRequestCancelled&#40;object state&#41;&#10; &#123;&#10; var request &#61; &#40;Request&#41;state&#59;&#10; CancellationTokenSource cancellationTokenSource &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; &#47;&#47; Now try to remove it. It&#39;s possible that requests may already be null. You could&#10; &#47;&#47; imagine that cancellation was requested, but before we could aquire the lock&#10; &#47;&#47; here the computation completed and the entire CompleteWithTask synchronized&#10; &#47;&#47; block ran. In that case, the requests collection may already be null, or it&#10; &#47;&#47; &#40;even scarier&#33;&#41; may have been replaced with another collection because another&#10; &#47;&#47; computation has started.&#10; if &#40;this.requests &#33;&#61; null&#41;&#10; &#123;&#10; if &#40;this.requests.Remove&#40;request&#41;&#41;&#10; &#123;&#10; if &#40;this.requests.Count &#61;&#61; 0&#41;&#10; &#123;&#10; this.requests &#61; null&#59;&#10;&#10; if &#40;this.asynchronousComputationCancellationSource &#33;&#61; null&#41;&#10; &#123;&#10; cancellationTokenSource &#61; this.asynchronousComputationCancellationSource&#59;&#10; this.asynchronousComputationCancellationSource &#61; null&#59;&#10; this.computationActive &#61; false&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; request.CancelAsynchronously&#40;&#41;&#59;&#10;&#10; if &#40;cancellationTokenSource &#33;&#61; null&#41;&#10; &#123;&#10; cancellationTokenSource.Cancel&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; Using inheritance instead of wrapping a TaskCompletionSource to avoid a second allocation&#10; private class Request &#58; TaskCompletionSource&#60;T&#62;&#10; &#123;&#10; private CancellationTokenRegistration cancellationTokenRegistration&#59;&#10;&#10; public void RegisterForCancellation&#40;Action&#60;object&#62; callback, CancellationToken cancellationToken&#41;&#10; &#123;&#10; cancellationTokenRegistration &#61; cancellationToken.Register&#40;callback, this&#41;&#59;&#10; &#125;&#10;&#10; public void CompleteFromTaskAsynchronously&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; System.Threading.Tasks.Task.Factory.StartNew&#40;CompleteFromTaskSynchronouslyStub, task, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default&#41;&#59;&#10; &#125;&#10;&#10; private void CompleteFromTaskSynchronouslyStub&#40;object task&#41;&#10; &#123;&#10; CompleteFromTaskSynchronously&#40;&#40;Task&#60;T&#62;&#41;task&#41;&#59;&#10; &#125;&#10;&#10; public void CompleteFromTaskSynchronously&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; if &#40;task.Status &#61;&#61; TaskStatus.RanToCompletion&#41;&#10; &#123;&#10; if &#40;TrySetResult&#40;task.Result&#41;&#41;&#10; &#123;&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; else if &#40;task.Status &#61;&#61; TaskStatus.Faulted&#41;&#10; &#123;&#10; if &#40;TrySetException&#40;task.Exception&#41;&#41;&#10; &#123;&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; else&#10; &#123;&#10; CancelSynchronously&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; public void CancelAsynchronously&#40;&#41;&#10; &#123;&#10; &#47;&#47; Since there could be synchronous continuations on the TaskCancellationSource, we queue this to the threadpool&#10; &#47;&#47; to avoid inline running of other operations.&#10; System.Threading.Tasks.Task.Factory.StartNew&#40;CancelSynchronously, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default&#41;&#59;&#10; &#125;&#10;&#10; private void CancelSynchronously&#40;&#41;&#10; &#123;&#10; if &#40;TrySetCanceled&#40;&#41;&#41;&#10; &#123;&#10; &#47;&#47; Paranoia&#58; the only reason we should ever get here is if the CancellationToken that&#10; &#47;&#47; we registered against was cancelled, but just in case, dispose the registration&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; A lightweight mutual exclusion object which supports waiting with cancellation and prevents&#10; &#47;&#47;&#47; recursion &#40;i.e. you may not call Wait if you already hold the lock&#41;&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; The &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62; provides a lightweight mutual exclusion class that doesn&#39;t&#10; &#47;&#47;&#47; use Windows kernel synchronization primitives.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; The implementation is distilled from the workings of &#60;see cref&#61;&#34;T&#58;System.Threading.SemaphoreSlim&#34;&#47;&#62;&#10; &#47;&#47;&#47; The basic idea is that we use a regular sync object &#40;Monitor.Enter&#47;Exit&#41; to guard the setting&#10; &#47;&#47;&#47; of an &#39;owning thread&#39; field. If, during the Wait, we find the lock is held by someone else&#10; &#47;&#47;&#47; then we register a cancellation callback and enter a &#34;Monitor.Wait&#34; loop. If the cancellation&#10; &#47;&#47;&#47; callback fires, then it &#34;pulses&#34; all the waiters to wake them up and check for cancellation.&#10; &#47;&#47;&#47; Waiters are also &#34;pulsed&#34; when leaving the lock.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; All public members of &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62; are thread-safe and may be used concurrently&#10; &#47;&#47;&#47; from multiple threads.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; internal sealed class NonReentrantLock&#10; &#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; A synchronization object to protect access to the &#60;see cref&#61;&#34;F&#58;owningThread&#34;&#47;&#62; field and to be pulsed&#10; &#47;&#47;&#47; when &#60;see cref&#61;&#34;M&#58;Release&#34;&#47;&#62; is called and during cancellation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private readonly object syncLock&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Indicates which thread currently holds the lock. If null, then the lock is available.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private volatile Thread owningThread&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Constructor.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;useThisInstanceForSynchronization&#34;&#62;If false &#40;the default&#41;, then the class&#10; &#47;&#47;&#47; allocates an internal object to be used as a sync lock.&#10; &#47;&#47;&#47; If true, then the sync lock object will be the NonReentrantLock instance itself. This&#10; &#47;&#47;&#47; saves an allocation but a client may not safely further use this instance in a call to&#10; &#47;&#47;&#47; Monitor.Enter&#47;Exit or in a &#34;lock&#34; statement.&#10; &#47;&#47;&#47; &#60;&#47;param&#62;&#10; public NonReentrantLock&#40;bool useThisInstanceForSynchronization &#61; false&#41;&#10; &#123;&#10; this.syncLock &#61; useThisInstanceForSynchronization &#63; this &#58; new object&#40;&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Shared factory for use in lazy initialization.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public static readonly Func&#60;NonReentrantLock&#62; Factory &#61; &#40;&#41; &#61;&#62; new NonReentrantLock&#40;useThisInstanceForSynchronization&#58; true&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Blocks the current thread until it can enter the &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62;, while observing a&#10; &#47;&#47;&#47; &#60;see cref&#61;&#34;T&#58;System.Threading.CancellationToken&#34;&#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; Recursive locking is not supported. i.e. A thread may not call Wait successfully twice without an&#10; &#47;&#47;&#47; intervening &#60;see cref&#61;&#34;Release&#34;&#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;cancellationToken&#34;&#62;The &#60;see cref&#61;&#34;T&#58;System.Threading.CancellationToken&#34;&#47;&#62; token to&#10; &#47;&#47;&#47; observe.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;System.OperationCanceledException&#34;&#62;&#60;paramref name&#61;&#34;cancellationToken&#34;&#47;&#62; was&#10; &#47;&#47;&#47; canceled.&#60;&#47;exception&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;System.LockRecursionException&#34;&#62;The caller already holds the lock&#60;&#47;exception&#62;&#10; public void Wait&#40;CancellationToken cancellationToken &#61; default&#40;CancellationToken&#41;&#41;&#10; &#123;&#10; if &#40;this.IsOwnedByMe&#41;&#10; &#123;&#10; throw new LockRecursionException&#40;&#41;&#59;&#10; &#125;&#10;&#10; CancellationTokenRegistration cancellationTokenRegistration &#61; default&#40;CancellationTokenRegistration&#41;&#59;&#10;&#10; if &#40;cancellationToken.CanBeCanceled&#41;&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; Fast path to try and avoid allocations in callback registration.&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; if &#40;&#33;this.IsLocked&#41;&#10; &#123;&#10; this.TakeOwnership&#40;&#41;&#59;&#10; return&#59;&#10; &#125;&#10; &#125;&#10;&#10; cancellationTokenRegistration &#61; cancellationToken.Register&#40;cancellationTokenCanceledEventHandler, this.syncLock, useSynchronizationContext&#58; false&#41;&#59;&#10; &#125;&#10;&#10; using &#40;cancellationTokenRegistration&#41;&#10; &#123;&#10; &#47;&#47; PERF&#58; First spin wait for the lock to become available, but only up to the first planned yield.&#10; &#47;&#47; This additional amount of spinwaiting was inherited from SemaphoreSlim&#39;s implementation where&#10; &#47;&#47; it showed measurable perf gains in test scenarios.&#10; SpinWait spin &#61; new SpinWait&#40;&#41;&#59;&#10; while &#40;this.IsLocked &#38;&#38; &#33;spin.NextSpinWillYield&#41;&#10; &#123;&#10; spin.SpinOnce&#40;&#41;&#59;&#10; &#125;&#10;&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; while &#40;this.IsLocked&#41;&#10; &#123;&#10; &#47;&#47; If cancelled, we throw. Trying to wait could lead to deadlock.&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; using &#40;Logger.LogBlock&#40;FeatureId.WaitIndicator, FunctionId.Utilities_NonReentrantLock_BlockingWait, cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; Another thread holds the lock. Wait until we get awoken either&#10; &#47;&#47; by some code calling &#34;Release&#34; or by cancellation.&#10; Monitor.Wait&#40;this.syncLock&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; We now hold the lock&#10; this.TakeOwnership&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Exit the mutual exclusion.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; The calling thread must currently hold the lock.&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;Roslyn.Utilities.ContractFailureException&#34;&#62;The lock is not currently held by the calling thread.&#60;&#47;exception&#62;&#10; public void Release&#40;&#41;&#10; &#123;&#10; AssertHasLock&#40;&#41;&#59;&#10;&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; this.ReleaseOwnership&#40;&#41;&#59;&#10;&#10; &#47;&#47; Release one waiter&#10; Monitor.Pulse&#40;this.syncLock&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Determine if the lock is currently held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;returns&#62;True if the lock is currently held by the calling thread.&#60;&#47;returns&#62;&#10; public bool LockHeldByMe&#40;&#41;&#10; &#123;&#10; return this.IsOwnedByMe&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Throw an exception if the lock is not held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;Roslyn.Utilities.ContractFailureException&#34;&#62;The lock is not currently held by the calling thread.&#60;&#47;exception&#62;&#10; public void AssertHasLock&#40;&#41;&#10; &#123;&#10; Contract.ThrowIfFalse&#40;LockHeldByMe&#40;&#41;&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Checks if the lock is currently held.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool IsLocked&#10; &#123;&#10; get&#10; &#123;&#10; return this.owningThread &#33;&#61; null&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Checks if the lock is currently held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool IsOwnedByMe&#10; &#123;&#10; get&#10; &#123;&#10; return this.owningThread &#61;&#61; Thread.CurrentThread&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Take ownership of the lock &#40;by the calling thread&#41;. The lock may not already&#10; &#47;&#47;&#47; be held by any other code.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private void TakeOwnership&#40;&#41;&#10; &#123;&#10; Contract.Assert&#40;&#33;this.IsLocked&#41;&#59;&#10; this.owningThread &#61; Thread.CurrentThread&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Release ownership of the lock. The lock must already be held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private void ReleaseOwnership&#40;&#41;&#10; &#123;&#10; Contract.Assert&#40;this.IsOwnedByMe&#41;&#59;&#10; this.owningThread &#61; null&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Action object passed to a cancellation token registration.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private static readonly Action&#60;object&#62; cancellationTokenCanceledEventHandler &#61; new Action&#60;object&#62;&#40;CancellationTokenCanceledEventHandler&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Callback executed when a cancellation token is canceled during a Wait.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;obj&#34;&#62;The syncLock that protects a &#60;see cref&#61;&#34;NonReentrantLock &#34;&#47;&#62; instance.&#60;&#47;param&#62;&#10; private static void CancellationTokenCanceledEventHandler&#40;object obj&#41;&#10; &#123;&#10; lock &#40;obj&#41;&#10; &#123;&#10; &#47;&#47; Release all waiters to check their cancellation tokens.&#10; Monitor.PulseAll&#40;obj&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#125;</pre>2013-05-23T06:05:29-07:00phttp://rise4fun.com/Seal/pSeal/p<pre>using System&#59;&#10;public class TestHeapCloning&#123;&#10; TestHeapCloning f&#59;&#10; int h&#59;&#10; public void foo&#40;TestHeapCloning param&#41;&#123;&#10; TestHeapCloning local1 &#61; bar&#40;&#41;&#59;&#10; TestHeapCloning local2 &#61; bar&#40;&#41;&#59;&#10; local1.f &#61; param&#59;&#10; local2.f &#61; new TestHeapCloning&#40;&#41;&#59;&#10; local2.f.h &#61; 1&#59;&#10; &#125;&#10; public TestHeapCloning bar&#40;&#41;&#123;&#10; TestHeapCloning r &#61; new TestHeapCloning&#40;&#41;&#59;&#10; return r&#59;&#10; &#125;&#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00plohttp://rise4fun.com/Seal/ploSeal/plo<pre>using System&#59;&#10;class Test &#123;&#10; public Test f &#61; null&#59;&#10; public void ImpureMethod&#40;Test param&#41;&#123;&#10; if &#40;2 &#43; 2 &#61;&#61; 4&#41;&#10; param.f &#61; null&#59;&#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00plGhttp://rise4fun.com/Seal/plGSeal/plG<pre>using System&#59;&#10;class Test &#123;&#10; public Test f &#61; null&#59;&#10; public void PureMethod&#40;Test param&#41;&#123;&#10; var temp &#61; new Test&#40;&#41;&#59;&#10; temp.f &#61; param&#59;&#10; return temp.f&#59; &#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00P3http://rise4fun.com/Seal/P3Seal/P3<pre>&#10;using System&#59;&#10;using System.Collections.Generic&#59;&#10;using System.Runtime.CompilerServices&#59;&#10;using System.Threading&#59;&#10;using System.Threading.Tasks&#59;&#10;&#10;namespace Roslyn.Utilities&#10;&#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Represents a value that can be retrieved synchronously or asynchronously by many clients.&#10; &#47;&#47;&#47; The value will be computed on-demand the moment the first client asks for it. While being&#10; &#47;&#47;&#47; computed, more clients can request the value. As long as there are outstanding clients the&#10; &#47;&#47;&#47; underlying computation will proceed. If all outstanding clients cancel their request then&#10; &#47;&#47;&#47; the underlying value computation will be cancelled as well.&#10; &#47;&#47;&#47; &#10; &#47;&#47;&#47; Creators of an &#60;see cref&#61;&#34;AsyncLazy&#123;T&#125;&#34; &#47;&#62; can specify whether the result of the computation is&#10; &#47;&#47;&#47; cached for future requests or not. Choosing to not cache means the computation functions are kept&#10; &#47;&#47;&#47; alive, whereas caching means the value &#40;but not functions&#41; are kept alive once complete.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; internal sealed class AsyncLazy&#60;T&#62;&#10; &#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The underlying function that starts an asynchronous computation of the resulting value.&#10; &#47;&#47;&#47; Null&#39;ed out once we&#39;ve computed the result and we&#39;ve been asked to cache it. Otherwise,&#10; &#47;&#47;&#47; it is kept around in case the value needs to be computed again.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The underlying function that starts an synchronous computation of the resulting value.&#10; &#47;&#47;&#47; Null&#39;ed out once we&#39;ve computed the result and we&#39;ve been asked to cache it, or if we&#10; &#47;&#47;&#47; didn&#39;t get any synchronous function given to us in the first place.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Func&#60;CancellationToken, T&#62; synchronousComputeFunction&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Whether or not we should keep the value around once we&#39;ve computed it.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private readonly bool cacheResult&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The Task that holds the cached result.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Task&#60;T&#62; cachedResult&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Mutex used to protect reading and writing to all mutable objects and fields. Traces&#10; &#47;&#47;&#47; indicate that there&#39;s negligible contention on this lock, hence we can save some memory&#10; &#47;&#47;&#47; by using a single lock for all AsyncLazy instances. Only trivial and non-reentrant work&#10; &#47;&#47;&#47; should be done while holding the lock.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private static readonly NonReentrantLock gate &#61; new NonReentrantLock&#40;useThisInstanceForSynchronization&#58; true&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The hash set of all currently outstanding asynchronous requests. Null if there are no requests,&#10; &#47;&#47;&#47; and will never be empty.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private HashSet&#60;Request&#62; requests &#61; null&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; If an asynchronous request is active, the CancellationTokenSource that allows for&#10; &#47;&#47;&#47; cancelling the underlying computation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private CancellationTokenSource asynchronousComputationCancellationSource &#61; null&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Whether a computation is active or queued on any thread, whether synchronous or&#10; &#47;&#47;&#47; asynchronous.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool computationActive &#61; false&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Creates an AsyncLazy that always returns the value, analogous to &#60;see cref&#61;&#34;Task.FromResult&#123;T&#125;&#34; &#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public AsyncLazy&#40;T value&#41;&#10; &#123;&#10; this.cacheResult &#61; true&#59;&#10; this.cachedResult &#61; new Task&#40;&#40;&#41;&#61;&#62;value&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Important&#58; callers of this constructor should ensure that the compute function returns&#10; &#47;&#47;&#47; a task in a non-blocking fashion. i.e. the function should &#42;not&#42; synchronously compute&#10; &#47;&#47;&#47; a value and then return it using Task.FromResult. Instead, it should return an actual&#10; &#47;&#47;&#47; task that operates asynchronously. If this function synchronously computes a value&#10; &#47;&#47;&#47; then that will cause locks to be held in this type for excessive periods of time.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public AsyncLazy&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, bool cacheResult&#41;&#10; &#58; this&#40;asynchronousComputeFunction, synchronousComputeFunction&#58; null, cacheResult&#58; cacheResult&#41;&#10; &#123;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Creates an AsyncLazy that supports both asynchronous computation and inline synchronous&#10; &#47;&#47;&#47; computation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;asynchronousComputeFunction&#34;&#62;A function called to start the asynchronous&#10; &#47;&#47;&#47; computation. This function should be cheap and non-blocking.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;synchronousComputeFunction&#34;&#62;A function to do the work synchronously, which&#10; &#47;&#47;&#47; is allowed to block. This function should not be implemented by a simple Wait on the&#10; &#47;&#47;&#47; asynchronous value. If that&#39;s all you are doing, just don&#39;t pass a synchronous function&#10; &#47;&#47;&#47; in the first place.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;cacheResult&#34;&#62;Whether the result should be cached once the computation is&#10; &#47;&#47;&#47; complete.&#60;&#47;param&#62;&#10; public AsyncLazy&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, Func&#60;CancellationToken, T&#62; synchronousComputeFunction, bool cacheResult&#41;&#10; &#123;&#10; Contract.ThrowIfNull&#40;asynchronousComputeFunction&#41;&#59;&#10;&#10; this.asynchronousComputeFunction &#61; asynchronousComputeFunction&#59;&#10; this.synchronousComputeFunction &#61; synchronousComputeFunction&#59;&#10; this.cacheResult &#61; cacheResult&#59;&#10; &#125;&#10;&#10; &#35;region Lock Wrapper for Invariant Checking&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Takes the lock for this object and if acquired validates the invariants of this class.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private WaitThatValidatesInvariants TakeLock&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; gate.Wait&#40;cancellationToken&#41;&#59;&#10; AssertInvariants_NoLock&#40;&#41;&#59;&#10; return new WaitThatValidatesInvariants&#40;this&#41;&#59;&#10; &#125;&#10;&#10; private struct WaitThatValidatesInvariants &#58; IDisposable&#10; &#123;&#10; private readonly AsyncLazy&#60;T&#62; asyncLazy&#59;&#10;&#10; public WaitThatValidatesInvariants&#40;AsyncLazy&#60;T&#62; asyncLazy&#41;&#10; &#123;&#10; this.asyncLazy &#61; asyncLazy&#59;&#10; &#125;&#10;&#10; public void Dispose&#40;&#41;&#10; &#123;&#10; asyncLazy.AssertInvariants_NoLock&#40;&#41;&#59;&#10; gate.Release&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void AssertInvariants_NoLock&#40;&#41;&#10; &#123;&#10; &#47;&#47; Invariant &#35;1&#58; thou shalt never have an asynchronous computation running without it&#10; &#47;&#47; being considered a computation&#10; Contract.ThrowIfTrue&#40;this.asynchronousComputationCancellationSource &#33;&#61; null &#38;&#38;&#10; &#33;this.computationActive&#41;&#59;&#10;&#10; &#47;&#47; Invariant &#35;2&#58; thou shalt never waste memory holding onto empty HashSets&#10; Contract.ThrowIfTrue&#40;this.requests &#33;&#61; null &#38;&#38;&#10; this.requests.Count &#61;&#61; 0&#41;&#59;&#10;&#10; &#47;&#47; Invariant &#35;3&#58; thou shalt never have an request if there is not&#10; &#47;&#47; something trying to compute it&#10; Contract.ThrowIfTrue&#40;this.requests &#33;&#61; null &#38;&#38;&#10; &#33;this.computationActive&#41;&#59;&#10;&#10; &#47;&#47; Invariant &#35;4&#58; thou shalt never have a cached value and any computation function&#10; Contract.ThrowIfTrue&#40;this.cachedResult &#33;&#61; null &#38;&#38;&#10; &#40;this.synchronousComputeFunction &#33;&#61; null &#124;&#124; this.asynchronousComputeFunction &#33;&#61; null&#41;&#41;&#59;&#10;&#10; &#47;&#47; Invariant &#35;5&#58; thou shalt never have a synchronous computation function but not an&#10; &#47;&#47; asynchronous one&#10; Contract.ThrowIfTrue&#40;this.asynchronousComputeFunction &#61;&#61; null &#38;&#38; this.synchronousComputeFunction &#33;&#61; null&#41;&#59;&#10; &#125;&#10;&#10; &#35;endregion&#10;&#10; public bool TryGetValue&#40;out T result&#41;&#10; &#123;&#10; &#47;&#47; No need to lock here since this is only a fast check to &#10; &#47;&#47; see if the result is already computed.&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; result &#61; cachedResult.Result&#59;&#10; return true&#59;&#10; &#125;&#10;&#10; result &#61; default&#40;T&#41;&#59;&#10; return false&#59;&#10; &#125;&#10;&#10; public T GetValue&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; Request request &#61; null&#59;&#10; AsynchronousComputationToStart&#63; newAsynchronousComputation &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If cached, get immediately&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return cachedResult.Result&#59;&#10; &#125;&#10;&#10; &#47;&#47; If there is an existing computation active, we&#39;ll just create another request&#10; if &#40;computationActive&#41;&#10; &#123;&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10; &#125;&#10; else if &#40;synchronousComputeFunction &#61;&#61; null&#41;&#10; &#123;&#10; &#47;&#47; A synchronous request, but we have no synchronous function. Start off the async work&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10;&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; else&#10; &#123;&#10; &#47;&#47; We will do the computation here&#10; this.computationActive &#61; true&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; If we simply created a new asynchronous request, so wait for it. Yes, we&#39;re blocking the thread&#10; &#47;&#47; but we don&#39;t want multiple threads attempting to compute the same thing.&#10; if &#40;request &#33;&#61; null&#41;&#10; &#123;&#10; request.RegisterForCancellation&#40;OnAsynchronousRequestCancelled, cancellationToken&#41;&#59;&#10;&#10; &#47;&#47; Since we already registered for cancellation, it&#39;s possible that the registration has&#10; &#47;&#47; cancelled this new computation if we were the only requestor.&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; request&#41;&#59;&#10; &#125;&#10;&#10; return request.Task.WaitAndGetResult&#40;cancellationToken&#41;&#59;&#10; &#125;&#10; else&#10; &#123;&#10; T result&#59;&#10;&#10; &#47;&#47; We are the active computation, so let&#39;s go ahead and compute.&#10; try&#10; &#123;&#10; result &#61; synchronousComputeFunction&#40;cancellationToken&#41;&#59;&#10; &#125;&#10; catch &#40;OperationCanceledException&#41;&#10; &#123;&#10; &#47;&#47; This cancelled for some reason. We don&#39;t care why, but&#10; &#47;&#47; it means anybody else waiting for this result isn&#39;t going to get it&#10; &#47;&#47; from us.&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; this.computationActive &#61; false&#59;&#10;&#10; if &#40;requests &#33;&#61; null&#41;&#10; &#123;&#10; &#47;&#47; There&#39;s a possible improvement here&#58; there might be another synchronous caller who&#10; &#47;&#47; also wants the value. We might consider stealing their thread rather than punting&#10; &#47;&#47; to the thread pool.&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; null&#41;&#59;&#10; &#125;&#10;&#10; throw&#59;&#10; &#125;&#10; catch &#40;Exception ex&#41;&#10; &#123;&#10; &#47;&#47; We faulted for some unknown reason. We should simply fault everything.&#10; TaskCompletionSource&#60;T&#62; tcs &#61; new TaskCompletionSource&#60;T&#62;&#40;&#41;&#59;&#10; tcs.SetException&#40;ex&#41;&#59;&#10; CompleteWithTask&#40;tcs.Task, CancellationToken.None&#41;&#59;&#10;&#10; throw&#59;&#10; &#125;&#10;&#10; &#47;&#47; We have a value, so complete&#10; CompleteWithTask&#40;Task.FromResult&#40;result&#41;, CancellationToken.None&#41;&#59;&#10;&#10; return result&#59;&#10; &#125;&#10; &#125;&#10;&#10; private Request CreateNewRequest_NoLock&#40;&#41;&#10; &#123;&#10; if &#40;this.requests &#61;&#61; null&#41;&#10; &#123;&#10; this.requests &#61; new HashSet&#60;Request&#62;&#40;&#41;&#59;&#10; &#125;&#10;&#10; Request request &#61; new Request&#40;&#41;&#59;&#10; this.requests.Add&#40;request&#41;&#59;&#10; return request&#59;&#10; &#125;&#10;&#10; public Task&#60;T&#62; GetValueAsync&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; &#47;&#47; Optimization&#58; if we&#39;re already cancelled, do not pass go&#10; if &#40;cancellationToken.IsCancellationRequested&#41;&#10; &#123;&#10; return new Task&#60;T&#62;&#40;&#40;&#41; &#61;&#62; default&#40;T&#41;, cancellationToken&#41;&#59;&#10; &#125;&#10;&#10; Request request&#59;&#10; AsynchronousComputationToStart&#63; newAsynchronousComputation &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If cached, get immediately&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return cachedResult&#59;&#10; &#125;&#10;&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10;&#10; &#47;&#47; If we have either synchronous or asynchronous work current in flight, we don&#39;t need to do anything.&#10; &#47;&#47; Otherwise, we shall start an asynchronous computation for this&#10; if &#40;&#33;computationActive&#41;&#10; &#123;&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; We now have the request counted for, register for cancellation. It is critical this is&#10; &#47;&#47; done outside the lock, as our reigstration may immediately fire and we want to avoid the&#10; &#47;&#47; reentrancy&#10; request.RegisterForCancellation&#40;OnAsynchronousRequestCancelled, cancellationToken&#41;&#59;&#10;&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; request&#41;&#59;&#10; &#125;&#10;&#10; return request.Task&#59;&#10; &#125;&#10;&#10; private AsynchronousComputationToStart RegisterAsynchronousComputation_NoLock&#40;&#41;&#10; &#123;&#10; Contract.ThrowIfTrue&#40;this.computationActive&#41;&#59;&#10;&#10; this.asynchronousComputationCancellationSource &#61; new CancellationTokenSource&#40;&#41;&#59;&#10; this.computationActive &#61; true&#59;&#10;&#10; return new AsynchronousComputationToStart&#40;this.asynchronousComputeFunction, this.asynchronousComputationCancellationSource&#41;&#59;&#10; &#125;&#10;&#10; private struct AsynchronousComputationToStart&#10; &#123;&#10; public readonly Func&#60;CancellationToken, Task&#60;T&#62;&#62; AsynchronousComputeFunction&#59;&#10; public readonly CancellationTokenSource CancellationTokenSource&#59;&#10;&#10; public AsynchronousComputationToStart&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, CancellationTokenSource cancellationTokenSource&#41;&#10; &#123;&#10; this.AsynchronousComputeFunction &#61; asynchronousComputeFunction&#59;&#10; this.CancellationTokenSource &#61; cancellationTokenSource&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void StartAsynchronousComputation&#40;AsynchronousComputationToStart computationToStart, Request requestToCompleteSynchronously&#41;&#10; &#123;&#10; var cancellationToken &#61; computationToStart.CancellationTokenSource.Token&#59;&#10;&#10; try&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; DO NOT ACCESS ANY FIELDS OR STATE BEYOND THIS POINT. Since this function&#10; &#47;&#47; runs unsynchronized, it&#39;s possible that during this function this request&#10; &#47;&#47; might be cancelled, and then a whole additional request might start and&#10; &#47;&#47; complete inline, and cache the result. By grabbing state before we check&#10; &#47;&#47; the cancellation token, we can be assured that we are only operating on&#10; &#47;&#47; a state that was complete.&#10;&#10; ExceptionUtilities.ExecuteWithErrorReporting&#40;&#40;&#41; &#61;&#62;&#10; &#123;&#10; &#47;&#47; We avoid creating a full closure just to pass the token along&#10; &#47;&#47; Also, use TaskContinuationOptions.ExecuteSynchronously so that we inline &#10; &#47;&#47; the continuation if asynchronousComputeFunction completes synchronously&#10; var task &#61; computationToStart.AsynchronousComputeFunction&#40;cancellationToken&#41;&#59;&#10;&#10; task.ContinueWith&#40;&#10; &#40;t, s&#41; &#61;&#62; CompleteWithTask&#40;t, &#40;&#40;CancellationTokenSource&#41;s&#41;.Token&#41;,&#10; computationToStart.CancellationTokenSource,&#10; cancellationToken,&#10; TaskContinuationOptions.ExecuteSynchronously,&#10; TaskScheduler.Default&#41;&#59;&#10;&#10; if &#40;requestToCompleteSynchronously &#33;&#61; null &#38;&#38; task.IsCompleted&#41;&#10; &#123;&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; task &#61; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;task&#41;&#59;&#10; &#125;&#10;&#10; requestToCompleteSynchronously.CompleteFromTaskSynchronously&#40;task&#41;&#59;&#10; &#125;&#10; &#125;&#41;&#59;&#10; &#125;&#10; catch &#40;OperationCanceledException oce&#41;&#10; &#123;&#10; &#47;&#47; As long as it&#39;s the right token, this means that our thread was the first thread&#10; &#47;&#47; to start an asynchronous computation, but the requestor cancelled as we were starting up&#10; &#47;&#47; the computation.&#10; if &#40;oce.CancellationToken &#33;&#61; cancellationToken&#41;&#10; &#123;&#10; ExceptionUtilities.FailFast&#40;oce&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; private void CompleteWithTask&#40;Task&#60;T&#62; task, CancellationToken cancellationToken&#41;&#10; &#123;&#10; IEnumerable&#60;Request&#62; requestsToComplete&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If the underlying computation was cancelled, then all state was already updated in OnAsynchronousRequestCancelled&#10; &#47;&#47; and there is no new work to do here. We &#42;must&#42; use the local one since this completion may be running far after&#10; &#47;&#47; the background computation was cancelled and a new one might have already been enqueued. We must do this&#10; &#47;&#47; check here under the lock to ensure proper synchronization with OnAsynchronousRequestCancelled.&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; The computation is complete, so get all requests to complete and null out the list. We&#39;ll create another one&#10; &#47;&#47; later if it&#39;s needed&#10; requestsToComplete &#61; this.requests &#63;&#63; SpecializedCollections.EmptyEnumerable&#60;Request&#62;&#40;&#41;&#59;&#10; this.requests &#61; null&#59;&#10;&#10; &#47;&#47; The computations are done&#10; this.asynchronousComputationCancellationSource &#61; null&#59;&#10; this.computationActive &#61; false&#59;&#10; task &#61; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;task&#41;&#59;&#10; &#125;&#10;&#10; foreach &#40;var requestToComplete in requestsToComplete&#41;&#10; &#123;&#10; requestToComplete.CompleteFromTaskAsynchronously&#40;task&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; private Task&#60;T&#62; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; if &#40;this.cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return this.cachedResult&#59;&#10; &#125;&#10; else&#10; &#123;&#10; if &#40;cacheResult &#38;&#38; task.Status &#61;&#61; TaskStatus.RanToCompletion&#41;&#10; &#123;&#10; &#47;&#47; Hold onto the completed task. We can get rid of the computation functions for good&#10; this.cachedResult &#61; task&#59;&#10; this.asynchronousComputeFunction &#61; null&#59;&#10; this.synchronousComputeFunction &#61; null&#59;&#10; &#125;&#10;&#10; return task&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void OnAsynchronousRequestCancelled&#40;object state&#41;&#10; &#123;&#10; var request &#61; &#40;Request&#41;state&#59;&#10; CancellationTokenSource cancellationTokenSource &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; &#47;&#47; Now try to remove it. It&#39;s possible that requests may already be null. You could&#10; &#47;&#47; imagine that cancellation was requested, but before we could aquire the lock&#10; &#47;&#47; here the computation completed and the entire CompleteWithTask synchronized&#10; &#47;&#47; block ran. In that case, the requests collection may already be null, or it&#10; &#47;&#47; &#40;even scarier&#33;&#41; may have been replaced with another collection because another&#10; &#47;&#47; computation has started.&#10; if &#40;this.requests &#33;&#61; null&#41;&#10; &#123;&#10; if &#40;this.requests.Remove&#40;request&#41;&#41;&#10; &#123;&#10; if &#40;this.requests.Count &#61;&#61; 0&#41;&#10; &#123;&#10; this.requests &#61; null&#59;&#10;&#10; if &#40;this.asynchronousComputationCancellationSource &#33;&#61; null&#41;&#10; &#123;&#10; cancellationTokenSource &#61; this.asynchronousComputationCancellationSource&#59;&#10; this.asynchronousComputationCancellationSource &#61; null&#59;&#10; this.computationActive &#61; false&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; request.CancelAsynchronously&#40;&#41;&#59;&#10;&#10; if &#40;cancellationTokenSource &#33;&#61; null&#41;&#10; &#123;&#10; cancellationTokenSource.Cancel&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; Using inheritance instead of wrapping a TaskCompletionSource to avoid a second allocation&#10; private class Request &#58; TaskCompletionSource&#60;T&#62;&#10; &#123;&#10; private CancellationTokenRegistration cancellationTokenRegistration&#59;&#10;&#10; public void RegisterForCancellation&#40;Action&#60;object&#62; callback, CancellationToken cancellationToken&#41;&#10; &#123;&#10; cancellationTokenRegistration &#61; cancellationToken.Register&#40;callback, this&#41;&#59;&#10; &#125;&#10;&#10; public void CompleteFromTaskAsynchronously&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; System.Threading.Tasks.Task.Factory.StartNew&#40;CompleteFromTaskSynchronouslyStub, task, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default&#41;&#59;&#10; &#125;&#10;&#10; private void CompleteFromTaskSynchronouslyStub&#40;object task&#41;&#10; &#123;&#10; CompleteFromTaskSynchronously&#40;&#40;Task&#60;T&#62;&#41;task&#41;&#59;&#10; &#125;&#10;&#10; public void CompleteFromTaskSynchronously&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; if &#40;task.Status &#61;&#61; TaskStatus.RanToCompletion&#41;&#10; &#123;&#10; if &#40;TrySetResult&#40;task.Result&#41;&#41;&#10; &#123;&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; else if &#40;task.Status &#61;&#61; TaskStatus.Faulted&#41;&#10; &#123;&#10; if &#40;TrySetException&#40;task.Exception&#41;&#41;&#10; &#123;&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; else&#10; &#123;&#10; CancelSynchronously&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; public void CancelAsynchronously&#40;&#41;&#10; &#123;&#10; &#47;&#47; Since there could be synchronous continuations on the TaskCancellationSource, we queue this to the threadpool&#10; &#47;&#47; to avoid inline running of other operations.&#10; System.Threading.Tasks.Task.Factory.StartNew&#40;CancelSynchronously, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default&#41;&#59;&#10; &#125;&#10;&#10; private void CancelSynchronously&#40;&#41;&#10; &#123;&#10; if &#40;TrySetCanceled&#40;&#41;&#41;&#10; &#123;&#10; &#47;&#47; Paranoia&#58; the only reason we should ever get here is if the CancellationToken that&#10; &#47;&#47; we registered against was cancelled, but just in case, dispose the registration&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; A lightweight mutual exclusion object which supports waiting with cancellation and prevents&#10; &#47;&#47;&#47; recursion &#40;i.e. you may not call Wait if you already hold the lock&#41;&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; The &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62; provides a lightweight mutual exclusion class that doesn&#39;t&#10; &#47;&#47;&#47; use Windows kernel synchronization primitives.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; The implementation is distilled from the workings of &#60;see cref&#61;&#34;T&#58;System.Threading.SemaphoreSlim&#34;&#47;&#62;&#10; &#47;&#47;&#47; The basic idea is that we use a regular sync object &#40;Monitor.Enter&#47;Exit&#41; to guard the setting&#10; &#47;&#47;&#47; of an &#39;owning thread&#39; field. If, during the Wait, we find the lock is held by someone else&#10; &#47;&#47;&#47; then we register a cancellation callback and enter a &#34;Monitor.Wait&#34; loop. If the cancellation&#10; &#47;&#47;&#47; callback fires, then it &#34;pulses&#34; all the waiters to wake them up and check for cancellation.&#10; &#47;&#47;&#47; Waiters are also &#34;pulsed&#34; when leaving the lock.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; All public members of &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62; are thread-safe and may be used concurrently&#10; &#47;&#47;&#47; from multiple threads.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; internal sealed class NonReentrantLock&#10; &#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; A synchronization object to protect access to the &#60;see cref&#61;&#34;F&#58;owningThread&#34;&#47;&#62; field and to be pulsed&#10; &#47;&#47;&#47; when &#60;see cref&#61;&#34;M&#58;Release&#34;&#47;&#62; is called and during cancellation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private readonly object syncLock&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Indicates which thread currently holds the lock. If null, then the lock is available.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private volatile Thread owningThread&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Constructor.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;useThisInstanceForSynchronization&#34;&#62;If false &#40;the default&#41;, then the class&#10; &#47;&#47;&#47; allocates an internal object to be used as a sync lock.&#10; &#47;&#47;&#47; If true, then the sync lock object will be the NonReentrantLock instance itself. This&#10; &#47;&#47;&#47; saves an allocation but a client may not safely further use this instance in a call to&#10; &#47;&#47;&#47; Monitor.Enter&#47;Exit or in a &#34;lock&#34; statement.&#10; &#47;&#47;&#47; &#60;&#47;param&#62;&#10; public NonReentrantLock&#40;bool useThisInstanceForSynchronization &#61; false&#41;&#10; &#123;&#10; this.syncLock &#61; useThisInstanceForSynchronization &#63; this &#58; new object&#40;&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Shared factory for use in lazy initialization.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public static readonly Func&#60;NonReentrantLock&#62; Factory &#61; &#40;&#41; &#61;&#62; new NonReentrantLock&#40;useThisInstanceForSynchronization&#58; true&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Blocks the current thread until it can enter the &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62;, while observing a&#10; &#47;&#47;&#47; &#60;see cref&#61;&#34;T&#58;System.Threading.CancellationToken&#34;&#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; Recursive locking is not supported. i.e. A thread may not call Wait successfully twice without an&#10; &#47;&#47;&#47; intervening &#60;see cref&#61;&#34;Release&#34;&#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;cancellationToken&#34;&#62;The &#60;see cref&#61;&#34;T&#58;System.Threading.CancellationToken&#34;&#47;&#62; token to&#10; &#47;&#47;&#47; observe.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;System.OperationCanceledException&#34;&#62;&#60;paramref name&#61;&#34;cancellationToken&#34;&#47;&#62; was&#10; &#47;&#47;&#47; canceled.&#60;&#47;exception&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;System.LockRecursionException&#34;&#62;The caller already holds the lock&#60;&#47;exception&#62;&#10; public void Wait&#40;CancellationToken cancellationToken &#61; default&#40;CancellationToken&#41;&#41;&#10; &#123;&#10; if &#40;this.IsOwnedByMe&#41;&#10; &#123;&#10; throw new LockRecursionException&#40;&#41;&#59;&#10; &#125;&#10;&#10; CancellationTokenRegistration cancellationTokenRegistration &#61; default&#40;CancellationTokenRegistration&#41;&#59;&#10;&#10; if &#40;cancellationToken.CanBeCanceled&#41;&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; Fast path to try and avoid allocations in callback registration.&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; if &#40;&#33;this.IsLocked&#41;&#10; &#123;&#10; this.TakeOwnership&#40;&#41;&#59;&#10; return&#59;&#10; &#125;&#10; &#125;&#10;&#10; cancellationTokenRegistration &#61; cancellationToken.Register&#40;cancellationTokenCanceledEventHandler, this.syncLock, useSynchronizationContext&#58; false&#41;&#59;&#10; &#125;&#10;&#10; using &#40;cancellationTokenRegistration&#41;&#10; &#123;&#10; &#47;&#47; PERF&#58; First spin wait for the lock to become available, but only up to the first planned yield.&#10; &#47;&#47; This additional amount of spinwaiting was inherited from SemaphoreSlim&#39;s implementation where&#10; &#47;&#47; it showed measurable perf gains in test scenarios.&#10; SpinWait spin &#61; new SpinWait&#40;&#41;&#59;&#10; while &#40;this.IsLocked &#38;&#38; &#33;spin.NextSpinWillYield&#41;&#10; &#123;&#10; spin.SpinOnce&#40;&#41;&#59;&#10; &#125;&#10;&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; while &#40;this.IsLocked&#41;&#10; &#123;&#10; &#47;&#47; If cancelled, we throw. Trying to wait could lead to deadlock.&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; using &#40;Logger.LogBlock&#40;FeatureId.WaitIndicator, FunctionId.Utilities_NonReentrantLock_BlockingWait, cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; Another thread holds the lock. Wait until we get awoken either&#10; &#47;&#47; by some code calling &#34;Release&#34; or by cancellation.&#10; Monitor.Wait&#40;this.syncLock&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; We now hold the lock&#10; this.TakeOwnership&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Exit the mutual exclusion.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; The calling thread must currently hold the lock.&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;Roslyn.Utilities.ContractFailureException&#34;&#62;The lock is not currently held by the calling thread.&#60;&#47;exception&#62;&#10; public void Release&#40;&#41;&#10; &#123;&#10; AssertHasLock&#40;&#41;&#59;&#10;&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; this.ReleaseOwnership&#40;&#41;&#59;&#10;&#10; &#47;&#47; Release one waiter&#10; Monitor.Pulse&#40;this.syncLock&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Determine if the lock is currently held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;returns&#62;True if the lock is currently held by the calling thread.&#60;&#47;returns&#62;&#10; public bool LockHeldByMe&#40;&#41;&#10; &#123;&#10; return this.IsOwnedByMe&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Throw an exception if the lock is not held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;Roslyn.Utilities.ContractFailureException&#34;&#62;The lock is not currently held by the calling thread.&#60;&#47;exception&#62;&#10; public void AssertHasLock&#40;&#41;&#10; &#123;&#10; Contract.ThrowIfFalse&#40;LockHeldByMe&#40;&#41;&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Checks if the lock is currently held.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool IsLocked&#10; &#123;&#10; get&#10; &#123;&#10; return this.owningThread &#33;&#61; null&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Checks if the lock is currently held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool IsOwnedByMe&#10; &#123;&#10; get&#10; &#123;&#10; return this.owningThread &#61;&#61; Thread.CurrentThread&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Take ownership of the lock &#40;by the calling thread&#41;. The lock may not already&#10; &#47;&#47;&#47; be held by any other code.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private void TakeOwnership&#40;&#41;&#10; &#123;&#10; Contract.Assert&#40;&#33;this.IsLocked&#41;&#59;&#10; this.owningThread &#61; Thread.CurrentThread&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Release ownership of the lock. The lock must already be held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private void ReleaseOwnership&#40;&#41;&#10; &#123;&#10; Contract.Assert&#40;this.IsOwnedByMe&#41;&#59;&#10; this.owningThread &#61; null&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Action object passed to a cancellation token registration.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private static readonly Action&#60;object&#62; cancellationTokenCanceledEventHandler &#61; new Action&#60;object&#62;&#40;CancellationTokenCanceledEventHandler&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Callback executed when a cancellation token is canceled during a Wait.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;obj&#34;&#62;The syncLock that protects a &#60;see cref&#61;&#34;NonReentrantLock &#34;&#47;&#62; instance.&#60;&#47;param&#62;&#10; private static void CancellationTokenCanceledEventHandler&#40;object obj&#41;&#10; &#123;&#10; lock &#40;obj&#41;&#10; &#123;&#10; &#47;&#47; Release all waiters to check their cancellation tokens.&#10; Monitor.PulseAll&#40;obj&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#125;</pre>2013-05-23T06:05:29-07:00KLhttp://rise4fun.com/Seal/KLSeal/KL<pre>using System&#59;&#10;class Test &#123;&#10; public Test f &#61; null&#59;&#10; public void PureMethod&#40;Test param&#41;&#123;&#10; var temp &#61; new Test&#40;&#41;&#59;&#10; temp.f &#61; param&#59; &#10; param.f &#61; null&#59; &#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00KFhttp://rise4fun.com/Seal/KFSeal/KF<pre>using System&#59;&#10;class Test &#123;&#10; public Test f &#61; null&#59;&#10; public void ImpureMethod&#40;Test param&#41;&#123;&#10; var temp &#61; new Test&#40;&#41;&#59;&#10; param.f &#61; temp&#59; &#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00kDLhttp://rise4fun.com/Seal/kDLSeal/kDL<pre>using System&#59;&#10;class Test &#123;&#10; public Test f &#61; null&#59;&#10; public void ImpureMethod&#40;Test param&#41;&#123;&#10; int i &#61; 0&#59;&#10; if &#40;i &#60; 0&#41;&#10; param.f &#61; null&#59;&#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00kdHhttp://rise4fun.com/Seal/kdHSeal/kdH<pre>using System&#59;&#10;using System.Collections.Generic&#59;&#10;using System.Runtime.CompilerServices&#59;&#10;using System.Threading&#59;&#10;using System.Threading.Tasks&#59;&#10;&#10;namespace Roslyn.Utilities&#10;&#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Represents a value that can be retrieved synchronously or asynchronously by many clients.&#10; &#47;&#47;&#47; The value will be computed on-demand the moment the first client asks for it. While being&#10; &#47;&#47;&#47; computed, more clients can request the value. As long as there are outstanding clients the&#10; &#47;&#47;&#47; underlying computation will proceed. If all outstanding clients cancel their request then&#10; &#47;&#47;&#47; the underlying value computation will be cancelled as well.&#10; &#47;&#47;&#47; &#10; &#47;&#47;&#47; Creators of an &#60;see cref&#61;&#34;AsyncLazy&#123;T&#125;&#34; &#47;&#62; can specify whether the result of the computation is&#10; &#47;&#47;&#47; cached for future requests or not. Choosing to not cache means the computation functions are kept&#10; &#47;&#47;&#47; alive, whereas caching means the value &#40;but not functions&#41; are kept alive once complete.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; internal sealed class AsyncLazy&#60;T&#62;&#10; &#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The underlying function that starts an asynchronous computation of the resulting value.&#10; &#47;&#47;&#47; Null&#39;ed out once we&#39;ve computed the result and we&#39;ve been asked to cache it. Otherwise,&#10; &#47;&#47;&#47; it is kept around in case the value needs to be computed again.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The underlying function that starts an synchronous computation of the resulting value.&#10; &#47;&#47;&#47; Null&#39;ed out once we&#39;ve computed the result and we&#39;ve been asked to cache it, or if we&#10; &#47;&#47;&#47; didn&#39;t get any synchronous function given to us in the first place.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Func&#60;CancellationToken, T&#62; synchronousComputeFunction&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Whether or not we should keep the value around once we&#39;ve computed it.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private readonly bool cacheResult&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The Task that holds the cached result.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Task&#60;T&#62; cachedResult&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Mutex used to protect reading and writing to all mutable objects and fields. Traces&#10; &#47;&#47;&#47; indicate that there&#39;s negligible contention on this lock, hence we can save some memory&#10; &#47;&#47;&#47; by using a single lock for all AsyncLazy instances. Only trivial and non-reentrant work&#10; &#47;&#47;&#47; should be done while holding the lock.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private static readonly NonReentrantLock gate &#61; new NonReentrantLock&#40;useThisInstanceForSynchronization&#58; true&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The hash set of all currently outstanding asynchronous requests. Null if there are no requests,&#10; &#47;&#47;&#47; and will never be empty.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private HashSet&#60;Request&#62; requests &#61; null&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; If an asynchronous request is active, the CancellationTokenSource that allows for&#10; &#47;&#47;&#47; cancelling the underlying computation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private CancellationTokenSource asynchronousComputationCancellationSource &#61; null&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Whether a computation is active or queued on any thread, whether synchronous or&#10; &#47;&#47;&#47; asynchronous.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool computationActive &#61; false&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Creates an AsyncLazy that always returns the value, analogous to &#60;see cref&#61;&#34;Task.FromResult&#123;T&#125;&#34; &#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public AsyncLazy&#40;T value&#41;&#10; &#123;&#10; this.cacheResult &#61; true&#59;&#10; this.cachedResult &#61; new Task&#60;T&#62;&#40;&#40;&#41; &#61;&#62; &#123;return value&#59;&#125;&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Important&#58; callers of this constructor should ensure that the compute function returns&#10; &#47;&#47;&#47; a task in a non-blocking fashion. i.e. the function should &#42;not&#42; synchronously compute&#10; &#47;&#47;&#47; a value and then return it using Task.FromResult. Instead, it should return an actual&#10; &#47;&#47;&#47; task that operates asynchronously. If this function synchronously computes a value&#10; &#47;&#47;&#47; then that will cause locks to be held in this type for excessive periods of time.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public AsyncLazy&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, bool cacheResult&#41;&#10; &#58; this&#40;asynchronousComputeFunction, synchronousComputeFunction&#58; null, cacheResult&#58; cacheResult&#41;&#10; &#123;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Creates an AsyncLazy that supports both asynchronous computation and inline synchronous&#10; &#47;&#47;&#47; computation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;asynchronousComputeFunction&#34;&#62;A function called to start the asynchronous&#10; &#47;&#47;&#47; computation. This function should be cheap and non-blocking.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;synchronousComputeFunction&#34;&#62;A function to do the work synchronously, which&#10; &#47;&#47;&#47; is allowed to block. This function should not be implemented by a simple Wait on the&#10; &#47;&#47;&#47; asynchronous value. If that&#39;s all you are doing, just don&#39;t pass a synchronous function&#10; &#47;&#47;&#47; in the first place.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;cacheResult&#34;&#62;Whether the result should be cached once the computation is&#10; &#47;&#47;&#47; complete.&#60;&#47;param&#62;&#10; public AsyncLazy&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, Func&#60;CancellationToken, T&#62; synchronousComputeFunction, bool cacheResult&#41;&#10; &#123;&#10; this.asynchronousComputeFunction &#61; asynchronousComputeFunction&#59;&#10; this.synchronousComputeFunction &#61; synchronousComputeFunction&#59;&#10; this.cacheResult &#61; cacheResult&#59;&#10; &#125;&#10;&#10; &#35;region Lock Wrapper for Invariant Checking&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Takes the lock for this object and if acquired validates the invariants of this class.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private WaitThatValidatesInvariants TakeLock&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; gate.Wait&#40;cancellationToken&#41;&#59;&#10; AssertInvariants_NoLock&#40;&#41;&#59;&#10; return new WaitThatValidatesInvariants&#40;this&#41;&#59;&#10; &#125;&#10;&#10; private struct WaitThatValidatesInvariants &#58; IDisposable&#10; &#123;&#10; private readonly AsyncLazy&#60;T&#62; asyncLazy&#59;&#10;&#10; public WaitThatValidatesInvariants&#40;AsyncLazy&#60;T&#62; asyncLazy&#41;&#10; &#123;&#10; this.asyncLazy &#61; asyncLazy&#59;&#10; &#125;&#10;&#10; public void Dispose&#40;&#41;&#10; &#123;&#10; asyncLazy.AssertInvariants_NoLock&#40;&#41;&#59;&#10; gate.Release&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void AssertInvariants_NoLock&#40;&#41;&#10; &#123;&#10;&#10; &#125;&#10;&#10; &#35;endregion&#10;&#10; public bool TryGetValue&#40;out T result&#41;&#10; &#123;&#10; &#47;&#47; No need to lock here since this is only a fast check to &#10; &#47;&#47; see if the result is already computed.&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; result &#61; cachedResult.Result&#59;&#10; return true&#59;&#10; &#125;&#10;&#10; result &#61; default&#40;T&#41;&#59;&#10; return false&#59;&#10; &#125;&#10;&#10; public T GetValue&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; Request request &#61; null&#59;&#10; AsynchronousComputationToStart&#63; newAsynchronousComputation &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If cached, get immediately&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return cachedResult.Result&#59;&#10; &#125;&#10;&#10; &#47;&#47; If there is an existing computation active, we&#39;ll just create another request&#10; if &#40;computationActive&#41;&#10; &#123;&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10; &#125;&#10; else if &#40;synchronousComputeFunction &#61;&#61; null&#41;&#10; &#123;&#10; &#47;&#47; A synchronous request, but we have no synchronous function. Start off the async work&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10;&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; else&#10; &#123;&#10; &#47;&#47; We will do the computation here&#10; this.computationActive &#61; true&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; If we simply created a new asynchronous request, so wait for it. Yes, we&#39;re blocking the thread&#10; &#47;&#47; but we don&#39;t want multiple threads attempting to compute the same thing.&#10; if &#40;request &#33;&#61; null&#41;&#10; &#123;&#10; request.RegisterForCancellation&#40;OnAsynchronousRequestCancelled, cancellationToken&#41;&#59;&#10;&#10; &#47;&#47; Since we already registered for cancellation, it&#39;s possible that the registration has&#10; &#47;&#47; cancelled this new computation if we were the only requestor.&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; request&#41;&#59;&#10; &#125;&#10;&#10; return WaitAndGetResult&#40;request.Task, cancellationToken&#41;&#59;&#10; &#125;&#10; else&#10; &#123;&#10; T result&#59;&#10;&#10; &#47;&#47; We are the active computation, so let&#39;s go ahead and compute.&#10; try&#10; &#123;&#10; result &#61; synchronousComputeFunction&#40;cancellationToken&#41;&#59;&#10; &#125;&#10; catch &#40;OperationCanceledException&#41;&#10; &#123;&#10; &#47;&#47; This cancelled for some reason. We don&#39;t care why, but&#10; &#47;&#47; it means anybody else waiting for this result isn&#39;t going to get it&#10; &#47;&#47; from us.&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; this.computationActive &#61; false&#59;&#10;&#10; if &#40;requests &#33;&#61; null&#41;&#10; &#123;&#10; &#47;&#47; There&#39;s a possible improvement here&#58; there might be another synchronous caller who&#10; &#47;&#47; also wants the value. We might consider stealing their thread rather than punting&#10; &#47;&#47; to the thread pool.&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; null&#41;&#59;&#10; &#125;&#10;&#10; throw&#59;&#10; &#125;&#10; catch &#40;Exception ex&#41;&#10; &#123;&#10; &#47;&#47; We faulted for some unknown reason. We should simply fault everything.&#10; TaskCompletionSource&#60;T&#62; tcs &#61; new TaskCompletionSource&#60;T&#62;&#40;&#41;&#59;&#10; tcs.SetException&#40;ex&#41;&#59;&#10; CompleteWithTask&#40;tcs.Task, CancellationToken.None&#41;&#59;&#10;&#10; throw&#59;&#10; &#125;&#10;&#10; &#47;&#47; We have a value, so complete&#10; CompleteWithTask&#40;Task.FromResult&#40;result&#41;, CancellationToken.None&#41;&#59;&#10;&#10; return result&#59;&#10; &#125;&#10; &#125;&#10; &#10; private T WaitAndGetResult&#40;Task&#60;T&#62; task, CancellationToken cancellationToken&#41;&#10; &#123;&#10; task.Wait&#40;cancellationToken&#41;&#59;&#10; return task.Result&#59;&#10; &#125;&#10;&#10; private Request CreateNewRequest_NoLock&#40;&#41;&#10; &#123;&#10; if &#40;this.requests &#61;&#61; null&#41;&#10; &#123;&#10; this.requests &#61; new HashSet&#60;Request&#62;&#40;&#41;&#59;&#10; &#125;&#10;&#10; Request request &#61; new Request&#40;&#41;&#59;&#10; this.requests.Add&#40;request&#41;&#59;&#10; return request&#59;&#10; &#125;&#10;&#10; public Task&#60;T&#62; GetValueAsync&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; &#47;&#47; Optimization&#58; if we&#39;re already cancelled, do not pass go&#10; if &#40;cancellationToken.IsCancellationRequested&#41;&#10; &#123;&#10; return new Task&#60;T&#62;&#40;&#40;&#41; &#61;&#62; default&#40;T&#41;, cancellationToken&#41;&#59;&#10; &#125;&#10;&#10; Request request&#59;&#10; AsynchronousComputationToStart&#63; newAsynchronousComputation &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If cached, get immediately&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return cachedResult&#59;&#10; &#125;&#10;&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10;&#10; &#47;&#47; If we have either synchronous or asynchronous work current in flight, we don&#39;t need to do anything.&#10; &#47;&#47; Otherwise, we shall start an asynchronous computation for this&#10; if &#40;&#33;computationActive&#41;&#10; &#123;&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; We now have the request counted for, register for cancellation. It is critical this is&#10; &#47;&#47; done outside the lock, as our reigstration may immediately fire and we want to avoid the&#10; &#47;&#47; reentrancy&#10; request.RegisterForCancellation&#40;OnAsynchronousRequestCancelled, cancellationToken&#41;&#59;&#10;&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; request&#41;&#59;&#10; &#125;&#10;&#10; return request.Task&#59;&#10; &#125;&#10;&#10; private AsynchronousComputationToStart RegisterAsynchronousComputation_NoLock&#40;&#41;&#10; &#123;&#10; Contract.ThrowIfTrue&#40;this.computationActive&#41;&#59;&#10;&#10; this.asynchronousComputationCancellationSource &#61; new CancellationTokenSource&#40;&#41;&#59;&#10; this.computationActive &#61; true&#59;&#10;&#10; return new AsynchronousComputationToStart&#40;this.asynchronousComputeFunction, this.asynchronousComputationCancellationSource&#41;&#59;&#10; &#125;&#10;&#10; private struct AsynchronousComputationToStart&#10; &#123;&#10; public readonly Func&#60;CancellationToken, Task&#60;T&#62;&#62; AsynchronousComputeFunction&#59;&#10; public readonly CancellationTokenSource CancellationTokenSource&#59;&#10;&#10; public AsynchronousComputationToStart&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, CancellationTokenSource cancellationTokenSource&#41;&#10; &#123;&#10; this.AsynchronousComputeFunction &#61; asynchronousComputeFunction&#59;&#10; this.CancellationTokenSource &#61; cancellationTokenSource&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void StartAsynchronousComputation&#40;AsynchronousComputationToStart computationToStart, Request requestToCompleteSynchronously&#41;&#10; &#123;&#10; var cancellationToken &#61; computationToStart.CancellationTokenSource.Token&#59;&#10;&#10; try&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; DO NOT ACCESS ANY FIELDS OR STATE BEYOND THIS POINT. Since this function&#10; &#47;&#47; runs unsynchronized, it&#39;s possible that during this function this request&#10; &#47;&#47; might be cancelled, and then a whole additional request might start and&#10; &#47;&#47; complete inline, and cache the result. By grabbing state before we check&#10; &#47;&#47; the cancellation token, we can be assured that we are only operating on&#10; &#47;&#47; a state that was complete.&#10;&#10; ExceptionUtilities.ExecuteWithErrorReporting&#40;&#40;&#41; &#61;&#62;&#10; &#123;&#10; &#47;&#47; We avoid creating a full closure just to pass the token along&#10; &#47;&#47; Also, use TaskContinuationOptions.ExecuteSynchronously so that we inline &#10; &#47;&#47; the continuation if asynchronousComputeFunction completes synchronously&#10; var task &#61; computationToStart.AsynchronousComputeFunction&#40;cancellationToken&#41;&#59;&#10;&#10; task.ContinueWith&#40;&#10; &#40;t, s&#41; &#61;&#62; CompleteWithTask&#40;t, &#40;&#40;CancellationTokenSource&#41;s&#41;.Token&#41;,&#10; computationToStart.CancellationTokenSource,&#10; cancellationToken,&#10; TaskContinuationOptions.ExecuteSynchronously,&#10; TaskScheduler.Default&#41;&#59;&#10;&#10; if &#40;requestToCompleteSynchronously &#33;&#61; null &#38;&#38; task.IsCompleted&#41;&#10; &#123;&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; task &#61; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;task&#41;&#59;&#10; &#125;&#10;&#10; requestToCompleteSynchronously.CompleteFromTaskSynchronously&#40;task&#41;&#59;&#10; &#125;&#10; &#125;&#41;&#59;&#10; &#125;&#10; catch &#40;OperationCanceledException oce&#41;&#10; &#123;&#10; &#47;&#47; As long as it&#39;s the right token, this means that our thread was the first thread&#10; &#47;&#47; to start an asynchronous computation, but the requestor cancelled as we were starting up&#10; &#47;&#47; the computation.&#10; if &#40;oce.CancellationToken &#33;&#61; cancellationToken&#41;&#10; &#123;&#10; ExceptionUtilities.FailFast&#40;oce&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; private void CompleteWithTask&#40;Task&#60;T&#62; task, CancellationToken cancellationToken&#41;&#10; &#123;&#10; IEnumerable&#60;Request&#62; requestsToComplete&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If the underlying computation was cancelled, then all state was already updated in OnAsynchronousRequestCancelled&#10; &#47;&#47; and there is no new work to do here. We &#42;must&#42; use the local one since this completion may be running far after&#10; &#47;&#47; the background computation was cancelled and a new one might have already been enqueued. We must do this&#10; &#47;&#47; check here under the lock to ensure proper synchronization with OnAsynchronousRequestCancelled.&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; The computation is complete, so get all requests to complete and null out the list. We&#39;ll create another one&#10; &#47;&#47; later if it&#39;s needed&#10; requestsToComplete &#61; this.requests &#63;&#63; SpecializedCollections.EmptyEnumerable&#60;Request&#62;&#40;&#41;&#59;&#10; this.requests &#61; null&#59;&#10;&#10; &#47;&#47; The computations are done&#10; this.asynchronousComputationCancellationSource &#61; null&#59;&#10; this.computationActive &#61; false&#59;&#10; task &#61; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;task&#41;&#59;&#10; &#125;&#10;&#10; foreach &#40;var requestToComplete in requestsToComplete&#41;&#10; &#123;&#10; requestToComplete.CompleteFromTaskAsynchronously&#40;task&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; private Task&#60;T&#62; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; if &#40;this.cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return this.cachedResult&#59;&#10; &#125;&#10; else&#10; &#123;&#10; if &#40;cacheResult &#38;&#38; task.Status &#61;&#61; TaskStatus.RanToCompletion&#41;&#10; &#123;&#10; &#47;&#47; Hold onto the completed task. We can get rid of the computation functions for good&#10; this.cachedResult &#61; task&#59;&#10; this.asynchronousComputeFunction &#61; null&#59;&#10; this.synchronousComputeFunction &#61; null&#59;&#10; &#125;&#10;&#10; return task&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void OnAsynchronousRequestCancelled&#40;object state&#41;&#10; &#123;&#10; var request &#61; &#40;Request&#41;state&#59;&#10; CancellationTokenSource cancellationTokenSource &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; &#47;&#47; Now try to remove it. It&#39;s possible that requests may already be null. You could&#10; &#47;&#47; imagine that cancellation was requested, but before we could aquire the lock&#10; &#47;&#47; here the computation completed and the entire CompleteWithTask synchronized&#10; &#47;&#47; block ran. In that case, the requests collection may already be null, or it&#10; &#47;&#47; &#40;even scarier&#33;&#41; may have been replaced with another collection because another&#10; &#47;&#47; computation has started.&#10; if &#40;this.requests &#33;&#61; null&#41;&#10; &#123;&#10; if &#40;this.requests.Remove&#40;request&#41;&#41;&#10; &#123;&#10; if &#40;this.requests.Count &#61;&#61; 0&#41;&#10; &#123;&#10; this.requests &#61; null&#59;&#10;&#10; if &#40;this.asynchronousComputationCancellationSource &#33;&#61; null&#41;&#10; &#123;&#10; cancellationTokenSource &#61; this.asynchronousComputationCancellationSource&#59;&#10; this.asynchronousComputationCancellationSource &#61; null&#59;&#10; this.computationActive &#61; false&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; request.CancelAsynchronously&#40;&#41;&#59;&#10;&#10; if &#40;cancellationTokenSource &#33;&#61; null&#41;&#10; &#123;&#10; cancellationTokenSource.Cancel&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; Using inheritance instead of wrapping a TaskCompletionSource to avoid a second allocation&#10; private class Request &#58; TaskCompletionSource&#60;T&#62;&#10; &#123;&#10; private CancellationTokenRegistration cancellationTokenRegistration&#59;&#10;&#10; public void RegisterForCancellation&#40;Action&#60;object&#62; callback, CancellationToken cancellationToken&#41;&#10; &#123;&#10; cancellationTokenRegistration &#61; cancellationToken.Register&#40;callback, this&#41;&#59;&#10; &#125;&#10;&#10; public void CompleteFromTaskAsynchronously&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; System.Threading.Tasks.Task.Factory.StartNew&#40;CompleteFromTaskSynchronouslyStub, task, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default&#41;&#59;&#10; &#125;&#10;&#10; private void CompleteFromTaskSynchronouslyStub&#40;object task&#41;&#10; &#123;&#10; CompleteFromTaskSynchronously&#40;&#40;Task&#60;T&#62;&#41;task&#41;&#59;&#10; &#125;&#10;&#10; public void CompleteFromTaskSynchronously&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; if &#40;task.Status &#61;&#61; TaskStatus.RanToCompletion&#41;&#10; &#123;&#10; if &#40;TrySetResult&#40;task.Result&#41;&#41;&#10; &#123;&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; else if &#40;task.Status &#61;&#61; TaskStatus.Faulted&#41;&#10; &#123;&#10; if &#40;TrySetException&#40;task.Exception&#41;&#41;&#10; &#123;&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; else&#10; &#123;&#10; CancelSynchronously&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; public void CancelAsynchronously&#40;&#41;&#10; &#123;&#10; &#47;&#47; Since there could be synchronous continuations on the TaskCancellationSource, we queue this to the threadpool&#10; &#47;&#47; to avoid inline running of other operations.&#10; System.Threading.Tasks.Task.Factory.StartNew&#40;CancelSynchronously, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default&#41;&#59;&#10; &#125;&#10;&#10; private void CancelSynchronously&#40;&#41;&#10; &#123;&#10; if &#40;TrySetCanceled&#40;&#41;&#41;&#10; &#123;&#10; &#47;&#47; Paranoia&#58; the only reason we should ever get here is if the CancellationToken that&#10; &#47;&#47; we registered against was cancelled, but just in case, dispose the registration&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; A lightweight mutual exclusion object which supports waiting with cancellation and prevents&#10; &#47;&#47;&#47; recursion &#40;i.e. you may not call Wait if you already hold the lock&#41;&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; The &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62; provides a lightweight mutual exclusion class that doesn&#39;t&#10; &#47;&#47;&#47; use Windows kernel synchronization primitives.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; The implementation is distilled from the workings of &#60;see cref&#61;&#34;T&#58;System.Threading.SemaphoreSlim&#34;&#47;&#62;&#10; &#47;&#47;&#47; The basic idea is that we use a regular sync object &#40;Monitor.Enter&#47;Exit&#41; to guard the setting&#10; &#47;&#47;&#47; of an &#39;owning thread&#39; field. If, during the Wait, we find the lock is held by someone else&#10; &#47;&#47;&#47; then we register a cancellation callback and enter a &#34;Monitor.Wait&#34; loop. If the cancellation&#10; &#47;&#47;&#47; callback fires, then it &#34;pulses&#34; all the waiters to wake them up and check for cancellation.&#10; &#47;&#47;&#47; Waiters are also &#34;pulsed&#34; when leaving the lock.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; All public members of &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62; are thread-safe and may be used concurrently&#10; &#47;&#47;&#47; from multiple threads.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; internal sealed class NonReentrantLock&#10; &#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; A synchronization object to protect access to the &#60;see cref&#61;&#34;F&#58;owningThread&#34;&#47;&#62; field and to be pulsed&#10; &#47;&#47;&#47; when &#60;see cref&#61;&#34;M&#58;Release&#34;&#47;&#62; is called and during cancellation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private readonly object syncLock&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Indicates which thread currently holds the lock. If null, then the lock is available.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private volatile Thread owningThread&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Constructor.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;useThisInstanceForSynchronization&#34;&#62;If false &#40;the default&#41;, then the class&#10; &#47;&#47;&#47; allocates an internal object to be used as a sync lock.&#10; &#47;&#47;&#47; If true, then the sync lock object will be the NonReentrantLock instance itself. This&#10; &#47;&#47;&#47; saves an allocation but a client may not safely further use this instance in a call to&#10; &#47;&#47;&#47; Monitor.Enter&#47;Exit or in a &#34;lock&#34; statement.&#10; &#47;&#47;&#47; &#60;&#47;param&#62;&#10; public NonReentrantLock&#40;bool useThisInstanceForSynchronization &#61; false&#41;&#10; &#123;&#10; this.syncLock &#61; useThisInstanceForSynchronization &#63; this &#58; new object&#40;&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Shared factory for use in lazy initialization.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public static readonly Func&#60;NonReentrantLock&#62; Factory &#61; &#40;&#41; &#61;&#62; new NonReentrantLock&#40;useThisInstanceForSynchronization&#58; true&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Blocks the current thread until it can enter the &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62;, while observing a&#10; &#47;&#47;&#47; &#60;see cref&#61;&#34;T&#58;System.Threading.CancellationToken&#34;&#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; Recursive locking is not supported. i.e. A thread may not call Wait successfully twice without an&#10; &#47;&#47;&#47; intervening &#60;see cref&#61;&#34;Release&#34;&#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;cancellationToken&#34;&#62;The &#60;see cref&#61;&#34;T&#58;System.Threading.CancellationToken&#34;&#47;&#62; token to&#10; &#47;&#47;&#47; observe.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;System.OperationCanceledException&#34;&#62;&#60;paramref name&#61;&#34;cancellationToken&#34;&#47;&#62; was&#10; &#47;&#47;&#47; canceled.&#60;&#47;exception&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;System.LockRecursionException&#34;&#62;The caller already holds the lock&#60;&#47;exception&#62;&#10; public void Wait&#40;CancellationToken cancellationToken &#61; default&#40;CancellationToken&#41;&#41;&#10; &#123;&#10; if &#40;this.IsOwnedByMe&#41;&#10; &#123;&#10; throw new LockRecursionException&#40;&#41;&#59;&#10; &#125;&#10;&#10; CancellationTokenRegistration cancellationTokenRegistration &#61; default&#40;CancellationTokenRegistration&#41;&#59;&#10;&#10; if &#40;cancellationToken.CanBeCanceled&#41;&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; Fast path to try and avoid allocations in callback registration.&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; if &#40;&#33;this.IsLocked&#41;&#10; &#123;&#10; this.TakeOwnership&#40;&#41;&#59;&#10; return&#59;&#10; &#125;&#10; &#125;&#10;&#10; cancellationTokenRegistration &#61; cancellationToken.Register&#40;cancellationTokenCanceledEventHandler, this.syncLock, useSynchronizationContext&#58; false&#41;&#59;&#10; &#125;&#10;&#10; using &#40;cancellationTokenRegistration&#41;&#10; &#123;&#10; &#47;&#47; PERF&#58; First spin wait for the lock to become available, but only up to the first planned yield.&#10; &#47;&#47; This additional amount of spinwaiting was inherited from SemaphoreSlim&#39;s implementation where&#10; &#47;&#47; it showed measurable perf gains in test scenarios.&#10; SpinWait spin &#61; new SpinWait&#40;&#41;&#59;&#10; while &#40;this.IsLocked &#38;&#38; &#33;spin.NextSpinWillYield&#41;&#10; &#123;&#10; spin.SpinOnce&#40;&#41;&#59;&#10; &#125;&#10;&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; while &#40;this.IsLocked&#41;&#10; &#123;&#10; &#47;&#47; If cancelled, we throw. Trying to wait could lead to deadlock.&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; using &#40;Logger.LogBlock&#40;FeatureId.WaitIndicator, FunctionId.Utilities_NonReentrantLock_BlockingWait, cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; Another thread holds the lock. Wait until we get awoken either&#10; &#47;&#47; by some code calling &#34;Release&#34; or by cancellation.&#10; Monitor.Wait&#40;this.syncLock&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; We now hold the lock&#10; this.TakeOwnership&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Exit the mutual exclusion.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; The calling thread must currently hold the lock.&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;Roslyn.Utilities.ContractFailureException&#34;&#62;The lock is not currently held by the calling thread.&#60;&#47;exception&#62;&#10; public void Release&#40;&#41;&#10; &#123;&#10; AssertHasLock&#40;&#41;&#59;&#10;&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; this.ReleaseOwnership&#40;&#41;&#59;&#10;&#10; &#47;&#47; Release one waiter&#10; Monitor.Pulse&#40;this.syncLock&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Determine if the lock is currently held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;returns&#62;True if the lock is currently held by the calling thread.&#60;&#47;returns&#62;&#10; public bool LockHeldByMe&#40;&#41;&#10; &#123;&#10; return this.IsOwnedByMe&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Throw an exception if the lock is not held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;Roslyn.Utilities.ContractFailureException&#34;&#62;The lock is not currently held by the calling thread.&#60;&#47;exception&#62;&#10; public void AssertHasLock&#40;&#41;&#10; &#123;&#10; Contract.ThrowIfFalse&#40;LockHeldByMe&#40;&#41;&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Checks if the lock is currently held.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool IsLocked&#10; &#123;&#10; get&#10; &#123;&#10; return this.owningThread &#33;&#61; null&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Checks if the lock is currently held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool IsOwnedByMe&#10; &#123;&#10; get&#10; &#123;&#10; return this.owningThread &#61;&#61; Thread.CurrentThread&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Take ownership of the lock &#40;by the calling thread&#41;. The lock may not already&#10; &#47;&#47;&#47; be held by any other code.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private void TakeOwnership&#40;&#41;&#10; &#123;&#10; Contract.Assert&#40;&#33;this.IsLocked&#41;&#59;&#10; this.owningThread &#61; Thread.CurrentThread&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Release ownership of the lock. The lock must already be held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private void ReleaseOwnership&#40;&#41;&#10; &#123;&#10; Contract.Assert&#40;this.IsOwnedByMe&#41;&#59;&#10; this.owningThread &#61; null&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Action object passed to a cancellation token registration.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private static readonly Action&#60;object&#62; cancellationTokenCanceledEventHandler &#61; new Action&#60;object&#62;&#40;CancellationTokenCanceledEventHandler&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Callback executed when a cancellation token is canceled during a Wait.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;obj&#34;&#62;The syncLock that protects a &#60;see cref&#61;&#34;NonReentrantLock &#34;&#47;&#62; instance.&#60;&#47;param&#62;&#10; private static void CancellationTokenCanceledEventHandler&#40;object obj&#41;&#10; &#123;&#10; lock &#40;obj&#41;&#10; &#123;&#10; &#47;&#47; Release all waiters to check their cancellation tokens.&#10; Monitor.PulseAll&#40;obj&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#125;</pre>2013-05-23T06:05:29-07:00Kahttp://rise4fun.com/Seal/KaSeal/Ka<pre>using System&#59;&#13;&#10;using System.Collections.Generic&#59;&#13;&#10;using System.Linq&#59;&#13;&#10;using System.Text&#59;&#13;&#10;&#13;&#10;namespace TestMscorlibCalls&#13;&#10;&#123;&#13;&#10; public class TestMscorlibCalls&#13;&#10; &#123;&#13;&#10; String f &#61; &#34;Hello&#34;&#59; &#13;&#10; public void foo&#40;List&#60;String&#62; paramList&#41;&#13;&#10; &#123;&#13;&#10; List&#60;TestMscorlibCalls&#62; l &#61; new List&#60;TestMscorlibCalls&#62;&#40;&#41;&#59;&#13;&#10; l.Add&#40;this&#41;&#59;&#13;&#10; foreach &#40;var elem in l&#41;&#13;&#10; &#123;&#13;&#10; elem.f &#61; &#34;Hi&#34;&#59; &#13;&#10; &#125;&#13;&#10; &#125;&#13;&#10;&#13;&#10; public void bar&#40;List&#60;String&#62; paramSet&#41;&#13;&#10; &#123;&#13;&#10; Dictionary&#60;TestMscorlibCalls, String&#62; d &#61; new Dictionary&#60;TestMscorlibCalls, string&#62;&#40;&#41;&#59;&#13;&#10; d.Add&#40;this, this.f&#41;&#59;&#13;&#10; foreach &#40;var key in d.Keys&#41;&#13;&#10; &#123;&#13;&#10; key.f &#61; &#34;Hi&#34;&#59; &#13;&#10; &#125;&#13;&#10; &#125;&#13;&#10;&#13;&#10; &#125;&#13;&#10;&#125;&#13;&#10;</pre>2013-05-23T06:05:29-07:00KAohttp://rise4fun.com/Seal/KAoSeal/KAo<pre>using System&#59;&#10;using System.Collections.Generic&#59;&#10;using System.Runtime.CompilerServices&#59;&#10;using System.Threading&#59;&#10;using System.Threading.Tasks&#59;&#10;&#10;namespace Roslyn.Utilities&#10;&#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Represents a value that can be retrieved synchronously or asynchronously by many clients.&#10; &#47;&#47;&#47; The value will be computed on-demand the moment the first client asks for it. While being&#10; &#47;&#47;&#47; computed, more clients can request the value. As long as there are outstanding clients the&#10; &#47;&#47;&#47; underlying computation will proceed. If all outstanding clients cancel their request then&#10; &#47;&#47;&#47; the underlying value computation will be cancelled as well.&#10; &#47;&#47;&#47; &#10; &#47;&#47;&#47; Creators of an &#60;see cref&#61;&#34;AsyncLazy&#123;T&#125;&#34; &#47;&#62; can specify whether the result of the computation is&#10; &#47;&#47;&#47; cached for future requests or not. Choosing to not cache means the computation functions are kept&#10; &#47;&#47;&#47; alive, whereas caching means the value &#40;but not functions&#41; are kept alive once complete.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; internal sealed class AsyncLazy&#60;T&#62;&#10; &#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The underlying function that starts an asynchronous computation of the resulting value.&#10; &#47;&#47;&#47; Null&#39;ed out once we&#39;ve computed the result and we&#39;ve been asked to cache it. Otherwise,&#10; &#47;&#47;&#47; it is kept around in case the value needs to be computed again.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The underlying function that starts an synchronous computation of the resulting value.&#10; &#47;&#47;&#47; Null&#39;ed out once we&#39;ve computed the result and we&#39;ve been asked to cache it, or if we&#10; &#47;&#47;&#47; didn&#39;t get any synchronous function given to us in the first place.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Func&#60;CancellationToken, T&#62; synchronousComputeFunction&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Whether or not we should keep the value around once we&#39;ve computed it.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private readonly bool cacheResult&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The Task that holds the cached result.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Task&#60;T&#62; cachedResult&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Mutex used to protect reading and writing to all mutable objects and fields. Traces&#10; &#47;&#47;&#47; indicate that there&#39;s negligible contention on this lock, hence we can save some memory&#10; &#47;&#47;&#47; by using a single lock for all AsyncLazy instances. Only trivial and non-reentrant work&#10; &#47;&#47;&#47; should be done while holding the lock.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private static readonly NonReentrantLock gate &#61; new NonReentrantLock&#40;useThisInstanceForSynchronization&#58; true&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The hash set of all currently outstanding asynchronous requests. Null if there are no requests,&#10; &#47;&#47;&#47; and will never be empty.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private HashSet&#60;Request&#62; requests &#61; null&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; If an asynchronous request is active, the CancellationTokenSource that allows for&#10; &#47;&#47;&#47; cancelling the underlying computation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private CancellationTokenSource asynchronousComputationCancellationSource &#61; null&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Whether a computation is active or queued on any thread, whether synchronous or&#10; &#47;&#47;&#47; asynchronous.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool computationActive &#61; false&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Creates an AsyncLazy that always returns the value, analogous to &#60;see cref&#61;&#34;Task.FromResult&#123;T&#125;&#34; &#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public AsyncLazy&#40;T value&#41;&#10; &#123;&#10; this.cacheResult &#61; true&#59;&#10; this.cachedResult &#61; new Task&#60;T&#62;&#40;&#40;&#41; &#61;&#62; &#123;return value&#59;&#125;&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Important&#58; callers of this constructor should ensure that the compute function returns&#10; &#47;&#47;&#47; a task in a non-blocking fashion. i.e. the function should &#42;not&#42; synchronously compute&#10; &#47;&#47;&#47; a value and then return it using Task.FromResult. Instead, it should return an actual&#10; &#47;&#47;&#47; task that operates asynchronously. If this function synchronously computes a value&#10; &#47;&#47;&#47; then that will cause locks to be held in this type for excessive periods of time.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public AsyncLazy&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, bool cacheResult&#41;&#10; &#58; this&#40;asynchronousComputeFunction, synchronousComputeFunction&#58; null, cacheResult&#58; cacheResult&#41;&#10; &#123;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Creates an AsyncLazy that supports both asynchronous computation and inline synchronous&#10; &#47;&#47;&#47; computation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;asynchronousComputeFunction&#34;&#62;A function called to start the asynchronous&#10; &#47;&#47;&#47; computation. This function should be cheap and non-blocking.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;synchronousComputeFunction&#34;&#62;A function to do the work synchronously, which&#10; &#47;&#47;&#47; is allowed to block. This function should not be implemented by a simple Wait on the&#10; &#47;&#47;&#47; asynchronous value. If that&#39;s all you are doing, just don&#39;t pass a synchronous function&#10; &#47;&#47;&#47; in the first place.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;cacheResult&#34;&#62;Whether the result should be cached once the computation is&#10; &#47;&#47;&#47; complete.&#60;&#47;param&#62;&#10; public AsyncLazy&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, Func&#60;CancellationToken, T&#62; synchronousComputeFunction, bool cacheResult&#41;&#10; &#123;&#10; this.asynchronousComputeFunction &#61; asynchronousComputeFunction&#59;&#10; this.synchronousComputeFunction &#61; synchronousComputeFunction&#59;&#10; this.cacheResult &#61; cacheResult&#59;&#10; &#125;&#10;&#10; &#35;region Lock Wrapper for Invariant Checking&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Takes the lock for this object and if acquired validates the invariants of this class.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private WaitThatValidatesInvariants TakeLock&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; gate.Wait&#40;cancellationToken&#41;&#59;&#10; return new WaitThatValidatesInvariants&#40;this&#41;&#59;&#10; &#125;&#10;&#10; private struct WaitThatValidatesInvariants &#58; IDisposable&#10; &#123;&#10; private readonly AsyncLazy&#60;T&#62; asyncLazy&#59;&#10;&#10; public WaitThatValidatesInvariants&#40;AsyncLazy&#60;T&#62; asyncLazy&#41;&#10; &#123;&#10; this.asyncLazy &#61; asyncLazy&#59;&#10; &#125;&#10;&#10; public void Dispose&#40;&#41;&#10; &#123;&#10; gate.Release&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#35;endregion&#10;&#10; public bool TryGetValue&#40;out T result&#41;&#10; &#123;&#10; &#47;&#47; No need to lock here since this is only a fast check to &#10; &#47;&#47; see if the result is already computed.&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; result &#61; cachedResult.Result&#59;&#10; return true&#59;&#10; &#125;&#10;&#10; result &#61; default&#40;T&#41;&#59;&#10; return false&#59;&#10; &#125;&#10;&#10; public T GetValue&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; Request request &#61; null&#59;&#10; AsynchronousComputationToStart&#63; newAsynchronousComputation &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If cached, get immediately&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return cachedResult.Result&#59;&#10; &#125;&#10;&#10; &#47;&#47; If there is an existing computation active, we&#39;ll just create another request&#10; if &#40;computationActive&#41;&#10; &#123;&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10; &#125;&#10; else if &#40;synchronousComputeFunction &#61;&#61; null&#41;&#10; &#123;&#10; &#47;&#47; A synchronous request, but we have no synchronous function. Start off the async work&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10;&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; else&#10; &#123;&#10; &#47;&#47; We will do the computation here&#10; this.computationActive &#61; true&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; If we simply created a new asynchronous request, so wait for it. Yes, we&#39;re blocking the thread&#10; &#47;&#47; but we don&#39;t want multiple threads attempting to compute the same thing.&#10; if &#40;request &#33;&#61; null&#41;&#10; &#123;&#10; request.RegisterForCancellation&#40;OnAsynchronousRequestCancelled, cancellationToken&#41;&#59;&#10;&#10; &#47;&#47; Since we already registered for cancellation, it&#39;s possible that the registration has&#10; &#47;&#47; cancelled this new computation if we were the only requestor.&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; request&#41;&#59;&#10; &#125;&#10;&#10; return WaitAndGetResult&#40;request.Task, cancellationToken&#41;&#59;&#10; &#125;&#10; else&#10; &#123;&#10; T result&#59;&#10;&#10; &#47;&#47; We are the active computation, so let&#39;s go ahead and compute.&#10; try&#10; &#123;&#10; result &#61; synchronousComputeFunction&#40;cancellationToken&#41;&#59;&#10; &#125;&#10; catch &#40;OperationCanceledException&#41;&#10; &#123;&#10; &#47;&#47; This cancelled for some reason. We don&#39;t care why, but&#10; &#47;&#47; it means anybody else waiting for this result isn&#39;t going to get it&#10; &#47;&#47; from us.&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; this.computationActive &#61; false&#59;&#10;&#10; if &#40;requests &#33;&#61; null&#41;&#10; &#123;&#10; &#47;&#47; There&#39;s a possible improvement here&#58; there might be another synchronous caller who&#10; &#47;&#47; also wants the value. We might consider stealing their thread rather than punting&#10; &#47;&#47; to the thread pool.&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; null&#41;&#59;&#10; &#125;&#10;&#10; throw&#59;&#10; &#125;&#10; catch &#40;Exception ex&#41;&#10; &#123;&#10; &#47;&#47; We faulted for some unknown reason. We should simply fault everything.&#10; TaskCompletionSource&#60;T&#62; tcs &#61; new TaskCompletionSource&#60;T&#62;&#40;&#41;&#59;&#10; tcs.SetException&#40;ex&#41;&#59;&#10; CompleteWithTask&#40;tcs.Task, CancellationToken.None&#41;&#59;&#10;&#10; throw&#59;&#10; &#125;&#10;&#10; &#47;&#47; We have a value, so complete&#10; CompleteWithTask&#40;new Task&#60;T&#62;&#40;&#40;&#41;&#61;&#62; result&#41;, CancellationToken.None&#41;&#59;&#10;&#10; return result&#59;&#10; &#125;&#10; &#125;&#10; &#10; private T WaitAndGetResult&#40;Task&#60;T&#62; task, CancellationToken cancellationToken&#41;&#10; &#123;&#10; task.Wait&#40;cancellationToken&#41;&#59;&#10; return task.Result&#59;&#10; &#125;&#10;&#10; private Request CreateNewRequest_NoLock&#40;&#41;&#10; &#123;&#10; if &#40;this.requests &#61;&#61; null&#41;&#10; &#123;&#10; this.requests &#61; new HashSet&#60;Request&#62;&#40;&#41;&#59;&#10; &#125;&#10;&#10; Request request &#61; new Request&#40;&#41;&#59;&#10; this.requests.Add&#40;request&#41;&#59;&#10; return request&#59;&#10; &#125;&#10;&#10; public Task&#60;T&#62; GetValueAsync&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; &#47;&#47; Optimization&#58; if we&#39;re already cancelled, do not pass go&#10; if &#40;cancellationToken.IsCancellationRequested&#41;&#10; &#123;&#10; return new Task&#60;T&#62;&#40;&#40;&#41; &#61;&#62; default&#40;T&#41;, cancellationToken&#41;&#59;&#10; &#125;&#10;&#10; Request request&#59;&#10; AsynchronousComputationToStart&#63; newAsynchronousComputation &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If cached, get immediately&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return cachedResult&#59;&#10; &#125;&#10;&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10;&#10; &#47;&#47; If we have either synchronous or asynchronous work current in flight, we don&#39;t need to do anything.&#10; &#47;&#47; Otherwise, we shall start an asynchronous computation for this&#10; if &#40;&#33;computationActive&#41;&#10; &#123;&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; We now have the request counted for, register for cancellation. It is critical this is&#10; &#47;&#47; done outside the lock, as our reigstration may immediately fire and we want to avoid the&#10; &#47;&#47; reentrancy&#10; request.RegisterForCancellation&#40;OnAsynchronousRequestCancelled, cancellationToken&#41;&#59;&#10;&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; request&#41;&#59;&#10; &#125;&#10;&#10; return request.Task&#59;&#10; &#125;&#10;&#10; private AsynchronousComputationToStart RegisterAsynchronousComputation_NoLock&#40;&#41;&#10; &#123;&#10; this.asynchronousComputationCancellationSource &#61; new CancellationTokenSource&#40;&#41;&#59;&#10; this.computationActive &#61; true&#59;&#10;&#10; return new AsynchronousComputationToStart&#40;this.asynchronousComputeFunction, this.asynchronousComputationCancellationSource&#41;&#59;&#10; &#125;&#10;&#10; private struct AsynchronousComputationToStart&#10; &#123;&#10; public readonly Func&#60;CancellationToken, Task&#60;T&#62;&#62; AsynchronousComputeFunction&#59;&#10; public readonly CancellationTokenSource CancellationTokenSource&#59;&#10;&#10; public AsynchronousComputationToStart&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, CancellationTokenSource cancellationTokenSource&#41;&#10; &#123;&#10; this.AsynchronousComputeFunction &#61; asynchronousComputeFunction&#59;&#10; this.CancellationTokenSource &#61; cancellationTokenSource&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void StartAsynchronousComputation&#40;AsynchronousComputationToStart computationToStart, Request requestToCompleteSynchronously&#41;&#10; &#123;&#10; var cancellationToken &#61; computationToStart.CancellationTokenSource.Token&#59;&#10;&#10; try&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; DO NOT ACCESS ANY FIELDS OR STATE BEYOND THIS POINT. Since this function&#10; &#47;&#47; runs unsynchronized, it&#39;s possible that during this function this request&#10; &#47;&#47; might be cancelled, and then a whole additional request might start and&#10; &#47;&#47; complete inline, and cache the result. By grabbing state before we check&#10; &#47;&#47; the cancellation token, we can be assured that we are only operating on&#10; &#47;&#47; a state that was complete.&#10;&#10;&#10; &#47;&#47; We avoid creating a full closure just to pass the token along&#10; &#47;&#47; Also, use TaskContinuationOptions.ExecuteSynchronously so that we inline &#10; &#47;&#47; the continuation if asynchronousComputeFunction completes synchronously&#10; var task &#61; computationToStart.AsynchronousComputeFunction&#40;cancellationToken&#41;&#59;&#10;&#10; task.ContinueWith&#40;&#10; &#40;t&#41; &#61;&#62; CompleteWithTask&#40;t, computationToStart.CancellationTokenSource.Token&#41;,&#10; cancellationToken,&#10; TaskContinuationOptions.ExecuteSynchronously,&#10; TaskScheduler.Default&#41;&#59;&#10;&#10; if &#40;requestToCompleteSynchronously &#33;&#61; null &#38;&#38; task.IsCompleted&#41;&#10; &#123;&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; task &#61; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;task&#41;&#59;&#10; &#125;&#10;&#10; requestToCompleteSynchronously.CompleteFromTaskSynchronously&#40;task&#41;&#59;&#10; &#125;&#10;&#10; &#125;&#10; catch &#40;OperationCanceledException oce&#41;&#10; &#123;&#10; &#47;&#47; As long as it&#39;s the right token, this means that our thread was the first thread&#10; &#47;&#47; to start an asynchronous computation, but the requestor cancelled as we were starting up&#10; &#47;&#47; the computation.&#10; if &#40;oce.CancellationToken &#33;&#61; cancellationToken&#41;&#10; &#123;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; private void CompleteWithTask&#40;Task&#60;T&#62; task, CancellationToken cancellationToken&#41;&#10; &#123;&#10; IEnumerable&#60;Request&#62; requestsToComplete&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If the underlying computation was cancelled, then all state was already updated in OnAsynchronousRequestCancelled&#10; &#47;&#47; and there is no new work to do here. We &#42;must&#42; use the local one since this completion may be running far after&#10; &#47;&#47; the background computation was cancelled and a new one might have already been enqueued. We must do this&#10; &#47;&#47; check here under the lock to ensure proper synchronization with OnAsynchronousRequestCancelled.&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; The computation is complete, so get all requests to complete and null out the list. We&#39;ll create another one&#10; &#47;&#47; later if it&#39;s needed&#10; requestsToComplete &#61; this.requests &#63;&#63; new List&#60;Request&#62;&#40;&#41; as IEnumerable&#60;Request&#62;&#59;&#10; this.requests &#61; null&#59;&#10;&#10; &#47;&#47; The computations are done&#10; this.asynchronousComputationCancellationSource &#61; null&#59;&#10; this.computationActive &#61; false&#59;&#10; task &#61; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;task&#41;&#59;&#10; &#125;&#10;&#10; foreach &#40;var requestToComplete in requestsToComplete&#41;&#10; &#123;&#10; requestToComplete.CompleteFromTaskAsynchronously&#40;task&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; private Task&#60;T&#62; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; if &#40;this.cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return this.cachedResult&#59;&#10; &#125;&#10; else&#10; &#123;&#10; if &#40;cacheResult &#38;&#38; task.Status &#61;&#61; TaskStatus.RanToCompletion&#41;&#10; &#123;&#10; &#47;&#47; Hold onto the completed task. We can get rid of the computation functions for good&#10; this.cachedResult &#61; task&#59;&#10; this.asynchronousComputeFunction &#61; null&#59;&#10; this.synchronousComputeFunction &#61; null&#59;&#10; &#125;&#10;&#10; return task&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void OnAsynchronousRequestCancelled&#40;object state&#41;&#10; &#123;&#10; var request &#61; &#40;Request&#41;state&#59;&#10; CancellationTokenSource cancellationTokenSource &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; &#47;&#47; Now try to remove it. It&#39;s possible that requests may already be null. You could&#10; &#47;&#47; imagine that cancellation was requested, but before we could aquire the lock&#10; &#47;&#47; here the computation completed and the entire CompleteWithTask synchronized&#10; &#47;&#47; block ran. In that case, the requests collection may already be null, or it&#10; &#47;&#47; &#40;even scarier&#33;&#41; may have been replaced with another collection because another&#10; &#47;&#47; computation has started.&#10; if &#40;this.requests &#33;&#61; null&#41;&#10; &#123;&#10; if &#40;this.requests.Remove&#40;request&#41;&#41;&#10; &#123;&#10; if &#40;this.requests.Count &#61;&#61; 0&#41;&#10; &#123;&#10; this.requests &#61; null&#59;&#10;&#10; if &#40;this.asynchronousComputationCancellationSource &#33;&#61; null&#41;&#10; &#123;&#10; cancellationTokenSource &#61; this.asynchronousComputationCancellationSource&#59;&#10; this.asynchronousComputationCancellationSource &#61; null&#59;&#10; this.computationActive &#61; false&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; request.CancelAsynchronously&#40;&#41;&#59;&#10;&#10; if &#40;cancellationTokenSource &#33;&#61; null&#41;&#10; &#123;&#10; cancellationTokenSource.Cancel&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; Using inheritance instead of wrapping a TaskCompletionSource to avoid a second allocation&#10; private class Request &#58; TaskCompletionSource&#60;T&#62;&#10; &#123;&#10; private CancellationTokenRegistration cancellationTokenRegistration&#59;&#10;&#10; public void RegisterForCancellation&#40;Action&#60;object&#62; callback, CancellationToken cancellationToken&#41;&#10; &#123;&#10; cancellationTokenRegistration &#61; cancellationToken.Register&#40;callback, this&#41;&#59;&#10; &#125;&#10;&#10; public void CompleteFromTaskAsynchronously&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; System.Threading.Tasks.Task.Factory.StartNew&#40;CompleteFromTaskSynchronouslyStub, task, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default&#41;&#59;&#10; &#125;&#10;&#10; private void CompleteFromTaskSynchronouslyStub&#40;object task&#41;&#10; &#123;&#10; CompleteFromTaskSynchronously&#40;&#40;Task&#60;T&#62;&#41;task&#41;&#59;&#10; &#125;&#10;&#10; public void CompleteFromTaskSynchronously&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; if &#40;task.Status &#61;&#61; TaskStatus.RanToCompletion&#41;&#10; &#123;&#10; if &#40;TrySetResult&#40;task.Result&#41;&#41;&#10; &#123;&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; else if &#40;task.Status &#61;&#61; TaskStatus.Faulted&#41;&#10; &#123;&#10; if &#40;TrySetException&#40;task.Exception&#41;&#41;&#10; &#123;&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; else&#10; &#123;&#10; CancelSynchronously&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; public void CancelAsynchronously&#40;&#41;&#10; &#123;&#10; &#47;&#47; Since there could be synchronous continuations on the TaskCancellationSource, we queue this to the threadpool&#10; &#47;&#47; to avoid inline running of other operations.&#10; System.Threading.Tasks.Task.Factory.StartNew&#40;CancelSynchronously, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default&#41;&#59;&#10; &#125;&#10;&#10; private void CancelSynchronously&#40;&#41;&#10; &#123;&#10; if &#40;TrySetCanceled&#40;&#41;&#41;&#10; &#123;&#10; &#47;&#47; Paranoia&#58; the only reason we should ever get here is if the CancellationToken that&#10; &#47;&#47; we registered against was cancelled, but just in case, dispose the registration&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; A lightweight mutual exclusion object which supports waiting with cancellation and prevents&#10; &#47;&#47;&#47; recursion &#40;i.e. you may not call Wait if you already hold the lock&#41;&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; The &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62; provides a lightweight mutual exclusion class that doesn&#39;t&#10; &#47;&#47;&#47; use Windows kernel synchronization primitives.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; The implementation is distilled from the workings of &#60;see cref&#61;&#34;T&#58;System.Threading.SemaphoreSlim&#34;&#47;&#62;&#10; &#47;&#47;&#47; The basic idea is that we use a regular sync object &#40;Monitor.Enter&#47;Exit&#41; to guard the setting&#10; &#47;&#47;&#47; of an &#39;owning thread&#39; field. If, during the Wait, we find the lock is held by someone else&#10; &#47;&#47;&#47; then we register a cancellation callback and enter a &#34;Monitor.Wait&#34; loop. If the cancellation&#10; &#47;&#47;&#47; callback fires, then it &#34;pulses&#34; all the waiters to wake them up and check for cancellation.&#10; &#47;&#47;&#47; Waiters are also &#34;pulsed&#34; when leaving the lock.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; All public members of &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62; are thread-safe and may be used concurrently&#10; &#47;&#47;&#47; from multiple threads.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; internal sealed class NonReentrantLock&#10; &#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; A synchronization object to protect access to the &#60;see cref&#61;&#34;F&#58;owningThread&#34;&#47;&#62; field and to be pulsed&#10; &#47;&#47;&#47; when &#60;see cref&#61;&#34;M&#58;Release&#34;&#47;&#62; is called and during cancellation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private readonly object syncLock&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Indicates which thread currently holds the lock. If null, then the lock is available.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private volatile Thread owningThread&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Constructor.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;useThisInstanceForSynchronization&#34;&#62;If false &#40;the default&#41;, then the class&#10; &#47;&#47;&#47; allocates an internal object to be used as a sync lock.&#10; &#47;&#47;&#47; If true, then the sync lock object will be the NonReentrantLock instance itself. This&#10; &#47;&#47;&#47; saves an allocation but a client may not safely further use this instance in a call to&#10; &#47;&#47;&#47; Monitor.Enter&#47;Exit or in a &#34;lock&#34; statement.&#10; &#47;&#47;&#47; &#60;&#47;param&#62;&#10; public NonReentrantLock&#40;bool useThisInstanceForSynchronization &#61; false&#41;&#10; &#123;&#10; this.syncLock &#61; useThisInstanceForSynchronization &#63; this &#58; new object&#40;&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Shared factory for use in lazy initialization.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public static readonly Func&#60;NonReentrantLock&#62; Factory &#61; &#40;&#41; &#61;&#62; new NonReentrantLock&#40;useThisInstanceForSynchronization&#58; true&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Blocks the current thread until it can enter the &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62;, while observing a&#10; &#47;&#47;&#47; &#60;see cref&#61;&#34;T&#58;System.Threading.CancellationToken&#34;&#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; Recursive locking is not supported. i.e. A thread may not call Wait successfully twice without an&#10; &#47;&#47;&#47; intervening &#60;see cref&#61;&#34;Release&#34;&#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;cancellationToken&#34;&#62;The &#60;see cref&#61;&#34;T&#58;System.Threading.CancellationToken&#34;&#47;&#62; token to&#10; &#47;&#47;&#47; observe.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;System.OperationCanceledException&#34;&#62;&#60;paramref name&#61;&#34;cancellationToken&#34;&#47;&#62; was&#10; &#47;&#47;&#47; canceled.&#60;&#47;exception&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;System.LockRecursionException&#34;&#62;The caller already holds the lock&#60;&#47;exception&#62;&#10; public void Wait&#40;CancellationToken cancellationToken &#61; default&#40;CancellationToken&#41;&#41;&#10; &#123;&#10; if &#40;this.IsOwnedByMe&#41;&#10; &#123;&#10; throw new LockRecursionException&#40;&#41;&#59;&#10; &#125;&#10;&#10; CancellationTokenRegistration cancellationTokenRegistration &#61; default&#40;CancellationTokenRegistration&#41;&#59;&#10;&#10; if &#40;cancellationToken.CanBeCanceled&#41;&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; Fast path to try and avoid allocations in callback registration.&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; if &#40;&#33;this.IsLocked&#41;&#10; &#123;&#10; this.TakeOwnership&#40;&#41;&#59;&#10; return&#59;&#10; &#125;&#10; &#125;&#10;&#10; cancellationTokenRegistration &#61; cancellationToken.Register&#40;cancellationTokenCanceledEventHandler, this.syncLock, useSynchronizationContext&#58; false&#41;&#59;&#10; &#125;&#10;&#10; using &#40;cancellationTokenRegistration&#41;&#10; &#123;&#10; &#47;&#47; PERF&#58; First spin wait for the lock to become available, but only up to the first planned yield.&#10; &#47;&#47; This additional amount of spinwaiting was inherited from SemaphoreSlim&#39;s implementation where&#10; &#47;&#47; it showed measurable perf gains in test scenarios.&#10; SpinWait spin &#61; new SpinWait&#40;&#41;&#59;&#10; while &#40;this.IsLocked &#38;&#38; &#33;spin.NextSpinWillYield&#41;&#10; &#123;&#10; spin.SpinOnce&#40;&#41;&#59;&#10; &#125;&#10;&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; while &#40;this.IsLocked&#41;&#10; &#123;&#10; &#47;&#47; If cancelled, we throw. Trying to wait could lead to deadlock.&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10;&#10; &#47;&#47; Another thread holds the lock. Wait until we get awoken either&#10; &#47;&#47; by some code calling &#34;Release&#34; or by cancellation.&#10; Monitor.Wait&#40;this.syncLock&#41;&#59;&#10;&#10; &#125;&#10;&#10; &#47;&#47; We now hold the lock&#10; this.TakeOwnership&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Exit the mutual exclusion.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; The calling thread must currently hold the lock.&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;Roslyn.Utilities.ContractFailureException&#34;&#62;The lock is not currently held by the calling thread.&#60;&#47;exception&#62;&#10; public void Release&#40;&#41;&#10; &#123;&#10; AssertHasLock&#40;&#41;&#59;&#10;&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; this.ReleaseOwnership&#40;&#41;&#59;&#10;&#10; &#47;&#47; Release one waiter&#10; Monitor.Pulse&#40;this.syncLock&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Determine if the lock is currently held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;returns&#62;True if the lock is currently held by the calling thread.&#60;&#47;returns&#62;&#10; public bool LockHeldByMe&#40;&#41;&#10; &#123;&#10; return this.IsOwnedByMe&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Throw an exception if the lock is not held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;Roslyn.Utilities.ContractFailureException&#34;&#62;The lock is not currently held by the calling thread.&#60;&#47;exception&#62;&#10; public void AssertHasLock&#40;&#41;&#10; &#123;&#10;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Checks if the lock is currently held.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool IsLocked&#10; &#123;&#10; get&#10; &#123;&#10; return this.owningThread &#33;&#61; null&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Checks if the lock is currently held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool IsOwnedByMe&#10; &#123;&#10; get&#10; &#123;&#10; return this.owningThread &#61;&#61; Thread.CurrentThread&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Take ownership of the lock &#40;by the calling thread&#41;. The lock may not already&#10; &#47;&#47;&#47; be held by any other code.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private void TakeOwnership&#40;&#41;&#10;&#123;&#10; this.owningThread &#61; Thread.CurrentThread&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Release ownership of the lock. The lock must already be held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private void ReleaseOwnership&#40;&#41;&#10; &#123;&#10;&#10; this.owningThread &#61; null&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Action object passed to a cancellation token registration.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private static readonly Action&#60;object&#62; cancellationTokenCanceledEventHandler &#61; new Action&#60;object&#62;&#40;CancellationTokenCanceledEventHandler&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Callback executed when a cancellation token is canceled during a Wait.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;obj&#34;&#62;The syncLock that protects a &#60;see cref&#61;&#34;NonReentrantLock &#34;&#47;&#62; instance.&#60;&#47;param&#62;&#10; private static void CancellationTokenCanceledEventHandler&#40;object obj&#41;&#10; &#123;&#10; lock &#40;obj&#41;&#10; &#123;&#10; &#47;&#47; Release all waiters to check their cancellation tokens.&#10; Monitor.PulseAll&#40;obj&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#125;</pre>2013-05-23T06:05:29-07:00k1http://rise4fun.com/Seal/k1Seal/k1<pre>using System&#59;&#10;using System.Collections.Generic&#59;&#10;using System.Linq&#59;&#10;&#10;public class LinqTest&#10;&#123;&#10; int f&#59;&#10; private void foo&#40;LinqTest param&#41;&#123;&#10; var col &#61; from i in IntegerGenerator&#40;param&#41;&#10; select i&#59;&#10; foreach &#40;var y in col&#41;&#10; &#123;&#10; y.f &#61; 1&#59;&#10; &#125;&#10; &#125;&#10;&#10; private IEnumerable&#60;LinqTest&#62; IntegerGenerator&#40;LinqTest r&#41; &#123;&#10; yield return r&#59;&#10; &#125;&#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00JYhttp://rise4fun.com/Seal/JYSeal/JY<pre>using System&#59;&#10;class Test &#123;&#10; public Test f &#61; null&#59;&#10; public void PureMethod&#40;Test param&#41;&#123;&#10; var temp &#61; new Test&#40;&#41;&#59;&#10; temp.f &#61; param&#59; &#10; &#47;&#47;Console.WriteLine&#40;&#34;Test&#34;&#41;&#59;&#10; f &#61; temp. &#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00Gphttp://rise4fun.com/Seal/GpSeal/Gp<pre>using System&#59;&#13;&#10;class TestAliasing &#123;&#13;&#10; TestAliasing f &#61; null&#59;&#13;&#10; public void A&#40;&#41; &#123;&#13;&#10; TestAliasing inst2 &#61; new TestAliasing&#40;&#41;&#59;&#13;&#10; var local &#61; B&#40;inst2, inst2, this&#41;&#59;&#13;&#10; local.f &#61; new TestAliasing&#40;&#41;&#59;&#13;&#10; &#125;&#13;&#10; public TestAliasing B&#40;TestAliasing p1, TestAliasing p2, TestAliasing p3&#41; &#123;&#13;&#10; p2.f &#61; p3&#59;&#13;&#10; return p1.f&#59;&#13;&#10; &#125;&#13;&#10;&#125;&#13;&#10;</pre>2013-05-23T06:05:29-07:00gpEhttp://rise4fun.com/Seal/gpESeal/gpE<pre>using System&#59;&#10;class Test &#123;&#10; public Test f &#61; null&#59;&#10; public Test PureMethod&#40;Test param&#41;&#123;&#10; var temp &#61; new Test&#40;&#41;&#59;&#10; temp.f &#61; param&#59;&#10; return temp.f&#59; &#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00EZLhttp://rise4fun.com/Seal/EZLSeal/EZL<pre>using System&#59;&#10;class Test &#123;&#10; public Test f &#61; null&#59;&#10; public void PureMethod&#40;Test param&#41;&#123;&#10; var temp &#61; new Test&#40;&#41;&#59;&#10; temp.f &#61; param&#59; &#10; &#125; &#10;&#125;</pre>2013-05-23T06:05:29-07:00Eg4http://rise4fun.com/Seal/Eg4Seal/Eg4<pre>using System&#59;&#10;class Test &#123;&#10; public object kp &#61; null&#59;&#10; public void PureMethod&#40;Test param&#41;&#123;&#10; var temp &#61; new Test&#40;&#41;&#59;&#10; temp.f &#61; param&#59; &#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:009uhttp://rise4fun.com/Seal/9uSeal/9u<pre>using System&#59;&#10;class Test &#123;&#10; public Test f &#61; null&#59;&#10; public void PureMethod&#40;Test param&#41;&#123;&#10; var temp &#61; new Test&#40;&#41;&#59;&#10; temp.f &#61; param&#59; &#10; f &#61; null&#59; &#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:0097http://rise4fun.com/Seal/97Seal/97<pre>using System&#59;&#10;&#10;class XYZ &#123;&#10; public static int add1&#40;int x, int y&#41; &#123; return x&#43;y&#59; &#125;&#10; int f1, f2&#59;&#10; public static int add2&#40;XYZ p&#41; &#123; return p.f1 &#43; p.f2&#59; &#125;&#10; public void modify&#40;&#41; &#123; f1 &#61; f1 &#43; f2&#59; &#125;&#10; public void localModify&#40;&#41; &#123;&#10; XYZ temp &#61; new XYZ&#40;&#41;&#59;&#10; temp.modify&#40;&#41;&#59;&#10; &#125;&#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:0095http://rise4fun.com/Seal/95Seal/95<pre>using System&#59;&#10;class Test &#123;&#10; public Test f &#61; null&#59;&#10; public void ImpureMethod&#40;Test param&#41;&#123;&#10; int i &#61; 0&#59;&#10; while &#40;i &#60; 100&#41; &#123;&#10; &#43;&#43;i&#59;&#10; &#125;&#10; if &#40;i &#60; 0&#41; &#123;&#10; param.f &#61; null&#59;&#10; &#125;&#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:007http://rise4fun.com/Seal/7Seal/7<pre>using System&#59;&#10;class Test &#123;&#10; private int f &#61; 0&#59; &#10; public void C&#40;Test inst3&#41;&#123; &#10; A&#40;new Test&#40;&#41;&#41;&#59;&#10; var temp &#61; inst3.foo&#40;&#41;&#59;&#10; temp.f &#61; 3&#59;&#10; &#125; &#10; public void A&#40;Test inst&#41;&#123;&#10; inst.f &#61; 1&#59;&#10; &#125; &#10; public Test foo&#40;&#41;&#123;&#10; return this&#59;&#10; &#125;&#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:007xhttp://rise4fun.com/Seal/7xSeal/7x<pre>using System&#59;&#10;&#10;class XYZ &#123;&#10; public static int add1&#40;int x, int y&#41; &#123; return x&#43;y&#59; &#125;&#10; int f1, f2&#59;&#10; public int add2&#40;&#41; &#123; return f1&#43;f2&#59; &#125;&#10; public void modify&#40;&#41; &#123; f1 &#61; f1 &#43; f2&#59; &#125;&#10; public void localModify&#40;&#41; &#123;&#10; XYZ temp &#61; new XYZ&#40;&#41;&#59;&#10; temp.modify&#40;&#41;&#59;&#10; &#125;&#10;&#125;&#10;&#10;</pre>2013-05-23T06:05:29-07:007Ghttp://rise4fun.com/Seal/7GSeal/7G<pre>using System&#59;&#10;class Test &#123;&#10; public Test f &#61; null&#59;&#10; public void ImpureMethod&#40;Test param&#41;&#123;&#10; Console.Write&#40;&#34;r&#34;&#41;&#59; &#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:0077http://rise4fun.com/Seal/77Seal/77<pre>using System&#59;&#10;public class Foo &#123;&#10; int field&#59;&#10; &#10; void Bar&#40;Foo foo&#41; &#123;&#10; int i &#61; 2 &#43; 2&#59;&#10; if &#40;i &#33;&#61; 4&#41;&#10; foo.field &#43;&#61; 1&#59;&#10; &#125;&#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:0068http://rise4fun.com/Seal/68Seal/68<pre>using System&#59;&#10;&#10;class XYZ &#123;&#10; public static int add1&#40;int x, int y&#41; &#123; return x&#43;y&#59; &#125;&#10; int f1, f2&#59;&#10; public static int add2&#40;XYZ p&#41; &#123; return p.f1 &#43; p.f2&#59; &#125;&#10; public static void modify&#40;XYZ p&#41; &#123; p.f1 &#61; p.f1 &#43; p.f2&#59; &#125;&#10; public void localModify&#40;&#41; &#123;&#10; XYZ temp &#61; new XYZ&#40;&#41;&#59;&#10; modify&#40;temp&#41;&#59;&#10; &#125;&#10;&#125;&#10;&#10;&#10;</pre>2013-05-23T06:05:29-07:005Ehttp://rise4fun.com/Seal/5ESeal/5E<pre>using System.Collections.Generic&#59;&#13;&#10;using System&#59;&#13;&#10;using System.Linq&#59;&#13;&#10;using System.Collections&#59;&#13;&#10;using System.Collections.Generic&#59;&#13;&#10;using System.Collections.ObjectModel&#59;&#13;&#10;&#13;&#10;namespace GameOfLife.Model&#13;&#10;&#123;&#13;&#10; &#47;&#47;&#47; &#60;summary&#62;&#13;&#10; &#47;&#47;&#47; Interface contract for a cell.&#13;&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#13;&#10; public interface ICell&#13;&#10; &#123;&#13;&#10; &#47;&#47;&#47; &#60;summary&#62;&#13;&#10; &#47;&#47;&#47; Determines whether cell is alive or not.&#13;&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#13;&#10; bool&#63; IsAlive &#123; get&#59; set&#59; &#125;&#13;&#10;&#13;&#10; &#47;&#47;&#47; &#60;summary&#62;&#13;&#10; &#47;&#47;&#47; List of all neighbours of the cell.&#13;&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#13;&#10; IList&#60;ICell&#62; Neighbours &#123; get&#59; &#125;&#13;&#10; &#125;&#13;&#10;&#125;&#13;&#10;&#13;&#10;&#13;&#10;namespace GameOfLife.Model&#13;&#10;&#123;&#13;&#10; &#47;&#47;&#47; &#60;summary&#62;&#13;&#10; &#47;&#47;&#47; Provides methods to manage the life of cells.&#13;&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#13;&#10; static class CellLifeManager&#13;&#10; &#123;&#13;&#10; &#35;region Public methods&#13;&#10;&#13;&#10; &#47;&#47;&#47; &#60;summary&#62;&#13;&#10; &#47;&#47;&#47; Calculates and assigns the neighbours for the given collection of cells.&#13;&#10; &#47;&#47;&#47; &#60;para&#62;&#60;remarks&#62;The algorithmic complexity of the method is O&#40;n&#94;2&#41;.&#60;&#47;remarks&#62;&#60;&#47;para&#62;&#13;&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#13;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;theCellCollection&#34;&#62;The read-only collection of the cells.&#60;&#47;param&#62;&#13;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;theTotalColumns&#34;&#62;Total no. of columns in which cells are arranged.&#60;&#47;param&#62;&#13;&#10; public static void SetNeighbours&#40;ReadOnlyCollection&#60;ICell&#62; theCellCollection, int theTotalColumns&#41;&#13;&#10; &#123;&#13;&#10; for &#40;int aCount &#61; 0&#59; aCount &#60; theCellCollection.Count&#59; aCount&#43;&#43;&#41;&#13;&#10; &#123;&#13;&#10; ICell aCell &#61; theCellCollection&#91;aCount&#93;&#59;&#13;&#10; bool aIsFirstColumnCell &#61; aCount &#37; theTotalColumns &#61;&#61; 0&#59;&#13;&#10; bool aIsLastColumnCell &#61; &#40;aCount &#43; 1&#41; &#37; theTotalColumns &#61;&#61; 0&#59;&#13;&#10; IEnumerable&#60;int&#62; aNeighbourIndex&#59;&#13;&#10; if &#40;aIsFirstColumnCell&#41;&#13;&#10; &#123;&#13;&#10; aNeighbourIndex &#61; TraverseFullAntiClockWise&#40;aCount, theTotalColumns&#41;&#13;&#10; .Where&#40;T &#61;&#62; &#40;T &#62;&#61; 0 &#38;&#38; T &#60; theCellCollection.Count &#38;&#38;&#13;&#10; &#40;T &#33;&#61; aCount &#43; theTotalColumns - 1&#41; &#38;&#38; &#40;T &#33;&#61; aCount - theTotalColumns - 1&#41;&#13;&#10; &#38;&#38; &#40;T &#33;&#61; aCount - 1&#41;&#41;&#41;&#59;&#13;&#10; &#125;&#13;&#10; else if &#40;aIsLastColumnCell&#41;&#13;&#10; &#123;&#13;&#10; aNeighbourIndex &#61; TraverseFullAntiClockWise&#40;aCount, theTotalColumns&#41;&#13;&#10; .Where&#40;T &#61;&#62; &#40;T &#62;&#61; 0 &#38;&#38; T &#60; theCellCollection.Count &#38;&#38;&#13;&#10; &#40;T &#33;&#61; aCount &#43; theTotalColumns &#43; 1&#41; &#38;&#38; &#40;T &#33;&#61; aCount - theTotalColumns &#43; 1&#41;&#13;&#10; &#38;&#38; &#40;T &#33;&#61; aCount &#43; 1&#41;&#41;&#41;&#59;&#13;&#10; &#125;&#13;&#10; else&#13;&#10; &#123;&#13;&#10; aNeighbourIndex &#61; TraverseFullAntiClockWise&#40;aCount, theTotalColumns&#41;&#13;&#10; .Where&#40;T &#61;&#62; &#40;T &#62;&#61; 0 &#38;&#38; T &#60; theCellCollection.Count&#41;&#41;&#59;&#13;&#10; &#125;&#13;&#10; aCell.Neighbours.Clear&#40;&#41;&#59;&#13;&#10; foreach &#40;int aIndex in aNeighbourIndex&#41;&#13;&#10; &#123;&#13;&#10; aCell.Neighbours.Add&#40;theCellCollection&#91;aIndex&#93;&#41;&#59;&#13;&#10; &#125;&#13;&#10; &#125;&#13;&#10; &#125;&#13;&#10;&#13;&#10; &#47;&#47;&#47; &#60;summary&#62;&#13;&#10; &#47;&#47;&#47; Sets the alive status of the cells based on following rules &#58;&#13;&#10; &#47;&#47;&#47; &#60;para&#62;&#60;&#47;para&#62;&#13;&#10; &#47;&#47;&#47; &#60;para&#62;&#60;&#47;para&#62;&#13;&#10; &#47;&#47;&#47; &#60;para&#62;&#60;&#47;para&#62;&#13;&#10; &#47;&#47;&#47; &#60;para&#62;&#60;remarks&#62;The algorithmic complexity of the method is O&#40;n&#41;.&#60;&#47;remarks&#62;&#60;&#47;para&#62;&#13;&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#13;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;theCellCollection&#34;&#62;The readonly collection of the cells.&#60;&#47;param&#62;&#13;&#10; public static void SetAliveStatus&#40;ReadOnlyCollection&#60;ICell&#62; theCellCollection&#41;&#13;&#10; &#123;&#13;&#10; int aFinalCount &#61; theCellCollection.Count&#59;&#13;&#10; IList&#60;bool&#62; aIsAliveList &#61; new List&#60;bool&#62;&#40;&#41;.PopulateDefaultValues&#40;aFinalCount, false&#41;&#59;&#13;&#10;&#13;&#10; for &#40;int i &#61; 0&#59; i &#60; aFinalCount&#59; i&#43;&#43;&#41;&#13;&#10; &#123;&#13;&#10; aIsAliveList&#91;i&#93; &#61; ShouldBeAlive&#40;theCellCollection&#91;i&#93;&#41;&#59;&#13;&#10; &#125;&#13;&#10;&#13;&#10; for &#40;int i &#61; 0&#59; i &#60; aFinalCount&#59; i&#43;&#43;&#41;&#13;&#10; &#123;&#13;&#10; theCellCollection&#91;i&#93;.IsAlive &#61; null&#59;&#13;&#10; theCellCollection&#91;i&#93;.IsAlive &#61; aIsAliveList&#91;i&#93;&#59;&#13;&#10; &#125;&#13;&#10; &#125;&#13;&#10; &#47;&#47;&#47; &#60;summary&#62;&#13;&#10; &#47;&#47;&#47; Makes the alive status of all cells to be indeterminate. Value is &#60;see cref&#61;&#34;System.Null&#34;&#47;&#62;&#13;&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#13;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;theCellCollection&#34;&#62;The read-only collection of the cells.&#60;&#47;param&#62;&#13;&#10; public static void ResetCellAliveStatus&#40;ReadOnlyCollection&#60;ICell&#62; theCellCollection&#41;&#13;&#10; &#123;&#13;&#10; foreach &#40;ICell aCell in theCellCollection&#41;&#13;&#10; &#123;&#13;&#10; aCell.IsAlive &#61; null&#59;&#13;&#10; &#125;&#13;&#10; &#125;&#13;&#10;&#13;&#10; &#35;endregion&#13;&#10;&#13;&#10; &#35;region Private methods&#13;&#10;&#13;&#10; &#47;&#47;&#47; &#60;summary&#62;&#13;&#10; &#47;&#47;&#47; Generates a list of all possible neighbours for a particular cell based upon the cells position.&#13;&#10; &#47;&#47;&#47; Calculation is made starting from top , traversing in anti-clockwise direction.&#13;&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#13;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;theIndex&#34;&#62;The zero based position of the cell in the grid.&#60;&#47;param&#62;&#13;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;theTotalColumns&#34;&#62;Total no. of columns in which cells are arranged.&#60;&#47;param&#62;&#13;&#10; &#47;&#47;&#47; &#60;returns&#62;List&#123;int&#125; containing all possible neighbours list.&#60;&#47;returns&#62;&#13;&#10; private static List&#60;int&#62; TraverseFullAntiClockWise&#40;int theIndex, int theTotalColumns&#41;&#13;&#10; &#123;&#13;&#10; return new List&#60;int&#62;&#40;&#41;&#13;&#10; &#123;&#13;&#10; theIndex &#43; theTotalColumns - 1,&#13;&#10; theIndex &#43; theTotalColumns,&#13;&#10; theIndex &#43; theTotalColumns &#43; 1,&#13;&#10; theIndex &#43; 1,&#13;&#10; theIndex - theTotalColumns &#43; 1,&#13;&#10; theIndex - theTotalColumns,&#13;&#10; theIndex - theTotalColumns - 1,&#13;&#10; theIndex - 1&#13;&#10; &#125;&#59;&#13;&#10; &#125;&#13;&#10;&#13;&#10; &#47;&#47;&#47; &#60;summary&#62;&#13;&#10; &#47;&#47;&#47; Determines whether a cell should be alive or not.&#13;&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#13;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;theCell&#34;&#62;The cell which alive status has to be determined.&#60;&#47;param&#62;&#13;&#10; &#47;&#47;&#47; &#60;returns&#62;True if alive or vice versa.&#60;&#47;returns&#62;&#13;&#10; private static bool ShouldBeAlive&#40;ICell theCell&#41;&#13;&#10; &#123;&#13;&#10; bool aIsAlive &#61; theCell.IsAlive.HasValue &#38;&#38; theCell.IsAlive.Value&#59;&#13;&#10; int aAliveNeighboursCount &#61; theCell.Neighbours.Where&#40;T &#61;&#62; T.IsAlive.HasValue &#38;&#38; T.IsAlive.Value&#41;.Count&#40;&#41;&#59;&#13;&#10; if &#40;&#40;aIsAlive &#38;&#38; &#40;aAliveNeighboursCount &#61;&#61; 2 &#124;&#124; aAliveNeighboursCount &#61;&#61; 3&#41;&#41;&#13;&#10; &#124;&#124; &#40;aAliveNeighboursCount &#61;&#61; 3 &#38;&#38; &#33;aIsAlive&#41;&#41;&#13;&#10; &#123;&#13;&#10; return true&#59;&#13;&#10; &#125;&#13;&#10; return false&#59;&#13;&#10; &#125;&#13;&#10;&#13;&#10; &#47;&#47;&#47; &#60;summary&#62;&#13;&#10; &#47;&#47;&#47; Extension method to create a boolean list of specified count with specified default value.&#13;&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#13;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;theList&#34;&#62;The list to be populated.&#60;&#47;param&#62;&#13;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;theFinalCount&#34;&#62;Total no of elements to be added.&#60;&#47;param&#62;&#13;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;theDefaultValue&#34;&#62;Default value for the element.&#60;&#47;param&#62;&#13;&#10; &#47;&#47;&#47; &#60;returns&#62;&#60;&#47;returns&#62;&#13;&#10; private static IList&#60;bool&#62; PopulateDefaultValues&#40;this IList&#60;bool&#62; theList, int theFinalCount, bool theDefaultValue&#41;&#13;&#10; &#123;&#13;&#10; for &#40;int i &#61; 0&#59; i &#60; theFinalCount&#59; i&#43;&#43;&#41;&#13;&#10; &#123;&#13;&#10; theList.Add&#40;theDefaultValue&#41;&#59;&#13;&#10; &#125;&#13;&#10; return theList&#59;&#13;&#10; &#125;&#13;&#10; &#35;endregion&#13;&#10; &#125;&#13;&#10;&#125;&#13;&#10;</pre>2013-05-23T06:05:29-07:004rThttp://rise4fun.com/Seal/4rTSeal/4rT<pre>using System&#59;&#10;&#10;class XYZ &#123;&#10; public static int add1&#40;int x, int y&#41; &#123; return x&#43;y&#59; &#125;&#10; int f1, f2&#59;&#10; public int add2&#40;&#41; &#123; return f1&#43;f2&#59; &#125;&#10; public void modify&#40;&#41; &#123; f1 &#61; f1 &#43; f2&#59; &#125;&#10; public void localModify&#40;&#41; &#123;&#10; XYZ temp &#61; new XYZ&#40;&#41;&#59;&#10; temp.modify&#40;&#41;&#59;&#10; &#125;&#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:004Rfhttp://rise4fun.com/Seal/4RfSeal/4Rf<pre>using System&#59;&#10;using System.Collections.Generic&#59;&#10;using System.Runtime.CompilerServices&#59;&#10;using System.Threading&#59;&#10;using System.Threading.Tasks&#59;&#10;&#10;namespace Roslyn.Utilities&#10;&#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Represents a value that can be retrieved synchronously or asynchronously by many clients.&#10; &#47;&#47;&#47; The value will be computed on-demand the moment the first client asks for it. While being&#10; &#47;&#47;&#47; computed, more clients can request the value. As long as there are outstanding clients the&#10; &#47;&#47;&#47; underlying computation will proceed. If all outstanding clients cancel their request then&#10; &#47;&#47;&#47; the underlying value computation will be cancelled as well.&#10; &#47;&#47;&#47; &#10; &#47;&#47;&#47; Creators of an &#60;see cref&#61;&#34;AsyncLazy&#123;T&#125;&#34; &#47;&#62; can specify whether the result of the computation is&#10; &#47;&#47;&#47; cached for future requests or not. Choosing to not cache means the computation functions are kept&#10; &#47;&#47;&#47; alive, whereas caching means the value &#40;but not functions&#41; are kept alive once complete.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; internal sealed class AsyncLazy&#60;T&#62;&#10; &#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The underlying function that starts an asynchronous computation of the resulting value.&#10; &#47;&#47;&#47; Null&#39;ed out once we&#39;ve computed the result and we&#39;ve been asked to cache it. Otherwise,&#10; &#47;&#47;&#47; it is kept around in case the value needs to be computed again.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The underlying function that starts an synchronous computation of the resulting value.&#10; &#47;&#47;&#47; Null&#39;ed out once we&#39;ve computed the result and we&#39;ve been asked to cache it, or if we&#10; &#47;&#47;&#47; didn&#39;t get any synchronous function given to us in the first place.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Func&#60;CancellationToken, T&#62; synchronousComputeFunction&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Whether or not we should keep the value around once we&#39;ve computed it.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private readonly bool cacheResult&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The Task that holds the cached result.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Task&#60;T&#62; cachedResult&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Mutex used to protect reading and writing to all mutable objects and fields. Traces&#10; &#47;&#47;&#47; indicate that there&#39;s negligible contention on this lock, hence we can save some memory&#10; &#47;&#47;&#47; by using a single lock for all AsyncLazy instances. Only trivial and non-reentrant work&#10; &#47;&#47;&#47; should be done while holding the lock.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private static readonly NonReentrantLock gate &#61; new NonReentrantLock&#40;useThisInstanceForSynchronization&#58; true&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The hash set of all currently outstanding asynchronous requests. Null if there are no requests,&#10; &#47;&#47;&#47; and will never be empty.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private HashSet&#60;Request&#62; requests &#61; null&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; If an asynchronous request is active, the CancellationTokenSource that allows for&#10; &#47;&#47;&#47; cancelling the underlying computation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private CancellationTokenSource asynchronousComputationCancellationSource &#61; null&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Whether a computation is active or queued on any thread, whether synchronous or&#10; &#47;&#47;&#47; asynchronous.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool computationActive &#61; false&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Creates an AsyncLazy that always returns the value, analogous to &#60;see cref&#61;&#34;Task.FromResult&#123;T&#125;&#34; &#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public AsyncLazy&#40;T value&#41;&#10; &#123;&#10; this.cacheResult &#61; true&#59;&#10; this.cachedResult &#61; new Task&#40;&#40;&#41; &#61;&#62; &#123;return value&#59;&#125;&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Important&#58; callers of this constructor should ensure that the compute function returns&#10; &#47;&#47;&#47; a task in a non-blocking fashion. i.e. the function should &#42;not&#42; synchronously compute&#10; &#47;&#47;&#47; a value and then return it using Task.FromResult. Instead, it should return an actual&#10; &#47;&#47;&#47; task that operates asynchronously. If this function synchronously computes a value&#10; &#47;&#47;&#47; then that will cause locks to be held in this type for excessive periods of time.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public AsyncLazy&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, bool cacheResult&#41;&#10; &#58; this&#40;asynchronousComputeFunction, synchronousComputeFunction&#58; null, cacheResult&#58; cacheResult&#41;&#10; &#123;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Creates an AsyncLazy that supports both asynchronous computation and inline synchronous&#10; &#47;&#47;&#47; computation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;asynchronousComputeFunction&#34;&#62;A function called to start the asynchronous&#10; &#47;&#47;&#47; computation. This function should be cheap and non-blocking.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;synchronousComputeFunction&#34;&#62;A function to do the work synchronously, which&#10; &#47;&#47;&#47; is allowed to block. This function should not be implemented by a simple Wait on the&#10; &#47;&#47;&#47; asynchronous value. If that&#39;s all you are doing, just don&#39;t pass a synchronous function&#10; &#47;&#47;&#47; in the first place.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;cacheResult&#34;&#62;Whether the result should be cached once the computation is&#10; &#47;&#47;&#47; complete.&#60;&#47;param&#62;&#10; public AsyncLazy&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, Func&#60;CancellationToken, T&#62; synchronousComputeFunction, bool cacheResult&#41;&#10; &#123;&#10; Contract.ThrowIfNull&#40;asynchronousComputeFunction&#41;&#59;&#10;&#10; this.asynchronousComputeFunction &#61; asynchronousComputeFunction&#59;&#10; this.synchronousComputeFunction &#61; synchronousComputeFunction&#59;&#10; this.cacheResult &#61; cacheResult&#59;&#10; &#125;&#10;&#10; &#35;region Lock Wrapper for Invariant Checking&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Takes the lock for this object and if acquired validates the invariants of this class.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private WaitThatValidatesInvariants TakeLock&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; gate.Wait&#40;cancellationToken&#41;&#59;&#10; AssertInvariants_NoLock&#40;&#41;&#59;&#10; return new WaitThatValidatesInvariants&#40;this&#41;&#59;&#10; &#125;&#10;&#10; private struct WaitThatValidatesInvariants &#58; IDisposable&#10; &#123;&#10; private readonly AsyncLazy&#60;T&#62; asyncLazy&#59;&#10;&#10; public WaitThatValidatesInvariants&#40;AsyncLazy&#60;T&#62; asyncLazy&#41;&#10; &#123;&#10; this.asyncLazy &#61; asyncLazy&#59;&#10; &#125;&#10;&#10; public void Dispose&#40;&#41;&#10; &#123;&#10; asyncLazy.AssertInvariants_NoLock&#40;&#41;&#59;&#10; gate.Release&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void AssertInvariants_NoLock&#40;&#41;&#10; &#123;&#10; &#47;&#47; Invariant &#35;1&#58; thou shalt never have an asynchronous computation running without it&#10; &#47;&#47; being considered a computation&#10; Contract.ThrowIfTrue&#40;this.asynchronousComputationCancellationSource &#33;&#61; null &#38;&#38;&#10; &#33;this.computationActive&#41;&#59;&#10;&#10; &#47;&#47; Invariant &#35;2&#58; thou shalt never waste memory holding onto empty HashSets&#10; Contract.ThrowIfTrue&#40;this.requests &#33;&#61; null &#38;&#38;&#10; this.requests.Count &#61;&#61; 0&#41;&#59;&#10;&#10; &#47;&#47; Invariant &#35;3&#58; thou shalt never have an request if there is not&#10; &#47;&#47; something trying to compute it&#10; Contract.ThrowIfTrue&#40;this.requests &#33;&#61; null &#38;&#38;&#10; &#33;this.computationActive&#41;&#59;&#10;&#10; &#47;&#47; Invariant &#35;4&#58; thou shalt never have a cached value and any computation function&#10; Contract.ThrowIfTrue&#40;this.cachedResult &#33;&#61; null &#38;&#38;&#10; &#40;this.synchronousComputeFunction &#33;&#61; null &#124;&#124; this.asynchronousComputeFunction &#33;&#61; null&#41;&#41;&#59;&#10;&#10; &#47;&#47; Invariant &#35;5&#58; thou shalt never have a synchronous computation function but not an&#10; &#47;&#47; asynchronous one&#10; Contract.ThrowIfTrue&#40;this.asynchronousComputeFunction &#61;&#61; null &#38;&#38; this.synchronousComputeFunction &#33;&#61; null&#41;&#59;&#10; &#125;&#10;&#10; &#35;endregion&#10;&#10; public bool TryGetValue&#40;out T result&#41;&#10; &#123;&#10; &#47;&#47; No need to lock here since this is only a fast check to &#10; &#47;&#47; see if the result is already computed.&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; result &#61; cachedResult.Result&#59;&#10; return true&#59;&#10; &#125;&#10;&#10; result &#61; default&#40;T&#41;&#59;&#10; return false&#59;&#10; &#125;&#10;&#10; public T GetValue&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; Request request &#61; null&#59;&#10; AsynchronousComputationToStart&#63; newAsynchronousComputation &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If cached, get immediately&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return cachedResult.Result&#59;&#10; &#125;&#10;&#10; &#47;&#47; If there is an existing computation active, we&#39;ll just create another request&#10; if &#40;computationActive&#41;&#10; &#123;&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10; &#125;&#10; else if &#40;synchronousComputeFunction &#61;&#61; null&#41;&#10; &#123;&#10; &#47;&#47; A synchronous request, but we have no synchronous function. Start off the async work&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10;&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; else&#10; &#123;&#10; &#47;&#47; We will do the computation here&#10; this.computationActive &#61; true&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; If we simply created a new asynchronous request, so wait for it. Yes, we&#39;re blocking the thread&#10; &#47;&#47; but we don&#39;t want multiple threads attempting to compute the same thing.&#10; if &#40;request &#33;&#61; null&#41;&#10; &#123;&#10; request.RegisterForCancellation&#40;OnAsynchronousRequestCancelled, cancellationToken&#41;&#59;&#10;&#10; &#47;&#47; Since we already registered for cancellation, it&#39;s possible that the registration has&#10; &#47;&#47; cancelled this new computation if we were the only requestor.&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; request&#41;&#59;&#10; &#125;&#10;&#10; return request.Task.WaitAndGetResult&#40;cancellationToken&#41;&#59;&#10; &#125;&#10; else&#10; &#123;&#10; T result&#59;&#10;&#10; &#47;&#47; We are the active computation, so let&#39;s go ahead and compute.&#10; try&#10; &#123;&#10; result &#61; synchronousComputeFunction&#40;cancellationToken&#41;&#59;&#10; &#125;&#10; catch &#40;OperationCanceledException&#41;&#10; &#123;&#10; &#47;&#47; This cancelled for some reason. We don&#39;t care why, but&#10; &#47;&#47; it means anybody else waiting for this result isn&#39;t going to get it&#10; &#47;&#47; from us.&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; this.computationActive &#61; false&#59;&#10;&#10; if &#40;requests &#33;&#61; null&#41;&#10; &#123;&#10; &#47;&#47; There&#39;s a possible improvement here&#58; there might be another synchronous caller who&#10; &#47;&#47; also wants the value. We might consider stealing their thread rather than punting&#10; &#47;&#47; to the thread pool.&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; null&#41;&#59;&#10; &#125;&#10;&#10; throw&#59;&#10; &#125;&#10; catch &#40;Exception ex&#41;&#10; &#123;&#10; &#47;&#47; We faulted for some unknown reason. We should simply fault everything.&#10; TaskCompletionSource&#60;T&#62; tcs &#61; new TaskCompletionSource&#60;T&#62;&#40;&#41;&#59;&#10; tcs.SetException&#40;ex&#41;&#59;&#10; CompleteWithTask&#40;tcs.Task, CancellationToken.None&#41;&#59;&#10;&#10; throw&#59;&#10; &#125;&#10;&#10; &#47;&#47; We have a value, so complete&#10; CompleteWithTask&#40;Task.FromResult&#40;result&#41;, CancellationToken.None&#41;&#59;&#10;&#10; return result&#59;&#10; &#125;&#10; &#125;&#10;&#10; private Request CreateNewRequest_NoLock&#40;&#41;&#10; &#123;&#10; if &#40;this.requests &#61;&#61; null&#41;&#10; &#123;&#10; this.requests &#61; new HashSet&#60;Request&#62;&#40;&#41;&#59;&#10; &#125;&#10;&#10; Request request &#61; new Request&#40;&#41;&#59;&#10; this.requests.Add&#40;request&#41;&#59;&#10; return request&#59;&#10; &#125;&#10;&#10; public Task&#60;T&#62; GetValueAsync&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; &#47;&#47; Optimization&#58; if we&#39;re already cancelled, do not pass go&#10; if &#40;cancellationToken.IsCancellationRequested&#41;&#10; &#123;&#10; return new Task&#60;T&#62;&#40;&#40;&#41; &#61;&#62; default&#40;T&#41;, cancellationToken&#41;&#59;&#10; &#125;&#10;&#10; Request request&#59;&#10; AsynchronousComputationToStart&#63; newAsynchronousComputation &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If cached, get immediately&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return cachedResult&#59;&#10; &#125;&#10;&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10;&#10; &#47;&#47; If we have either synchronous or asynchronous work current in flight, we don&#39;t need to do anything.&#10; &#47;&#47; Otherwise, we shall start an asynchronous computation for this&#10; if &#40;&#33;computationActive&#41;&#10; &#123;&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; We now have the request counted for, register for cancellation. It is critical this is&#10; &#47;&#47; done outside the lock, as our reigstration may immediately fire and we want to avoid the&#10; &#47;&#47; reentrancy&#10; request.RegisterForCancellation&#40;OnAsynchronousRequestCancelled, cancellationToken&#41;&#59;&#10;&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; request&#41;&#59;&#10; &#125;&#10;&#10; return request.Task&#59;&#10; &#125;&#10;&#10; private AsynchronousComputationToStart RegisterAsynchronousComputation_NoLock&#40;&#41;&#10; &#123;&#10; Contract.ThrowIfTrue&#40;this.computationActive&#41;&#59;&#10;&#10; this.asynchronousComputationCancellationSource &#61; new CancellationTokenSource&#40;&#41;&#59;&#10; this.computationActive &#61; true&#59;&#10;&#10; return new AsynchronousComputationToStart&#40;this.asynchronousComputeFunction, this.asynchronousComputationCancellationSource&#41;&#59;&#10; &#125;&#10;&#10; private struct AsynchronousComputationToStart&#10; &#123;&#10; public readonly Func&#60;CancellationToken, Task&#60;T&#62;&#62; AsynchronousComputeFunction&#59;&#10; public readonly CancellationTokenSource CancellationTokenSource&#59;&#10;&#10; public AsynchronousComputationToStart&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, CancellationTokenSource cancellationTokenSource&#41;&#10; &#123;&#10; this.AsynchronousComputeFunction &#61; asynchronousComputeFunction&#59;&#10; this.CancellationTokenSource &#61; cancellationTokenSource&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void StartAsynchronousComputation&#40;AsynchronousComputationToStart computationToStart, Request requestToCompleteSynchronously&#41;&#10; &#123;&#10; var cancellationToken &#61; computationToStart.CancellationTokenSource.Token&#59;&#10;&#10; try&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; DO NOT ACCESS ANY FIELDS OR STATE BEYOND THIS POINT. Since this function&#10; &#47;&#47; runs unsynchronized, it&#39;s possible that during this function this request&#10; &#47;&#47; might be cancelled, and then a whole additional request might start and&#10; &#47;&#47; complete inline, and cache the result. By grabbing state before we check&#10; &#47;&#47; the cancellation token, we can be assured that we are only operating on&#10; &#47;&#47; a state that was complete.&#10;&#10; ExceptionUtilities.ExecuteWithErrorReporting&#40;&#40;&#41; &#61;&#62;&#10; &#123;&#10; &#47;&#47; We avoid creating a full closure just to pass the token along&#10; &#47;&#47; Also, use TaskContinuationOptions.ExecuteSynchronously so that we inline &#10; &#47;&#47; the continuation if asynchronousComputeFunction completes synchronously&#10; var task &#61; computationToStart.AsynchronousComputeFunction&#40;cancellationToken&#41;&#59;&#10;&#10; task.ContinueWith&#40;&#10; &#40;t, s&#41; &#61;&#62; CompleteWithTask&#40;t, &#40;&#40;CancellationTokenSource&#41;s&#41;.Token&#41;,&#10; computationToStart.CancellationTokenSource,&#10; cancellationToken,&#10; TaskContinuationOptions.ExecuteSynchronously,&#10; TaskScheduler.Default&#41;&#59;&#10;&#10; if &#40;requestToCompleteSynchronously &#33;&#61; null &#38;&#38; task.IsCompleted&#41;&#10; &#123;&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; task &#61; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;task&#41;&#59;&#10; &#125;&#10;&#10; requestToCompleteSynchronously.CompleteFromTaskSynchronously&#40;task&#41;&#59;&#10; &#125;&#10; &#125;&#41;&#59;&#10; &#125;&#10; catch &#40;OperationCanceledException oce&#41;&#10; &#123;&#10; &#47;&#47; As long as it&#39;s the right token, this means that our thread was the first thread&#10; &#47;&#47; to start an asynchronous computation, but the requestor cancelled as we were starting up&#10; &#47;&#47; the computation.&#10; if &#40;oce.CancellationToken &#33;&#61; cancellationToken&#41;&#10; &#123;&#10; ExceptionUtilities.FailFast&#40;oce&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; private void CompleteWithTask&#40;Task&#60;T&#62; task, CancellationToken cancellationToken&#41;&#10; &#123;&#10; IEnumerable&#60;Request&#62; requestsToComplete&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If the underlying computation was cancelled, then all state was already updated in OnAsynchronousRequestCancelled&#10; &#47;&#47; and there is no new work to do here. We &#42;must&#42; use the local one since this completion may be running far after&#10; &#47;&#47; the background computation was cancelled and a new one might have already been enqueued. We must do this&#10; &#47;&#47; check here under the lock to ensure proper synchronization with OnAsynchronousRequestCancelled.&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; The computation is complete, so get all requests to complete and null out the list. We&#39;ll create another one&#10; &#47;&#47; later if it&#39;s needed&#10; requestsToComplete &#61; this.requests &#63;&#63; SpecializedCollections.EmptyEnumerable&#60;Request&#62;&#40;&#41;&#59;&#10; this.requests &#61; null&#59;&#10;&#10; &#47;&#47; The computations are done&#10; this.asynchronousComputationCancellationSource &#61; null&#59;&#10; this.computationActive &#61; false&#59;&#10; task &#61; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;task&#41;&#59;&#10; &#125;&#10;&#10; foreach &#40;var requestToComplete in requestsToComplete&#41;&#10; &#123;&#10; requestToComplete.CompleteFromTaskAsynchronously&#40;task&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; private Task&#60;T&#62; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; if &#40;this.cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return this.cachedResult&#59;&#10; &#125;&#10; else&#10; &#123;&#10; if &#40;cacheResult &#38;&#38; task.Status &#61;&#61; TaskStatus.RanToCompletion&#41;&#10; &#123;&#10; &#47;&#47; Hold onto the completed task. We can get rid of the computation functions for good&#10; this.cachedResult &#61; task&#59;&#10; this.asynchronousComputeFunction &#61; null&#59;&#10; this.synchronousComputeFunction &#61; null&#59;&#10; &#125;&#10;&#10; return task&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void OnAsynchronousRequestCancelled&#40;object state&#41;&#10; &#123;&#10; var request &#61; &#40;Request&#41;state&#59;&#10; CancellationTokenSource cancellationTokenSource &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; &#47;&#47; Now try to remove it. It&#39;s possible that requests may already be null. You could&#10; &#47;&#47; imagine that cancellation was requested, but before we could aquire the lock&#10; &#47;&#47; here the computation completed and the entire CompleteWithTask synchronized&#10; &#47;&#47; block ran. In that case, the requests collection may already be null, or it&#10; &#47;&#47; &#40;even scarier&#33;&#41; may have been replaced with another collection because another&#10; &#47;&#47; computation has started.&#10; if &#40;this.requests &#33;&#61; null&#41;&#10; &#123;&#10; if &#40;this.requests.Remove&#40;request&#41;&#41;&#10; &#123;&#10; if &#40;this.requests.Count &#61;&#61; 0&#41;&#10; &#123;&#10; this.requests &#61; null&#59;&#10;&#10; if &#40;this.asynchronousComputationCancellationSource &#33;&#61; null&#41;&#10; &#123;&#10; cancellationTokenSource &#61; this.asynchronousComputationCancellationSource&#59;&#10; this.asynchronousComputationCancellationSource &#61; null&#59;&#10; this.computationActive &#61; false&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; request.CancelAsynchronously&#40;&#41;&#59;&#10;&#10; if &#40;cancellationTokenSource &#33;&#61; null&#41;&#10; &#123;&#10; cancellationTokenSource.Cancel&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; Using inheritance instead of wrapping a TaskCompletionSource to avoid a second allocation&#10; private class Request &#58; TaskCompletionSource&#60;T&#62;&#10; &#123;&#10; private CancellationTokenRegistration cancellationTokenRegistration&#59;&#10;&#10; public void RegisterForCancellation&#40;Action&#60;object&#62; callback, CancellationToken cancellationToken&#41;&#10; &#123;&#10; cancellationTokenRegistration &#61; cancellationToken.Register&#40;callback, this&#41;&#59;&#10; &#125;&#10;&#10; public void CompleteFromTaskAsynchronously&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; System.Threading.Tasks.Task.Factory.StartNew&#40;CompleteFromTaskSynchronouslyStub, task, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default&#41;&#59;&#10; &#125;&#10;&#10; private void CompleteFromTaskSynchronouslyStub&#40;object task&#41;&#10; &#123;&#10; CompleteFromTaskSynchronously&#40;&#40;Task&#60;T&#62;&#41;task&#41;&#59;&#10; &#125;&#10;&#10; public void CompleteFromTaskSynchronously&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; if &#40;task.Status &#61;&#61; TaskStatus.RanToCompletion&#41;&#10; &#123;&#10; if &#40;TrySetResult&#40;task.Result&#41;&#41;&#10; &#123;&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; else if &#40;task.Status &#61;&#61; TaskStatus.Faulted&#41;&#10; &#123;&#10; if &#40;TrySetException&#40;task.Exception&#41;&#41;&#10; &#123;&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; else&#10; &#123;&#10; CancelSynchronously&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; public void CancelAsynchronously&#40;&#41;&#10; &#123;&#10; &#47;&#47; Since there could be synchronous continuations on the TaskCancellationSource, we queue this to the threadpool&#10; &#47;&#47; to avoid inline running of other operations.&#10; System.Threading.Tasks.Task.Factory.StartNew&#40;CancelSynchronously, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default&#41;&#59;&#10; &#125;&#10;&#10; private void CancelSynchronously&#40;&#41;&#10; &#123;&#10; if &#40;TrySetCanceled&#40;&#41;&#41;&#10; &#123;&#10; &#47;&#47; Paranoia&#58; the only reason we should ever get here is if the CancellationToken that&#10; &#47;&#47; we registered against was cancelled, but just in case, dispose the registration&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; A lightweight mutual exclusion object which supports waiting with cancellation and prevents&#10; &#47;&#47;&#47; recursion &#40;i.e. you may not call Wait if you already hold the lock&#41;&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; The &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62; provides a lightweight mutual exclusion class that doesn&#39;t&#10; &#47;&#47;&#47; use Windows kernel synchronization primitives.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; The implementation is distilled from the workings of &#60;see cref&#61;&#34;T&#58;System.Threading.SemaphoreSlim&#34;&#47;&#62;&#10; &#47;&#47;&#47; The basic idea is that we use a regular sync object &#40;Monitor.Enter&#47;Exit&#41; to guard the setting&#10; &#47;&#47;&#47; of an &#39;owning thread&#39; field. If, during the Wait, we find the lock is held by someone else&#10; &#47;&#47;&#47; then we register a cancellation callback and enter a &#34;Monitor.Wait&#34; loop. If the cancellation&#10; &#47;&#47;&#47; callback fires, then it &#34;pulses&#34; all the waiters to wake them up and check for cancellation.&#10; &#47;&#47;&#47; Waiters are also &#34;pulsed&#34; when leaving the lock.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; All public members of &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62; are thread-safe and may be used concurrently&#10; &#47;&#47;&#47; from multiple threads.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; internal sealed class NonReentrantLock&#10; &#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; A synchronization object to protect access to the &#60;see cref&#61;&#34;F&#58;owningThread&#34;&#47;&#62; field and to be pulsed&#10; &#47;&#47;&#47; when &#60;see cref&#61;&#34;M&#58;Release&#34;&#47;&#62; is called and during cancellation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private readonly object syncLock&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Indicates which thread currently holds the lock. If null, then the lock is available.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private volatile Thread owningThread&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Constructor.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;useThisInstanceForSynchronization&#34;&#62;If false &#40;the default&#41;, then the class&#10; &#47;&#47;&#47; allocates an internal object to be used as a sync lock.&#10; &#47;&#47;&#47; If true, then the sync lock object will be the NonReentrantLock instance itself. This&#10; &#47;&#47;&#47; saves an allocation but a client may not safely further use this instance in a call to&#10; &#47;&#47;&#47; Monitor.Enter&#47;Exit or in a &#34;lock&#34; statement.&#10; &#47;&#47;&#47; &#60;&#47;param&#62;&#10; public NonReentrantLock&#40;bool useThisInstanceForSynchronization &#61; false&#41;&#10; &#123;&#10; this.syncLock &#61; useThisInstanceForSynchronization &#63; this &#58; new object&#40;&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Shared factory for use in lazy initialization.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public static readonly Func&#60;NonReentrantLock&#62; Factory &#61; &#40;&#41; &#61;&#62; new NonReentrantLock&#40;useThisInstanceForSynchronization&#58; true&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Blocks the current thread until it can enter the &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62;, while observing a&#10; &#47;&#47;&#47; &#60;see cref&#61;&#34;T&#58;System.Threading.CancellationToken&#34;&#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; Recursive locking is not supported. i.e. A thread may not call Wait successfully twice without an&#10; &#47;&#47;&#47; intervening &#60;see cref&#61;&#34;Release&#34;&#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;cancellationToken&#34;&#62;The &#60;see cref&#61;&#34;T&#58;System.Threading.CancellationToken&#34;&#47;&#62; token to&#10; &#47;&#47;&#47; observe.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;System.OperationCanceledException&#34;&#62;&#60;paramref name&#61;&#34;cancellationToken&#34;&#47;&#62; was&#10; &#47;&#47;&#47; canceled.&#60;&#47;exception&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;System.LockRecursionException&#34;&#62;The caller already holds the lock&#60;&#47;exception&#62;&#10; public void Wait&#40;CancellationToken cancellationToken &#61; default&#40;CancellationToken&#41;&#41;&#10; &#123;&#10; if &#40;this.IsOwnedByMe&#41;&#10; &#123;&#10; throw new LockRecursionException&#40;&#41;&#59;&#10; &#125;&#10;&#10; CancellationTokenRegistration cancellationTokenRegistration &#61; default&#40;CancellationTokenRegistration&#41;&#59;&#10;&#10; if &#40;cancellationToken.CanBeCanceled&#41;&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; Fast path to try and avoid allocations in callback registration.&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; if &#40;&#33;this.IsLocked&#41;&#10; &#123;&#10; this.TakeOwnership&#40;&#41;&#59;&#10; return&#59;&#10; &#125;&#10; &#125;&#10;&#10; cancellationTokenRegistration &#61; cancellationToken.Register&#40;cancellationTokenCanceledEventHandler, this.syncLock, useSynchronizationContext&#58; false&#41;&#59;&#10; &#125;&#10;&#10; using &#40;cancellationTokenRegistration&#41;&#10; &#123;&#10; &#47;&#47; PERF&#58; First spin wait for the lock to become available, but only up to the first planned yield.&#10; &#47;&#47; This additional amount of spinwaiting was inherited from SemaphoreSlim&#39;s implementation where&#10; &#47;&#47; it showed measurable perf gains in test scenarios.&#10; SpinWait spin &#61; new SpinWait&#40;&#41;&#59;&#10; while &#40;this.IsLocked &#38;&#38; &#33;spin.NextSpinWillYield&#41;&#10; &#123;&#10; spin.SpinOnce&#40;&#41;&#59;&#10; &#125;&#10;&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; while &#40;this.IsLocked&#41;&#10; &#123;&#10; &#47;&#47; If cancelled, we throw. Trying to wait could lead to deadlock.&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; using &#40;Logger.LogBlock&#40;FeatureId.WaitIndicator, FunctionId.Utilities_NonReentrantLock_BlockingWait, cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; Another thread holds the lock. Wait until we get awoken either&#10; &#47;&#47; by some code calling &#34;Release&#34; or by cancellation.&#10; Monitor.Wait&#40;this.syncLock&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; We now hold the lock&#10; this.TakeOwnership&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Exit the mutual exclusion.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; The calling thread must currently hold the lock.&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;Roslyn.Utilities.ContractFailureException&#34;&#62;The lock is not currently held by the calling thread.&#60;&#47;exception&#62;&#10; public void Release&#40;&#41;&#10; &#123;&#10; AssertHasLock&#40;&#41;&#59;&#10;&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; this.ReleaseOwnership&#40;&#41;&#59;&#10;&#10; &#47;&#47; Release one waiter&#10; Monitor.Pulse&#40;this.syncLock&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Determine if the lock is currently held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;returns&#62;True if the lock is currently held by the calling thread.&#60;&#47;returns&#62;&#10; public bool LockHeldByMe&#40;&#41;&#10; &#123;&#10; return this.IsOwnedByMe&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Throw an exception if the lock is not held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;Roslyn.Utilities.ContractFailureException&#34;&#62;The lock is not currently held by the calling thread.&#60;&#47;exception&#62;&#10; public void AssertHasLock&#40;&#41;&#10; &#123;&#10; Contract.ThrowIfFalse&#40;LockHeldByMe&#40;&#41;&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Checks if the lock is currently held.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool IsLocked&#10; &#123;&#10; get&#10; &#123;&#10; return this.owningThread &#33;&#61; null&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Checks if the lock is currently held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool IsOwnedByMe&#10; &#123;&#10; get&#10; &#123;&#10; return this.owningThread &#61;&#61; Thread.CurrentThread&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Take ownership of the lock &#40;by the calling thread&#41;. The lock may not already&#10; &#47;&#47;&#47; be held by any other code.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private void TakeOwnership&#40;&#41;&#10; &#123;&#10; Contract.Assert&#40;&#33;this.IsLocked&#41;&#59;&#10; this.owningThread &#61; Thread.CurrentThread&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Release ownership of the lock. The lock must already be held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private void ReleaseOwnership&#40;&#41;&#10; &#123;&#10; Contract.Assert&#40;this.IsOwnedByMe&#41;&#59;&#10; this.owningThread &#61; null&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Action object passed to a cancellation token registration.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private static readonly Action&#60;object&#62; cancellationTokenCanceledEventHandler &#61; new Action&#60;object&#62;&#40;CancellationTokenCanceledEventHandler&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Callback executed when a cancellation token is canceled during a Wait.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;obj&#34;&#62;The syncLock that protects a &#60;see cref&#61;&#34;NonReentrantLock &#34;&#47;&#62; instance.&#60;&#47;param&#62;&#10; private static void CancellationTokenCanceledEventHandler&#40;object obj&#41;&#10; &#123;&#10; lock &#40;obj&#41;&#10; &#123;&#10; &#47;&#47; Release all waiters to check their cancellation tokens.&#10; Monitor.PulseAll&#40;obj&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#125;</pre>2013-05-23T06:05:29-07:002thttp://rise4fun.com/Seal/2tSeal/2t<pre>using System&#59;&#10;class Test &#123;&#10; public Test f &#61; null&#59;&#10; public int kp &#61; 0&#59;&#10; public void PureMethod&#40;Test param&#41;&#123;&#10; var temp &#61; new Test&#40;&#41;&#59;&#10; temp.f &#61; param&#59; &#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:001X5http://rise4fun.com/Seal/1X5Seal/1X5<pre>using System&#59;&#10;&#10;class XYZ &#123;&#10;&#10; public static int add1&#40;int x, int y&#41; &#123; return x&#43;y&#59; &#125;&#10;&#10; int f1, f2&#59;&#10;&#10; public int add2&#40;&#41; &#123; return f1&#43;f2&#59; &#125;&#10;&#10; public void modify&#40;&#41; &#123; f1 &#61; f1 &#43; f2&#59; &#125;&#10;&#10; public void localModify&#40;&#41; &#123;&#10; XYZ temp &#61; new XYZ&#40;&#41;&#59;&#10; temp.modify&#40;&#41;&#59;&#10; &#125;&#10; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:001lhttp://rise4fun.com/Seal/1lSeal/1l<pre>using System&#59;&#10;using System.Collections.Generic&#59;&#10;using System.Runtime.CompilerServices&#59;&#10;using System.Threading&#59;&#10;using System.Threading.Tasks&#59;&#10;&#10;namespace Roslyn.Utilities&#10;&#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Represents a value that can be retrieved synchronously or asynchronously by many clients.&#10; &#47;&#47;&#47; The value will be computed on-demand the moment the first client asks for it. While being&#10; &#47;&#47;&#47; computed, more clients can request the value. As long as there are outstanding clients the&#10; &#47;&#47;&#47; underlying computation will proceed. If all outstanding clients cancel their request then&#10; &#47;&#47;&#47; the underlying value computation will be cancelled as well.&#10; &#47;&#47;&#47; &#10; &#47;&#47;&#47; Creators of an &#60;see cref&#61;&#34;AsyncLazy&#123;T&#125;&#34; &#47;&#62; can specify whether the result of the computation is&#10; &#47;&#47;&#47; cached for future requests or not. Choosing to not cache means the computation functions are kept&#10; &#47;&#47;&#47; alive, whereas caching means the value &#40;but not functions&#41; are kept alive once complete.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; internal sealed class AsyncLazy&#60;T&#62;&#10; &#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The underlying function that starts an asynchronous computation of the resulting value.&#10; &#47;&#47;&#47; Null&#39;ed out once we&#39;ve computed the result and we&#39;ve been asked to cache it. Otherwise,&#10; &#47;&#47;&#47; it is kept around in case the value needs to be computed again.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The underlying function that starts an synchronous computation of the resulting value.&#10; &#47;&#47;&#47; Null&#39;ed out once we&#39;ve computed the result and we&#39;ve been asked to cache it, or if we&#10; &#47;&#47;&#47; didn&#39;t get any synchronous function given to us in the first place.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Func&#60;CancellationToken, T&#62; synchronousComputeFunction&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Whether or not we should keep the value around once we&#39;ve computed it.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private readonly bool cacheResult&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The Task that holds the cached result.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Task&#60;T&#62; cachedResult&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Mutex used to protect reading and writing to all mutable objects and fields. Traces&#10; &#47;&#47;&#47; indicate that there&#39;s negligible contention on this lock, hence we can save some memory&#10; &#47;&#47;&#47; by using a single lock for all AsyncLazy instances. Only trivial and non-reentrant work&#10; &#47;&#47;&#47; should be done while holding the lock.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private static readonly NonReentrantLock gate &#61; new NonReentrantLock&#40;useThisInstanceForSynchronization&#58; true&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The hash set of all currently outstanding asynchronous requests. Null if there are no requests,&#10; &#47;&#47;&#47; and will never be empty.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private HashSet&#60;Request&#62; requests &#61; null&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; If an asynchronous request is active, the CancellationTokenSource that allows for&#10; &#47;&#47;&#47; cancelling the underlying computation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private CancellationTokenSource asynchronousComputationCancellationSource &#61; null&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Whether a computation is active or queued on any thread, whether synchronous or&#10; &#47;&#47;&#47; asynchronous.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool computationActive &#61; false&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Creates an AsyncLazy that always returns the value, analogous to &#60;see cref&#61;&#34;Task.FromResult&#123;T&#125;&#34; &#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public AsyncLazy&#40;T value&#41;&#10; &#123;&#10; this.cacheResult &#61; true&#59;&#10; this.cachedResult &#61; new Task&#60;T&#62;&#40;&#40;&#41; &#61;&#62; &#123;return value&#59;&#125;&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Important&#58; callers of this constructor should ensure that the compute function returns&#10; &#47;&#47;&#47; a task in a non-blocking fashion. i.e. the function should &#42;not&#42; synchronously compute&#10; &#47;&#47;&#47; a value and then return it using Task.FromResult. Instead, it should return an actual&#10; &#47;&#47;&#47; task that operates asynchronously. If this function synchronously computes a value&#10; &#47;&#47;&#47; then that will cause locks to be held in this type for excessive periods of time.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public AsyncLazy&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, bool cacheResult&#41;&#10; &#58; this&#40;asynchronousComputeFunction, synchronousComputeFunction&#58; null, cacheResult&#58; cacheResult&#41;&#10; &#123;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Creates an AsyncLazy that supports both asynchronous computation and inline synchronous&#10; &#47;&#47;&#47; computation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;asynchronousComputeFunction&#34;&#62;A function called to start the asynchronous&#10; &#47;&#47;&#47; computation. This function should be cheap and non-blocking.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;synchronousComputeFunction&#34;&#62;A function to do the work synchronously, which&#10; &#47;&#47;&#47; is allowed to block. This function should not be implemented by a simple Wait on the&#10; &#47;&#47;&#47; asynchronous value. If that&#39;s all you are doing, just don&#39;t pass a synchronous function&#10; &#47;&#47;&#47; in the first place.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;cacheResult&#34;&#62;Whether the result should be cached once the computation is&#10; &#47;&#47;&#47; complete.&#60;&#47;param&#62;&#10; public AsyncLazy&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, Func&#60;CancellationToken, T&#62; synchronousComputeFunction, bool cacheResult&#41;&#10; &#123;&#10; this.asynchronousComputeFunction &#61; asynchronousComputeFunction&#59;&#10; this.synchronousComputeFunction &#61; synchronousComputeFunction&#59;&#10; this.cacheResult &#61; cacheResult&#59;&#10; &#125;&#10;&#10; &#35;region Lock Wrapper for Invariant Checking&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Takes the lock for this object and if acquired validates the invariants of this class.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private WaitThatValidatesInvariants TakeLock&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; gate.Wait&#40;cancellationToken&#41;&#59;&#10; AssertInvariants_NoLock&#40;&#41;&#59;&#10; return new WaitThatValidatesInvariants&#40;this&#41;&#59;&#10; &#125;&#10;&#10; private struct WaitThatValidatesInvariants &#58; IDisposable&#10; &#123;&#10; private readonly AsyncLazy&#60;T&#62; asyncLazy&#59;&#10;&#10; public WaitThatValidatesInvariants&#40;AsyncLazy&#60;T&#62; asyncLazy&#41;&#10; &#123;&#10; this.asyncLazy &#61; asyncLazy&#59;&#10; &#125;&#10;&#10; public void Dispose&#40;&#41;&#10; &#123;&#10; asyncLazy.AssertInvariants_NoLock&#40;&#41;&#59;&#10; gate.Release&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void AssertInvariants_NoLock&#40;&#41;&#10; &#123;&#10;&#10; &#125;&#10;&#10; &#35;endregion&#10;&#10; public bool TryGetValue&#40;out T result&#41;&#10; &#123;&#10; &#47;&#47; No need to lock here since this is only a fast check to &#10; &#47;&#47; see if the result is already computed.&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; result &#61; cachedResult.Result&#59;&#10; return true&#59;&#10; &#125;&#10;&#10; result &#61; default&#40;T&#41;&#59;&#10; return false&#59;&#10; &#125;&#10;&#10; public T GetValue&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; Request request &#61; null&#59;&#10; AsynchronousComputationToStart&#63; newAsynchronousComputation &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If cached, get immediately&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return cachedResult.Result&#59;&#10; &#125;&#10;&#10; &#47;&#47; If there is an existing computation active, we&#39;ll just create another request&#10; if &#40;computationActive&#41;&#10; &#123;&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10; &#125;&#10; else if &#40;synchronousComputeFunction &#61;&#61; null&#41;&#10; &#123;&#10; &#47;&#47; A synchronous request, but we have no synchronous function. Start off the async work&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10;&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; else&#10; &#123;&#10; &#47;&#47; We will do the computation here&#10; this.computationActive &#61; true&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; If we simply created a new asynchronous request, so wait for it. Yes, we&#39;re blocking the thread&#10; &#47;&#47; but we don&#39;t want multiple threads attempting to compute the same thing.&#10; if &#40;request &#33;&#61; null&#41;&#10; &#123;&#10; request.RegisterForCancellation&#40;OnAsynchronousRequestCancelled, cancellationToken&#41;&#59;&#10;&#10; &#47;&#47; Since we already registered for cancellation, it&#39;s possible that the registration has&#10; &#47;&#47; cancelled this new computation if we were the only requestor.&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; request&#41;&#59;&#10; &#125;&#10;&#10; return request.Task.WaitAndGetResult&#40;cancellationToken&#41;&#59;&#10; &#125;&#10; else&#10; &#123;&#10; T result&#59;&#10;&#10; &#47;&#47; We are the active computation, so let&#39;s go ahead and compute.&#10; try&#10; &#123;&#10; result &#61; synchronousComputeFunction&#40;cancellationToken&#41;&#59;&#10; &#125;&#10; catch &#40;OperationCanceledException&#41;&#10; &#123;&#10; &#47;&#47; This cancelled for some reason. We don&#39;t care why, but&#10; &#47;&#47; it means anybody else waiting for this result isn&#39;t going to get it&#10; &#47;&#47; from us.&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; this.computationActive &#61; false&#59;&#10;&#10; if &#40;requests &#33;&#61; null&#41;&#10; &#123;&#10; &#47;&#47; There&#39;s a possible improvement here&#58; there might be another synchronous caller who&#10; &#47;&#47; also wants the value. We might consider stealing their thread rather than punting&#10; &#47;&#47; to the thread pool.&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; null&#41;&#59;&#10; &#125;&#10;&#10; throw&#59;&#10; &#125;&#10; catch &#40;Exception ex&#41;&#10; &#123;&#10; &#47;&#47; We faulted for some unknown reason. We should simply fault everything.&#10; TaskCompletionSource&#60;T&#62; tcs &#61; new TaskCompletionSource&#60;T&#62;&#40;&#41;&#59;&#10; tcs.SetException&#40;ex&#41;&#59;&#10; CompleteWithTask&#40;tcs.Task, CancellationToken.None&#41;&#59;&#10;&#10; throw&#59;&#10; &#125;&#10;&#10; &#47;&#47; We have a value, so complete&#10; CompleteWithTask&#40;Task.FromResult&#40;result&#41;, CancellationToken.None&#41;&#59;&#10;&#10; return result&#59;&#10; &#125;&#10; &#125;&#10;&#10; private Request CreateNewRequest_NoLock&#40;&#41;&#10; &#123;&#10; if &#40;this.requests &#61;&#61; null&#41;&#10; &#123;&#10; this.requests &#61; new HashSet&#60;Request&#62;&#40;&#41;&#59;&#10; &#125;&#10;&#10; Request request &#61; new Request&#40;&#41;&#59;&#10; this.requests.Add&#40;request&#41;&#59;&#10; return request&#59;&#10; &#125;&#10;&#10; public Task&#60;T&#62; GetValueAsync&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; &#47;&#47; Optimization&#58; if we&#39;re already cancelled, do not pass go&#10; if &#40;cancellationToken.IsCancellationRequested&#41;&#10; &#123;&#10; return new Task&#60;T&#62;&#40;&#40;&#41; &#61;&#62; default&#40;T&#41;, cancellationToken&#41;&#59;&#10; &#125;&#10;&#10; Request request&#59;&#10; AsynchronousComputationToStart&#63; newAsynchronousComputation &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If cached, get immediately&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return cachedResult&#59;&#10; &#125;&#10;&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10;&#10; &#47;&#47; If we have either synchronous or asynchronous work current in flight, we don&#39;t need to do anything.&#10; &#47;&#47; Otherwise, we shall start an asynchronous computation for this&#10; if &#40;&#33;computationActive&#41;&#10; &#123;&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; We now have the request counted for, register for cancellation. It is critical this is&#10; &#47;&#47; done outside the lock, as our reigstration may immediately fire and we want to avoid the&#10; &#47;&#47; reentrancy&#10; request.RegisterForCancellation&#40;OnAsynchronousRequestCancelled, cancellationToken&#41;&#59;&#10;&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; request&#41;&#59;&#10; &#125;&#10;&#10; return request.Task&#59;&#10; &#125;&#10;&#10; private AsynchronousComputationToStart RegisterAsynchronousComputation_NoLock&#40;&#41;&#10; &#123;&#10; Contract.ThrowIfTrue&#40;this.computationActive&#41;&#59;&#10;&#10; this.asynchronousComputationCancellationSource &#61; new CancellationTokenSource&#40;&#41;&#59;&#10; this.computationActive &#61; true&#59;&#10;&#10; return new AsynchronousComputationToStart&#40;this.asynchronousComputeFunction, this.asynchronousComputationCancellationSource&#41;&#59;&#10; &#125;&#10;&#10; private struct AsynchronousComputationToStart&#10; &#123;&#10; public readonly Func&#60;CancellationToken, Task&#60;T&#62;&#62; AsynchronousComputeFunction&#59;&#10; public readonly CancellationTokenSource CancellationTokenSource&#59;&#10;&#10; public AsynchronousComputationToStart&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, CancellationTokenSource cancellationTokenSource&#41;&#10; &#123;&#10; this.AsynchronousComputeFunction &#61; asynchronousComputeFunction&#59;&#10; this.CancellationTokenSource &#61; cancellationTokenSource&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void StartAsynchronousComputation&#40;AsynchronousComputationToStart computationToStart, Request requestToCompleteSynchronously&#41;&#10; &#123;&#10; var cancellationToken &#61; computationToStart.CancellationTokenSource.Token&#59;&#10;&#10; try&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; DO NOT ACCESS ANY FIELDS OR STATE BEYOND THIS POINT. Since this function&#10; &#47;&#47; runs unsynchronized, it&#39;s possible that during this function this request&#10; &#47;&#47; might be cancelled, and then a whole additional request might start and&#10; &#47;&#47; complete inline, and cache the result. By grabbing state before we check&#10; &#47;&#47; the cancellation token, we can be assured that we are only operating on&#10; &#47;&#47; a state that was complete.&#10;&#10; ExceptionUtilities.ExecuteWithErrorReporting&#40;&#40;&#41; &#61;&#62;&#10; &#123;&#10; &#47;&#47; We avoid creating a full closure just to pass the token along&#10; &#47;&#47; Also, use TaskContinuationOptions.ExecuteSynchronously so that we inline &#10; &#47;&#47; the continuation if asynchronousComputeFunction completes synchronously&#10; var task &#61; computationToStart.AsynchronousComputeFunction&#40;cancellationToken&#41;&#59;&#10;&#10; task.ContinueWith&#40;&#10; &#40;t, s&#41; &#61;&#62; CompleteWithTask&#40;t, &#40;&#40;CancellationTokenSource&#41;s&#41;.Token&#41;,&#10; computationToStart.CancellationTokenSource,&#10; cancellationToken,&#10; TaskContinuationOptions.ExecuteSynchronously,&#10; TaskScheduler.Default&#41;&#59;&#10;&#10; if &#40;requestToCompleteSynchronously &#33;&#61; null &#38;&#38; task.IsCompleted&#41;&#10; &#123;&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; task &#61; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;task&#41;&#59;&#10; &#125;&#10;&#10; requestToCompleteSynchronously.CompleteFromTaskSynchronously&#40;task&#41;&#59;&#10; &#125;&#10; &#125;&#41;&#59;&#10; &#125;&#10; catch &#40;OperationCanceledException oce&#41;&#10; &#123;&#10; &#47;&#47; As long as it&#39;s the right token, this means that our thread was the first thread&#10; &#47;&#47; to start an asynchronous computation, but the requestor cancelled as we were starting up&#10; &#47;&#47; the computation.&#10; if &#40;oce.CancellationToken &#33;&#61; cancellationToken&#41;&#10; &#123;&#10; ExceptionUtilities.FailFast&#40;oce&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; private void CompleteWithTask&#40;Task&#60;T&#62; task, CancellationToken cancellationToken&#41;&#10; &#123;&#10; IEnumerable&#60;Request&#62; requestsToComplete&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If the underlying computation was cancelled, then all state was already updated in OnAsynchronousRequestCancelled&#10; &#47;&#47; and there is no new work to do here. We &#42;must&#42; use the local one since this completion may be running far after&#10; &#47;&#47; the background computation was cancelled and a new one might have already been enqueued. We must do this&#10; &#47;&#47; check here under the lock to ensure proper synchronization with OnAsynchronousRequestCancelled.&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; The computation is complete, so get all requests to complete and null out the list. We&#39;ll create another one&#10; &#47;&#47; later if it&#39;s needed&#10; requestsToComplete &#61; this.requests &#63;&#63; SpecializedCollections.EmptyEnumerable&#60;Request&#62;&#40;&#41;&#59;&#10; this.requests &#61; null&#59;&#10;&#10; &#47;&#47; The computations are done&#10; this.asynchronousComputationCancellationSource &#61; null&#59;&#10; this.computationActive &#61; false&#59;&#10; task &#61; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;task&#41;&#59;&#10; &#125;&#10;&#10; foreach &#40;var requestToComplete in requestsToComplete&#41;&#10; &#123;&#10; requestToComplete.CompleteFromTaskAsynchronously&#40;task&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; private Task&#60;T&#62; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; if &#40;this.cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return this.cachedResult&#59;&#10; &#125;&#10; else&#10; &#123;&#10; if &#40;cacheResult &#38;&#38; task.Status &#61;&#61; TaskStatus.RanToCompletion&#41;&#10; &#123;&#10; &#47;&#47; Hold onto the completed task. We can get rid of the computation functions for good&#10; this.cachedResult &#61; task&#59;&#10; this.asynchronousComputeFunction &#61; null&#59;&#10; this.synchronousComputeFunction &#61; null&#59;&#10; &#125;&#10;&#10; return task&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void OnAsynchronousRequestCancelled&#40;object state&#41;&#10; &#123;&#10; var request &#61; &#40;Request&#41;state&#59;&#10; CancellationTokenSource cancellationTokenSource &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; &#47;&#47; Now try to remove it. It&#39;s possible that requests may already be null. You could&#10; &#47;&#47; imagine that cancellation was requested, but before we could aquire the lock&#10; &#47;&#47; here the computation completed and the entire CompleteWithTask synchronized&#10; &#47;&#47; block ran. In that case, the requests collection may already be null, or it&#10; &#47;&#47; &#40;even scarier&#33;&#41; may have been replaced with another collection because another&#10; &#47;&#47; computation has started.&#10; if &#40;this.requests &#33;&#61; null&#41;&#10; &#123;&#10; if &#40;this.requests.Remove&#40;request&#41;&#41;&#10; &#123;&#10; if &#40;this.requests.Count &#61;&#61; 0&#41;&#10; &#123;&#10; this.requests &#61; null&#59;&#10;&#10; if &#40;this.asynchronousComputationCancellationSource &#33;&#61; null&#41;&#10; &#123;&#10; cancellationTokenSource &#61; this.asynchronousComputationCancellationSource&#59;&#10; this.asynchronousComputationCancellationSource &#61; null&#59;&#10; this.computationActive &#61; false&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; request.CancelAsynchronously&#40;&#41;&#59;&#10;&#10; if &#40;cancellationTokenSource &#33;&#61; null&#41;&#10; &#123;&#10; cancellationTokenSource.Cancel&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; Using inheritance instead of wrapping a TaskCompletionSource to avoid a second allocation&#10; private class Request &#58; TaskCompletionSource&#60;T&#62;&#10; &#123;&#10; private CancellationTokenRegistration cancellationTokenRegistration&#59;&#10;&#10; public void RegisterForCancellation&#40;Action&#60;object&#62; callback, CancellationToken cancellationToken&#41;&#10; &#123;&#10; cancellationTokenRegistration &#61; cancellationToken.Register&#40;callback, this&#41;&#59;&#10; &#125;&#10;&#10; public void CompleteFromTaskAsynchronously&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; System.Threading.Tasks.Task.Factory.StartNew&#40;CompleteFromTaskSynchronouslyStub, task, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default&#41;&#59;&#10; &#125;&#10;&#10; private void CompleteFromTaskSynchronouslyStub&#40;object task&#41;&#10; &#123;&#10; CompleteFromTaskSynchronously&#40;&#40;Task&#60;T&#62;&#41;task&#41;&#59;&#10; &#125;&#10;&#10; public void CompleteFromTaskSynchronously&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; if &#40;task.Status &#61;&#61; TaskStatus.RanToCompletion&#41;&#10; &#123;&#10; if &#40;TrySetResult&#40;task.Result&#41;&#41;&#10; &#123;&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; else if &#40;task.Status &#61;&#61; TaskStatus.Faulted&#41;&#10; &#123;&#10; if &#40;TrySetException&#40;task.Exception&#41;&#41;&#10; &#123;&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; else&#10; &#123;&#10; CancelSynchronously&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; public void CancelAsynchronously&#40;&#41;&#10; &#123;&#10; &#47;&#47; Since there could be synchronous continuations on the TaskCancellationSource, we queue this to the threadpool&#10; &#47;&#47; to avoid inline running of other operations.&#10; System.Threading.Tasks.Task.Factory.StartNew&#40;CancelSynchronously, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default&#41;&#59;&#10; &#125;&#10;&#10; private void CancelSynchronously&#40;&#41;&#10; &#123;&#10; if &#40;TrySetCanceled&#40;&#41;&#41;&#10; &#123;&#10; &#47;&#47; Paranoia&#58; the only reason we should ever get here is if the CancellationToken that&#10; &#47;&#47; we registered against was cancelled, but just in case, dispose the registration&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; A lightweight mutual exclusion object which supports waiting with cancellation and prevents&#10; &#47;&#47;&#47; recursion &#40;i.e. you may not call Wait if you already hold the lock&#41;&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; The &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62; provides a lightweight mutual exclusion class that doesn&#39;t&#10; &#47;&#47;&#47; use Windows kernel synchronization primitives.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; The implementation is distilled from the workings of &#60;see cref&#61;&#34;T&#58;System.Threading.SemaphoreSlim&#34;&#47;&#62;&#10; &#47;&#47;&#47; The basic idea is that we use a regular sync object &#40;Monitor.Enter&#47;Exit&#41; to guard the setting&#10; &#47;&#47;&#47; of an &#39;owning thread&#39; field. If, during the Wait, we find the lock is held by someone else&#10; &#47;&#47;&#47; then we register a cancellation callback and enter a &#34;Monitor.Wait&#34; loop. If the cancellation&#10; &#47;&#47;&#47; callback fires, then it &#34;pulses&#34; all the waiters to wake them up and check for cancellation.&#10; &#47;&#47;&#47; Waiters are also &#34;pulsed&#34; when leaving the lock.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; All public members of &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62; are thread-safe and may be used concurrently&#10; &#47;&#47;&#47; from multiple threads.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; internal sealed class NonReentrantLock&#10; &#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; A synchronization object to protect access to the &#60;see cref&#61;&#34;F&#58;owningThread&#34;&#47;&#62; field and to be pulsed&#10; &#47;&#47;&#47; when &#60;see cref&#61;&#34;M&#58;Release&#34;&#47;&#62; is called and during cancellation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private readonly object syncLock&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Indicates which thread currently holds the lock. If null, then the lock is available.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private volatile Thread owningThread&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Constructor.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;useThisInstanceForSynchronization&#34;&#62;If false &#40;the default&#41;, then the class&#10; &#47;&#47;&#47; allocates an internal object to be used as a sync lock.&#10; &#47;&#47;&#47; If true, then the sync lock object will be the NonReentrantLock instance itself. This&#10; &#47;&#47;&#47; saves an allocation but a client may not safely further use this instance in a call to&#10; &#47;&#47;&#47; Monitor.Enter&#47;Exit or in a &#34;lock&#34; statement.&#10; &#47;&#47;&#47; &#60;&#47;param&#62;&#10; public NonReentrantLock&#40;bool useThisInstanceForSynchronization &#61; false&#41;&#10; &#123;&#10; this.syncLock &#61; useThisInstanceForSynchronization &#63; this &#58; new object&#40;&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Shared factory for use in lazy initialization.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public static readonly Func&#60;NonReentrantLock&#62; Factory &#61; &#40;&#41; &#61;&#62; new NonReentrantLock&#40;useThisInstanceForSynchronization&#58; true&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Blocks the current thread until it can enter the &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62;, while observing a&#10; &#47;&#47;&#47; &#60;see cref&#61;&#34;T&#58;System.Threading.CancellationToken&#34;&#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; Recursive locking is not supported. i.e. A thread may not call Wait successfully twice without an&#10; &#47;&#47;&#47; intervening &#60;see cref&#61;&#34;Release&#34;&#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;cancellationToken&#34;&#62;The &#60;see cref&#61;&#34;T&#58;System.Threading.CancellationToken&#34;&#47;&#62; token to&#10; &#47;&#47;&#47; observe.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;System.OperationCanceledException&#34;&#62;&#60;paramref name&#61;&#34;cancellationToken&#34;&#47;&#62; was&#10; &#47;&#47;&#47; canceled.&#60;&#47;exception&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;System.LockRecursionException&#34;&#62;The caller already holds the lock&#60;&#47;exception&#62;&#10; public void Wait&#40;CancellationToken cancellationToken &#61; default&#40;CancellationToken&#41;&#41;&#10; &#123;&#10; if &#40;this.IsOwnedByMe&#41;&#10; &#123;&#10; throw new LockRecursionException&#40;&#41;&#59;&#10; &#125;&#10;&#10; CancellationTokenRegistration cancellationTokenRegistration &#61; default&#40;CancellationTokenRegistration&#41;&#59;&#10;&#10; if &#40;cancellationToken.CanBeCanceled&#41;&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; Fast path to try and avoid allocations in callback registration.&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; if &#40;&#33;this.IsLocked&#41;&#10; &#123;&#10; this.TakeOwnership&#40;&#41;&#59;&#10; return&#59;&#10; &#125;&#10; &#125;&#10;&#10; cancellationTokenRegistration &#61; cancellationToken.Register&#40;cancellationTokenCanceledEventHandler, this.syncLock, useSynchronizationContext&#58; false&#41;&#59;&#10; &#125;&#10;&#10; using &#40;cancellationTokenRegistration&#41;&#10; &#123;&#10; &#47;&#47; PERF&#58; First spin wait for the lock to become available, but only up to the first planned yield.&#10; &#47;&#47; This additional amount of spinwaiting was inherited from SemaphoreSlim&#39;s implementation where&#10; &#47;&#47; it showed measurable perf gains in test scenarios.&#10; SpinWait spin &#61; new SpinWait&#40;&#41;&#59;&#10; while &#40;this.IsLocked &#38;&#38; &#33;spin.NextSpinWillYield&#41;&#10; &#123;&#10; spin.SpinOnce&#40;&#41;&#59;&#10; &#125;&#10;&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; while &#40;this.IsLocked&#41;&#10; &#123;&#10; &#47;&#47; If cancelled, we throw. Trying to wait could lead to deadlock.&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; using &#40;Logger.LogBlock&#40;FeatureId.WaitIndicator, FunctionId.Utilities_NonReentrantLock_BlockingWait, cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; Another thread holds the lock. Wait until we get awoken either&#10; &#47;&#47; by some code calling &#34;Release&#34; or by cancellation.&#10; Monitor.Wait&#40;this.syncLock&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; We now hold the lock&#10; this.TakeOwnership&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Exit the mutual exclusion.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; The calling thread must currently hold the lock.&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;Roslyn.Utilities.ContractFailureException&#34;&#62;The lock is not currently held by the calling thread.&#60;&#47;exception&#62;&#10; public void Release&#40;&#41;&#10; &#123;&#10; AssertHasLock&#40;&#41;&#59;&#10;&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; this.ReleaseOwnership&#40;&#41;&#59;&#10;&#10; &#47;&#47; Release one waiter&#10; Monitor.Pulse&#40;this.syncLock&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Determine if the lock is currently held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;returns&#62;True if the lock is currently held by the calling thread.&#60;&#47;returns&#62;&#10; public bool LockHeldByMe&#40;&#41;&#10; &#123;&#10; return this.IsOwnedByMe&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Throw an exception if the lock is not held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;Roslyn.Utilities.ContractFailureException&#34;&#62;The lock is not currently held by the calling thread.&#60;&#47;exception&#62;&#10; public void AssertHasLock&#40;&#41;&#10; &#123;&#10; Contract.ThrowIfFalse&#40;LockHeldByMe&#40;&#41;&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Checks if the lock is currently held.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool IsLocked&#10; &#123;&#10; get&#10; &#123;&#10; return this.owningThread &#33;&#61; null&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Checks if the lock is currently held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool IsOwnedByMe&#10; &#123;&#10; get&#10; &#123;&#10; return this.owningThread &#61;&#61; Thread.CurrentThread&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Take ownership of the lock &#40;by the calling thread&#41;. The lock may not already&#10; &#47;&#47;&#47; be held by any other code.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private void TakeOwnership&#40;&#41;&#10; &#123;&#10; Contract.Assert&#40;&#33;this.IsLocked&#41;&#59;&#10; this.owningThread &#61; Thread.CurrentThread&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Release ownership of the lock. The lock must already be held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private void ReleaseOwnership&#40;&#41;&#10; &#123;&#10; Contract.Assert&#40;this.IsOwnedByMe&#41;&#59;&#10; this.owningThread &#61; null&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Action object passed to a cancellation token registration.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private static readonly Action&#60;object&#62; cancellationTokenCanceledEventHandler &#61; new Action&#60;object&#62;&#40;CancellationTokenCanceledEventHandler&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Callback executed when a cancellation token is canceled during a Wait.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;obj&#34;&#62;The syncLock that protects a &#60;see cref&#61;&#34;NonReentrantLock &#34;&#47;&#62; instance.&#60;&#47;param&#62;&#10; private static void CancellationTokenCanceledEventHandler&#40;object obj&#41;&#10; &#123;&#10; lock &#40;obj&#41;&#10; &#123;&#10; &#47;&#47; Release all waiters to check their cancellation tokens.&#10; Monitor.PulseAll&#40;obj&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#125;</pre>2013-05-23T06:05:29-07:000qhttp://rise4fun.com/Seal/0qSeal/0q<pre>using System&#59;&#13;&#10;using System.Linq&#59;&#13;&#10;using System.Collections&#59;&#13;&#10;using System.Collections.Generic&#59;&#13;&#10;using System.Collections.ObjectModel&#59;&#13;&#10;&#13;&#10;namespace GameOfLife.Model&#13;&#10;&#123;&#13;&#10; &#47;&#47;&#47; &#60;summary&#62;&#13;&#10; &#47;&#47;&#47; Provides methods to manage the life of cells.&#13;&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#13;&#10; static class CellLifeManager&#13;&#10; &#123;&#13;&#10; &#35;region Public methods&#13;&#10;&#13;&#10; &#47;&#47;&#47; &#60;summary&#62;&#13;&#10; &#47;&#47;&#47; Calculates and assigns the neighbours for the given collection of cells.&#13;&#10; &#47;&#47;&#47; &#60;para&#62;&#60;remarks&#62;The algorithmic complexity of the method is O&#40;n&#94;2&#41;.&#60;&#47;remarks&#62;&#60;&#47;para&#62;&#13;&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#13;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;theCellCollection&#34;&#62;The read-only collection of the cells.&#60;&#47;param&#62;&#13;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;theTotalColumns&#34;&#62;Total no. of columns in which cells are arranged.&#60;&#47;param&#62;&#13;&#10; public static void SetNeighbours&#40;ReadOnlyCollection&#60;ICell&#62; theCellCollection, int theTotalColumns&#41;&#13;&#10; &#123;&#13;&#10; for &#40;int aCount &#61; 0&#59; aCount &#60; theCellCollection.Count&#59; aCount&#43;&#43;&#41;&#13;&#10; &#123;&#13;&#10; ICell aCell &#61; theCellCollection&#91;aCount&#93;&#59;&#13;&#10; bool aIsFirstColumnCell &#61; aCount &#37; theTotalColumns &#61;&#61; 0&#59;&#13;&#10; bool aIsLastColumnCell &#61; &#40;aCount &#43; 1&#41; &#37; theTotalColumns &#61;&#61; 0&#59;&#13;&#10; IEnumerable&#60;int&#62; aNeighbourIndex&#59;&#13;&#10; if &#40;aIsFirstColumnCell&#41;&#13;&#10; &#123;&#13;&#10; aNeighbourIndex &#61; TraverseFullAntiClockWise&#40;aCount, theTotalColumns&#41;&#13;&#10; .Where&#40;T &#61;&#62; &#40;T &#62;&#61; 0 &#38;&#38; T &#60; theCellCollection.Count &#38;&#38;&#13;&#10; &#40;T &#33;&#61; aCount &#43; theTotalColumns - 1&#41; &#38;&#38; &#40;T &#33;&#61; aCount - theTotalColumns - 1&#41;&#13;&#10; &#38;&#38; &#40;T &#33;&#61; aCount - 1&#41;&#41;&#41;&#59;&#13;&#10; &#125;&#13;&#10; else if &#40;aIsLastColumnCell&#41;&#13;&#10; &#123;&#13;&#10; aNeighbourIndex &#61; TraverseFullAntiClockWise&#40;aCount, theTotalColumns&#41;&#13;&#10; .Where&#40;T &#61;&#62; &#40;T &#62;&#61; 0 &#38;&#38; T &#60; theCellCollection.Count &#38;&#38;&#13;&#10; &#40;T &#33;&#61; aCount &#43; theTotalColumns &#43; 1&#41; &#38;&#38; &#40;T &#33;&#61; aCount - theTotalColumns &#43; 1&#41;&#13;&#10; &#38;&#38; &#40;T &#33;&#61; aCount &#43; 1&#41;&#41;&#41;&#59;&#13;&#10; &#125;&#13;&#10; else&#13;&#10; &#123;&#13;&#10; aNeighbourIndex &#61; TraverseFullAntiClockWise&#40;aCount, theTotalColumns&#41;&#13;&#10; .Where&#40;T &#61;&#62; &#40;T &#62;&#61; 0 &#38;&#38; T &#60; theCellCollection.Count&#41;&#41;&#59;&#13;&#10; &#125;&#13;&#10; aCell.Neighbours.Clear&#40;&#41;&#59;&#13;&#10; foreach &#40;int aIndex in aNeighbourIndex&#41;&#13;&#10; &#123;&#13;&#10; aCell.Neighbours.Add&#40;theCellCollection&#91;aIndex&#93;&#41;&#59;&#13;&#10; &#125;&#13;&#10; &#125;&#13;&#10; &#125;&#13;&#10;&#13;&#10; &#47;&#47;&#47; &#60;summary&#62;&#13;&#10; &#47;&#47;&#47; Sets the alive status of the cells based on following rules &#58;&#13;&#10; &#47;&#47;&#47; &#60;para&#62;&#60;&#47;para&#62;&#13;&#10; &#47;&#47;&#47; &#60;para&#62;&#60;&#47;para&#62;&#13;&#10; &#47;&#47;&#47; &#60;para&#62;&#60;&#47;para&#62;&#13;&#10; &#47;&#47;&#47; &#60;para&#62;&#60;remarks&#62;The algorithmic complexity of the method is O&#40;n&#41;.&#60;&#47;remarks&#62;&#60;&#47;para&#62;&#13;&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#13;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;theCellCollection&#34;&#62;The readonly collection of the cells.&#60;&#47;param&#62;&#13;&#10; public static void SetAliveStatus&#40;ReadOnlyCollection&#60;ICell&#62; theCellCollection&#41;&#13;&#10; &#123;&#13;&#10; int aFinalCount &#61; theCellCollection.Count&#59;&#13;&#10; IList&#60;bool&#62; aIsAliveList &#61; new List&#60;bool&#62;&#40;&#41;.PopulateDefaultValues&#40;aFinalCount, false&#41;&#59;&#13;&#10;&#13;&#10; for &#40;int i &#61; 0&#59; i &#60; aFinalCount&#59; i&#43;&#43;&#41;&#13;&#10; &#123;&#13;&#10; aIsAliveList&#91;i&#93; &#61; ShouldBeAlive&#40;theCellCollection&#91;i&#93;&#41;&#59;&#13;&#10; &#125;&#13;&#10;&#13;&#10; for &#40;int i &#61; 0&#59; i &#60; aFinalCount&#59; i&#43;&#43;&#41;&#13;&#10; &#123;&#13;&#10; theCellCollection&#91;i&#93;.IsAlive &#61; null&#59;&#13;&#10; theCellCollection&#91;i&#93;.IsAlive &#61; aIsAliveList&#91;i&#93;&#59;&#13;&#10; &#125;&#13;&#10; &#125;&#13;&#10; &#47;&#47;&#47; &#60;summary&#62;&#13;&#10; &#47;&#47;&#47; Makes the alive status of all cells to be indeterminate. Value is &#60;see cref&#61;&#34;System.Null&#34;&#47;&#62;&#13;&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#13;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;theCellCollection&#34;&#62;The read-only collection of the cells.&#60;&#47;param&#62;&#13;&#10; public static void ResetCellAliveStatus&#40;ReadOnlyCollection&#60;ICell&#62; theCellCollection&#41;&#13;&#10; &#123;&#13;&#10; foreach &#40;ICell aCell in theCellCollection&#41;&#13;&#10; &#123;&#13;&#10; aCell.IsAlive &#61; null&#59;&#13;&#10; &#125;&#13;&#10; &#125;&#13;&#10;&#13;&#10; &#35;endregion&#13;&#10;&#13;&#10; &#35;region Private methods&#13;&#10;&#13;&#10; &#47;&#47;&#47; &#60;summary&#62;&#13;&#10; &#47;&#47;&#47; Generates a list of all possible neighbours for a particular cell based upon the cells position.&#13;&#10; &#47;&#47;&#47; Calculation is made starting from top , traversing in anti-clockwise direction.&#13;&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#13;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;theIndex&#34;&#62;The zero based position of the cell in the grid.&#60;&#47;param&#62;&#13;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;theTotalColumns&#34;&#62;Total no. of columns in which cells are arranged.&#60;&#47;param&#62;&#13;&#10; &#47;&#47;&#47; &#60;returns&#62;List&#123;int&#125; containing all possible neighbours list.&#60;&#47;returns&#62;&#13;&#10; private static List&#60;int&#62; TraverseFullAntiClockWise&#40;int theIndex, int theTotalColumns&#41;&#13;&#10; &#123;&#13;&#10; return new List&#60;int&#62;&#40;&#41;&#13;&#10; &#123;&#13;&#10; theIndex &#43; theTotalColumns - 1,&#13;&#10; theIndex &#43; theTotalColumns,&#13;&#10; theIndex &#43; theTotalColumns &#43; 1,&#13;&#10; theIndex &#43; 1,&#13;&#10; theIndex - theTotalColumns &#43; 1,&#13;&#10; theIndex - theTotalColumns,&#13;&#10; theIndex - theTotalColumns - 1,&#13;&#10; theIndex - 1&#13;&#10; &#125;&#59;&#13;&#10; &#125;&#13;&#10;&#13;&#10; &#47;&#47;&#47; &#60;summary&#62;&#13;&#10; &#47;&#47;&#47; Determines whether a cell should be alive or not.&#13;&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#13;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;theCell&#34;&#62;The cell which alive status has to be determined.&#60;&#47;param&#62;&#13;&#10; &#47;&#47;&#47; &#60;returns&#62;True if alive or vice versa.&#60;&#47;returns&#62;&#13;&#10; private static bool ShouldBeAlive&#40;ICell theCell&#41;&#13;&#10; &#123;&#13;&#10; bool aIsAlive &#61; theCell.IsAlive.HasValue &#38;&#38; theCell.IsAlive.Value&#59;&#13;&#10; int aAliveNeighboursCount &#61; theCell.Neighbours.Where&#40;T &#61;&#62; T.IsAlive.HasValue &#38;&#38; T.IsAlive.Value&#41;.Count&#40;&#41;&#59;&#13;&#10; if &#40;&#40;aIsAlive &#38;&#38; &#40;aAliveNeighboursCount &#61;&#61; 2 &#124;&#124; aAliveNeighboursCount &#61;&#61; 3&#41;&#41;&#13;&#10; &#124;&#124; &#40;aAliveNeighboursCount &#61;&#61; 3 &#38;&#38; &#33;aIsAlive&#41;&#41;&#13;&#10; &#123;&#13;&#10; return true&#59;&#13;&#10; &#125;&#13;&#10; return false&#59;&#13;&#10; &#125;&#13;&#10;&#13;&#10; &#47;&#47;&#47; &#60;summary&#62;&#13;&#10; &#47;&#47;&#47; Extension method to create a boolean list of specified count with specified default value.&#13;&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#13;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;theList&#34;&#62;The list to be populated.&#60;&#47;param&#62;&#13;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;theFinalCount&#34;&#62;Total no of elements to be added.&#60;&#47;param&#62;&#13;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;theDefaultValue&#34;&#62;Default value for the element.&#60;&#47;param&#62;&#13;&#10; &#47;&#47;&#47; &#60;returns&#62;&#60;&#47;returns&#62;&#13;&#10; private static IList&#60;bool&#62; PopulateDefaultValues&#40;this IList&#60;bool&#62; theList, int theFinalCount, bool theDefaultValue&#41;&#13;&#10; &#123;&#13;&#10; for &#40;int i &#61; 0&#59; i &#60; theFinalCount&#59; i&#43;&#43;&#41;&#13;&#10; &#123;&#13;&#10; theList.Add&#40;theDefaultValue&#41;&#59;&#13;&#10; &#125;&#13;&#10; return theList&#59;&#13;&#10; &#125;&#13;&#10; &#35;endregion&#13;&#10; &#125;&#13;&#10;&#125;&#13;&#10;</pre>2013-05-23T06:05:29-07:00wthttp://rise4fun.com/Seal/wtSeal/wt<pre>using System&#59;&#10;&#10;class XYZ &#123;&#10; public static int add1&#40;int x, int y&#41; &#123; return x&#43;y&#59; &#125;&#10; int f1, f2&#59;&#10; public static int add2&#40;XYZ p&#41; &#123; return p.f1 &#43; p.f2&#59; &#125;&#10; public static void modify&#40;XYZ p&#41; &#123; p.f1 &#61; p.f1 &#43; p.f2&#59; &#125;&#10; public void localModify&#40;&#41; &#123;&#10; XYZ temp &#61; new XYZ&#40;&#41;&#59;&#10; modify&#40;temp&#41;&#59;&#10; &#125;&#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00WLhttp://rise4fun.com/Seal/WLSeal/WL<pre>using System&#59;&#10;using System.Collections.Generic&#59;&#10;using System.Runtime.CompilerServices&#59;&#10;using System.Threading&#59;&#10;using System.Threading.Tasks&#59;&#10;&#10;namespace Roslyn.Utilities&#10;&#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Represents a value that can be retrieved synchronously or asynchronously by many clients.&#10; &#47;&#47;&#47; The value will be computed on-demand the moment the first client asks for it. While being&#10; &#47;&#47;&#47; computed, more clients can request the value. As long as there are outstanding clients the&#10; &#47;&#47;&#47; underlying computation will proceed. If all outstanding clients cancel their request then&#10; &#47;&#47;&#47; the underlying value computation will be cancelled as well.&#10; &#47;&#47;&#47; &#10; &#47;&#47;&#47; Creators of an &#60;see cref&#61;&#34;AsyncLazy&#123;T&#125;&#34; &#47;&#62; can specify whether the result of the computation is&#10; &#47;&#47;&#47; cached for future requests or not. Choosing to not cache means the computation functions are kept&#10; &#47;&#47;&#47; alive, whereas caching means the value &#40;but not functions&#41; are kept alive once complete.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; internal sealed class AsyncLazy&#60;T&#62;&#10; &#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The underlying function that starts an asynchronous computation of the resulting value.&#10; &#47;&#47;&#47; Null&#39;ed out once we&#39;ve computed the result and we&#39;ve been asked to cache it. Otherwise,&#10; &#47;&#47;&#47; it is kept around in case the value needs to be computed again.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The underlying function that starts an synchronous computation of the resulting value.&#10; &#47;&#47;&#47; Null&#39;ed out once we&#39;ve computed the result and we&#39;ve been asked to cache it, or if we&#10; &#47;&#47;&#47; didn&#39;t get any synchronous function given to us in the first place.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Func&#60;CancellationToken, T&#62; synchronousComputeFunction&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Whether or not we should keep the value around once we&#39;ve computed it.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private readonly bool cacheResult&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The Task that holds the cached result.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Task&#60;T&#62; cachedResult&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Mutex used to protect reading and writing to all mutable objects and fields. Traces&#10; &#47;&#47;&#47; indicate that there&#39;s negligible contention on this lock, hence we can save some memory&#10; &#47;&#47;&#47; by using a single lock for all AsyncLazy instances. Only trivial and non-reentrant work&#10; &#47;&#47;&#47; should be done while holding the lock.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private static readonly NonReentrantLock gate &#61; new NonReentrantLock&#40;useThisInstanceForSynchronization&#58; true&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The hash set of all currently outstanding asynchronous requests. Null if there are no requests,&#10; &#47;&#47;&#47; and will never be empty.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private HashSet&#60;Request&#62; requests &#61; null&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; If an asynchronous request is active, the CancellationTokenSource that allows for&#10; &#47;&#47;&#47; cancelling the underlying computation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private CancellationTokenSource asynchronousComputationCancellationSource &#61; null&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Whether a computation is active or queued on any thread, whether synchronous or&#10; &#47;&#47;&#47; asynchronous.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool computationActive &#61; false&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Creates an AsyncLazy that always returns the value, analogous to &#60;see cref&#61;&#34;Task.FromResult&#123;T&#125;&#34; &#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public AsyncLazy&#40;T value&#41;&#10; &#123;&#10; this.cacheResult &#61; true&#59;&#10; this.cachedResult &#61; new Task&#40;new Action&#40;&#40;&#41; &#61;&#62;value&#41;&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Important&#58; callers of this constructor should ensure that the compute function returns&#10; &#47;&#47;&#47; a task in a non-blocking fashion. i.e. the function should &#42;not&#42; synchronously compute&#10; &#47;&#47;&#47; a value and then return it using Task.FromResult. Instead, it should return an actual&#10; &#47;&#47;&#47; task that operates asynchronously. If this function synchronously computes a value&#10; &#47;&#47;&#47; then that will cause locks to be held in this type for excessive periods of time.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public AsyncLazy&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, bool cacheResult&#41;&#10; &#58; this&#40;asynchronousComputeFunction, synchronousComputeFunction&#58; null, cacheResult&#58; cacheResult&#41;&#10; &#123;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Creates an AsyncLazy that supports both asynchronous computation and inline synchronous&#10; &#47;&#47;&#47; computation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;asynchronousComputeFunction&#34;&#62;A function called to start the asynchronous&#10; &#47;&#47;&#47; computation. This function should be cheap and non-blocking.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;synchronousComputeFunction&#34;&#62;A function to do the work synchronously, which&#10; &#47;&#47;&#47; is allowed to block. This function should not be implemented by a simple Wait on the&#10; &#47;&#47;&#47; asynchronous value. If that&#39;s all you are doing, just don&#39;t pass a synchronous function&#10; &#47;&#47;&#47; in the first place.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;cacheResult&#34;&#62;Whether the result should be cached once the computation is&#10; &#47;&#47;&#47; complete.&#60;&#47;param&#62;&#10; public AsyncLazy&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, Func&#60;CancellationToken, T&#62; synchronousComputeFunction, bool cacheResult&#41;&#10; &#123;&#10; Contract.ThrowIfNull&#40;asynchronousComputeFunction&#41;&#59;&#10;&#10; this.asynchronousComputeFunction &#61; asynchronousComputeFunction&#59;&#10; this.synchronousComputeFunction &#61; synchronousComputeFunction&#59;&#10; this.cacheResult &#61; cacheResult&#59;&#10; &#125;&#10;&#10; &#35;region Lock Wrapper for Invariant Checking&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Takes the lock for this object and if acquired validates the invariants of this class.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private WaitThatValidatesInvariants TakeLock&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; gate.Wait&#40;cancellationToken&#41;&#59;&#10; AssertInvariants_NoLock&#40;&#41;&#59;&#10; return new WaitThatValidatesInvariants&#40;this&#41;&#59;&#10; &#125;&#10;&#10; private struct WaitThatValidatesInvariants &#58; IDisposable&#10; &#123;&#10; private readonly AsyncLazy&#60;T&#62; asyncLazy&#59;&#10;&#10; public WaitThatValidatesInvariants&#40;AsyncLazy&#60;T&#62; asyncLazy&#41;&#10; &#123;&#10; this.asyncLazy &#61; asyncLazy&#59;&#10; &#125;&#10;&#10; public void Dispose&#40;&#41;&#10; &#123;&#10; asyncLazy.AssertInvariants_NoLock&#40;&#41;&#59;&#10; gate.Release&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void AssertInvariants_NoLock&#40;&#41;&#10; &#123;&#10; &#47;&#47; Invariant &#35;1&#58; thou shalt never have an asynchronous computation running without it&#10; &#47;&#47; being considered a computation&#10; Contract.ThrowIfTrue&#40;this.asynchronousComputationCancellationSource &#33;&#61; null &#38;&#38;&#10; &#33;this.computationActive&#41;&#59;&#10;&#10; &#47;&#47; Invariant &#35;2&#58; thou shalt never waste memory holding onto empty HashSets&#10; Contract.ThrowIfTrue&#40;this.requests &#33;&#61; null &#38;&#38;&#10; this.requests.Count &#61;&#61; 0&#41;&#59;&#10;&#10; &#47;&#47; Invariant &#35;3&#58; thou shalt never have an request if there is not&#10; &#47;&#47; something trying to compute it&#10; Contract.ThrowIfTrue&#40;this.requests &#33;&#61; null &#38;&#38;&#10; &#33;this.computationActive&#41;&#59;&#10;&#10; &#47;&#47; Invariant &#35;4&#58; thou shalt never have a cached value and any computation function&#10; Contract.ThrowIfTrue&#40;this.cachedResult &#33;&#61; null &#38;&#38;&#10; &#40;this.synchronousComputeFunction &#33;&#61; null &#124;&#124; this.asynchronousComputeFunction &#33;&#61; null&#41;&#41;&#59;&#10;&#10; &#47;&#47; Invariant &#35;5&#58; thou shalt never have a synchronous computation function but not an&#10; &#47;&#47; asynchronous one&#10; Contract.ThrowIfTrue&#40;this.asynchronousComputeFunction &#61;&#61; null &#38;&#38; this.synchronousComputeFunction &#33;&#61; null&#41;&#59;&#10; &#125;&#10;&#10; &#35;endregion&#10;&#10; public bool TryGetValue&#40;out T result&#41;&#10; &#123;&#10; &#47;&#47; No need to lock here since this is only a fast check to &#10; &#47;&#47; see if the result is already computed.&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; result &#61; cachedResult.Result&#59;&#10; return true&#59;&#10; &#125;&#10;&#10; result &#61; default&#40;T&#41;&#59;&#10; return false&#59;&#10; &#125;&#10;&#10; public T GetValue&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; Request request &#61; null&#59;&#10; AsynchronousComputationToStart&#63; newAsynchronousComputation &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If cached, get immediately&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return cachedResult.Result&#59;&#10; &#125;&#10;&#10; &#47;&#47; If there is an existing computation active, we&#39;ll just create another request&#10; if &#40;computationActive&#41;&#10; &#123;&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10; &#125;&#10; else if &#40;synchronousComputeFunction &#61;&#61; null&#41;&#10; &#123;&#10; &#47;&#47; A synchronous request, but we have no synchronous function. Start off the async work&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10;&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; else&#10; &#123;&#10; &#47;&#47; We will do the computation here&#10; this.computationActive &#61; true&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; If we simply created a new asynchronous request, so wait for it. Yes, we&#39;re blocking the thread&#10; &#47;&#47; but we don&#39;t want multiple threads attempting to compute the same thing.&#10; if &#40;request &#33;&#61; null&#41;&#10; &#123;&#10; request.RegisterForCancellation&#40;OnAsynchronousRequestCancelled, cancellationToken&#41;&#59;&#10;&#10; &#47;&#47; Since we already registered for cancellation, it&#39;s possible that the registration has&#10; &#47;&#47; cancelled this new computation if we were the only requestor.&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; request&#41;&#59;&#10; &#125;&#10;&#10; return request.Task.WaitAndGetResult&#40;cancellationToken&#41;&#59;&#10; &#125;&#10; else&#10; &#123;&#10; T result&#59;&#10;&#10; &#47;&#47; We are the active computation, so let&#39;s go ahead and compute.&#10; try&#10; &#123;&#10; result &#61; synchronousComputeFunction&#40;cancellationToken&#41;&#59;&#10; &#125;&#10; catch &#40;OperationCanceledException&#41;&#10; &#123;&#10; &#47;&#47; This cancelled for some reason. We don&#39;t care why, but&#10; &#47;&#47; it means anybody else waiting for this result isn&#39;t going to get it&#10; &#47;&#47; from us.&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; this.computationActive &#61; false&#59;&#10;&#10; if &#40;requests &#33;&#61; null&#41;&#10; &#123;&#10; &#47;&#47; There&#39;s a possible improvement here&#58; there might be another synchronous caller who&#10; &#47;&#47; also wants the value. We might consider stealing their thread rather than punting&#10; &#47;&#47; to the thread pool.&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; null&#41;&#59;&#10; &#125;&#10;&#10; throw&#59;&#10; &#125;&#10; catch &#40;Exception ex&#41;&#10; &#123;&#10; &#47;&#47; We faulted for some unknown reason. We should simply fault everything.&#10; TaskCompletionSource&#60;T&#62; tcs &#61; new TaskCompletionSource&#60;T&#62;&#40;&#41;&#59;&#10; tcs.SetException&#40;ex&#41;&#59;&#10; CompleteWithTask&#40;tcs.Task, CancellationToken.None&#41;&#59;&#10;&#10; throw&#59;&#10; &#125;&#10;&#10; &#47;&#47; We have a value, so complete&#10; CompleteWithTask&#40;Task.FromResult&#40;result&#41;, CancellationToken.None&#41;&#59;&#10;&#10; return result&#59;&#10; &#125;&#10; &#125;&#10;&#10; private Request CreateNewRequest_NoLock&#40;&#41;&#10; &#123;&#10; if &#40;this.requests &#61;&#61; null&#41;&#10; &#123;&#10; this.requests &#61; new HashSet&#60;Request&#62;&#40;&#41;&#59;&#10; &#125;&#10;&#10; Request request &#61; new Request&#40;&#41;&#59;&#10; this.requests.Add&#40;request&#41;&#59;&#10; return request&#59;&#10; &#125;&#10;&#10; public Task&#60;T&#62; GetValueAsync&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; &#47;&#47; Optimization&#58; if we&#39;re already cancelled, do not pass go&#10; if &#40;cancellationToken.IsCancellationRequested&#41;&#10; &#123;&#10; return new Task&#60;T&#62;&#40;&#40;&#41; &#61;&#62; default&#40;T&#41;, cancellationToken&#41;&#59;&#10; &#125;&#10;&#10; Request request&#59;&#10; AsynchronousComputationToStart&#63; newAsynchronousComputation &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If cached, get immediately&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return cachedResult&#59;&#10; &#125;&#10;&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10;&#10; &#47;&#47; If we have either synchronous or asynchronous work current in flight, we don&#39;t need to do anything.&#10; &#47;&#47; Otherwise, we shall start an asynchronous computation for this&#10; if &#40;&#33;computationActive&#41;&#10; &#123;&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; We now have the request counted for, register for cancellation. It is critical this is&#10; &#47;&#47; done outside the lock, as our reigstration may immediately fire and we want to avoid the&#10; &#47;&#47; reentrancy&#10; request.RegisterForCancellation&#40;OnAsynchronousRequestCancelled, cancellationToken&#41;&#59;&#10;&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; request&#41;&#59;&#10; &#125;&#10;&#10; return request.Task&#59;&#10; &#125;&#10;&#10; private AsynchronousComputationToStart RegisterAsynchronousComputation_NoLock&#40;&#41;&#10; &#123;&#10; Contract.ThrowIfTrue&#40;this.computationActive&#41;&#59;&#10;&#10; this.asynchronousComputationCancellationSource &#61; new CancellationTokenSource&#40;&#41;&#59;&#10; this.computationActive &#61; true&#59;&#10;&#10; return new AsynchronousComputationToStart&#40;this.asynchronousComputeFunction, this.asynchronousComputationCancellationSource&#41;&#59;&#10; &#125;&#10;&#10; private struct AsynchronousComputationToStart&#10; &#123;&#10; public readonly Func&#60;CancellationToken, Task&#60;T&#62;&#62; AsynchronousComputeFunction&#59;&#10; public readonly CancellationTokenSource CancellationTokenSource&#59;&#10;&#10; public AsynchronousComputationToStart&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, CancellationTokenSource cancellationTokenSource&#41;&#10; &#123;&#10; this.AsynchronousComputeFunction &#61; asynchronousComputeFunction&#59;&#10; this.CancellationTokenSource &#61; cancellationTokenSource&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void StartAsynchronousComputation&#40;AsynchronousComputationToStart computationToStart, Request requestToCompleteSynchronously&#41;&#10; &#123;&#10; var cancellationToken &#61; computationToStart.CancellationTokenSource.Token&#59;&#10;&#10; try&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; DO NOT ACCESS ANY FIELDS OR STATE BEYOND THIS POINT. Since this function&#10; &#47;&#47; runs unsynchronized, it&#39;s possible that during this function this request&#10; &#47;&#47; might be cancelled, and then a whole additional request might start and&#10; &#47;&#47; complete inline, and cache the result. By grabbing state before we check&#10; &#47;&#47; the cancellation token, we can be assured that we are only operating on&#10; &#47;&#47; a state that was complete.&#10;&#10; ExceptionUtilities.ExecuteWithErrorReporting&#40;&#40;&#41; &#61;&#62;&#10; &#123;&#10; &#47;&#47; We avoid creating a full closure just to pass the token along&#10; &#47;&#47; Also, use TaskContinuationOptions.ExecuteSynchronously so that we inline &#10; &#47;&#47; the continuation if asynchronousComputeFunction completes synchronously&#10; var task &#61; computationToStart.AsynchronousComputeFunction&#40;cancellationToken&#41;&#59;&#10;&#10; task.ContinueWith&#40;&#10; &#40;t, s&#41; &#61;&#62; CompleteWithTask&#40;t, &#40;&#40;CancellationTokenSource&#41;s&#41;.Token&#41;,&#10; computationToStart.CancellationTokenSource,&#10; cancellationToken,&#10; TaskContinuationOptions.ExecuteSynchronously,&#10; TaskScheduler.Default&#41;&#59;&#10;&#10; if &#40;requestToCompleteSynchronously &#33;&#61; null &#38;&#38; task.IsCompleted&#41;&#10; &#123;&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; task &#61; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;task&#41;&#59;&#10; &#125;&#10;&#10; requestToCompleteSynchronously.CompleteFromTaskSynchronously&#40;task&#41;&#59;&#10; &#125;&#10; &#125;&#41;&#59;&#10; &#125;&#10; catch &#40;OperationCanceledException oce&#41;&#10; &#123;&#10; &#47;&#47; As long as it&#39;s the right token, this means that our thread was the first thread&#10; &#47;&#47; to start an asynchronous computation, but the requestor cancelled as we were starting up&#10; &#47;&#47; the computation.&#10; if &#40;oce.CancellationToken &#33;&#61; cancellationToken&#41;&#10; &#123;&#10; ExceptionUtilities.FailFast&#40;oce&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; private void CompleteWithTask&#40;Task&#60;T&#62; task, CancellationToken cancellationToken&#41;&#10; &#123;&#10; IEnumerable&#60;Request&#62; requestsToComplete&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If the underlying computation was cancelled, then all state was already updated in OnAsynchronousRequestCancelled&#10; &#47;&#47; and there is no new work to do here. We &#42;must&#42; use the local one since this completion may be running far after&#10; &#47;&#47; the background computation was cancelled and a new one might have already been enqueued. We must do this&#10; &#47;&#47; check here under the lock to ensure proper synchronization with OnAsynchronousRequestCancelled.&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; The computation is complete, so get all requests to complete and null out the list. We&#39;ll create another one&#10; &#47;&#47; later if it&#39;s needed&#10; requestsToComplete &#61; this.requests &#63;&#63; SpecializedCollections.EmptyEnumerable&#60;Request&#62;&#40;&#41;&#59;&#10; this.requests &#61; null&#59;&#10;&#10; &#47;&#47; The computations are done&#10; this.asynchronousComputationCancellationSource &#61; null&#59;&#10; this.computationActive &#61; false&#59;&#10; task &#61; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;task&#41;&#59;&#10; &#125;&#10;&#10; foreach &#40;var requestToComplete in requestsToComplete&#41;&#10; &#123;&#10; requestToComplete.CompleteFromTaskAsynchronously&#40;task&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; private Task&#60;T&#62; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; if &#40;this.cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return this.cachedResult&#59;&#10; &#125;&#10; else&#10; &#123;&#10; if &#40;cacheResult &#38;&#38; task.Status &#61;&#61; TaskStatus.RanToCompletion&#41;&#10; &#123;&#10; &#47;&#47; Hold onto the completed task. We can get rid of the computation functions for good&#10; this.cachedResult &#61; task&#59;&#10; this.asynchronousComputeFunction &#61; null&#59;&#10; this.synchronousComputeFunction &#61; null&#59;&#10; &#125;&#10;&#10; return task&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void OnAsynchronousRequestCancelled&#40;object state&#41;&#10; &#123;&#10; var request &#61; &#40;Request&#41;state&#59;&#10; CancellationTokenSource cancellationTokenSource &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; &#47;&#47; Now try to remove it. It&#39;s possible that requests may already be null. You could&#10; &#47;&#47; imagine that cancellation was requested, but before we could aquire the lock&#10; &#47;&#47; here the computation completed and the entire CompleteWithTask synchronized&#10; &#47;&#47; block ran. In that case, the requests collection may already be null, or it&#10; &#47;&#47; &#40;even scarier&#33;&#41; may have been replaced with another collection because another&#10; &#47;&#47; computation has started.&#10; if &#40;this.requests &#33;&#61; null&#41;&#10; &#123;&#10; if &#40;this.requests.Remove&#40;request&#41;&#41;&#10; &#123;&#10; if &#40;this.requests.Count &#61;&#61; 0&#41;&#10; &#123;&#10; this.requests &#61; null&#59;&#10;&#10; if &#40;this.asynchronousComputationCancellationSource &#33;&#61; null&#41;&#10; &#123;&#10; cancellationTokenSource &#61; this.asynchronousComputationCancellationSource&#59;&#10; this.asynchronousComputationCancellationSource &#61; null&#59;&#10; this.computationActive &#61; false&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; request.CancelAsynchronously&#40;&#41;&#59;&#10;&#10; if &#40;cancellationTokenSource &#33;&#61; null&#41;&#10; &#123;&#10; cancellationTokenSource.Cancel&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; Using inheritance instead of wrapping a TaskCompletionSource to avoid a second allocation&#10; private class Request &#58; TaskCompletionSource&#60;T&#62;&#10; &#123;&#10; private CancellationTokenRegistration cancellationTokenRegistration&#59;&#10;&#10; public void RegisterForCancellation&#40;Action&#60;object&#62; callback, CancellationToken cancellationToken&#41;&#10; &#123;&#10; cancellationTokenRegistration &#61; cancellationToken.Register&#40;callback, this&#41;&#59;&#10; &#125;&#10;&#10; public void CompleteFromTaskAsynchronously&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; System.Threading.Tasks.Task.Factory.StartNew&#40;CompleteFromTaskSynchronouslyStub, task, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default&#41;&#59;&#10; &#125;&#10;&#10; private void CompleteFromTaskSynchronouslyStub&#40;object task&#41;&#10; &#123;&#10; CompleteFromTaskSynchronously&#40;&#40;Task&#60;T&#62;&#41;task&#41;&#59;&#10; &#125;&#10;&#10; public void CompleteFromTaskSynchronously&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; if &#40;task.Status &#61;&#61; TaskStatus.RanToCompletion&#41;&#10; &#123;&#10; if &#40;TrySetResult&#40;task.Result&#41;&#41;&#10; &#123;&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; else if &#40;task.Status &#61;&#61; TaskStatus.Faulted&#41;&#10; &#123;&#10; if &#40;TrySetException&#40;task.Exception&#41;&#41;&#10; &#123;&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; else&#10; &#123;&#10; CancelSynchronously&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; public void CancelAsynchronously&#40;&#41;&#10; &#123;&#10; &#47;&#47; Since there could be synchronous continuations on the TaskCancellationSource, we queue this to the threadpool&#10; &#47;&#47; to avoid inline running of other operations.&#10; System.Threading.Tasks.Task.Factory.StartNew&#40;CancelSynchronously, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default&#41;&#59;&#10; &#125;&#10;&#10; private void CancelSynchronously&#40;&#41;&#10; &#123;&#10; if &#40;TrySetCanceled&#40;&#41;&#41;&#10; &#123;&#10; &#47;&#47; Paranoia&#58; the only reason we should ever get here is if the CancellationToken that&#10; &#47;&#47; we registered against was cancelled, but just in case, dispose the registration&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; A lightweight mutual exclusion object which supports waiting with cancellation and prevents&#10; &#47;&#47;&#47; recursion &#40;i.e. you may not call Wait if you already hold the lock&#41;&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; The &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62; provides a lightweight mutual exclusion class that doesn&#39;t&#10; &#47;&#47;&#47; use Windows kernel synchronization primitives.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; The implementation is distilled from the workings of &#60;see cref&#61;&#34;T&#58;System.Threading.SemaphoreSlim&#34;&#47;&#62;&#10; &#47;&#47;&#47; The basic idea is that we use a regular sync object &#40;Monitor.Enter&#47;Exit&#41; to guard the setting&#10; &#47;&#47;&#47; of an &#39;owning thread&#39; field. If, during the Wait, we find the lock is held by someone else&#10; &#47;&#47;&#47; then we register a cancellation callback and enter a &#34;Monitor.Wait&#34; loop. If the cancellation&#10; &#47;&#47;&#47; callback fires, then it &#34;pulses&#34; all the waiters to wake them up and check for cancellation.&#10; &#47;&#47;&#47; Waiters are also &#34;pulsed&#34; when leaving the lock.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; All public members of &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62; are thread-safe and may be used concurrently&#10; &#47;&#47;&#47; from multiple threads.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; internal sealed class NonReentrantLock&#10; &#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; A synchronization object to protect access to the &#60;see cref&#61;&#34;F&#58;owningThread&#34;&#47;&#62; field and to be pulsed&#10; &#47;&#47;&#47; when &#60;see cref&#61;&#34;M&#58;Release&#34;&#47;&#62; is called and during cancellation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private readonly object syncLock&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Indicates which thread currently holds the lock. If null, then the lock is available.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private volatile Thread owningThread&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Constructor.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;useThisInstanceForSynchronization&#34;&#62;If false &#40;the default&#41;, then the class&#10; &#47;&#47;&#47; allocates an internal object to be used as a sync lock.&#10; &#47;&#47;&#47; If true, then the sync lock object will be the NonReentrantLock instance itself. This&#10; &#47;&#47;&#47; saves an allocation but a client may not safely further use this instance in a call to&#10; &#47;&#47;&#47; Monitor.Enter&#47;Exit or in a &#34;lock&#34; statement.&#10; &#47;&#47;&#47; &#60;&#47;param&#62;&#10; public NonReentrantLock&#40;bool useThisInstanceForSynchronization &#61; false&#41;&#10; &#123;&#10; this.syncLock &#61; useThisInstanceForSynchronization &#63; this &#58; new object&#40;&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Shared factory for use in lazy initialization.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public static readonly Func&#60;NonReentrantLock&#62; Factory &#61; &#40;&#41; &#61;&#62; new NonReentrantLock&#40;useThisInstanceForSynchronization&#58; true&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Blocks the current thread until it can enter the &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62;, while observing a&#10; &#47;&#47;&#47; &#60;see cref&#61;&#34;T&#58;System.Threading.CancellationToken&#34;&#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; Recursive locking is not supported. i.e. A thread may not call Wait successfully twice without an&#10; &#47;&#47;&#47; intervening &#60;see cref&#61;&#34;Release&#34;&#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;cancellationToken&#34;&#62;The &#60;see cref&#61;&#34;T&#58;System.Threading.CancellationToken&#34;&#47;&#62; token to&#10; &#47;&#47;&#47; observe.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;System.OperationCanceledException&#34;&#62;&#60;paramref name&#61;&#34;cancellationToken&#34;&#47;&#62; was&#10; &#47;&#47;&#47; canceled.&#60;&#47;exception&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;System.LockRecursionException&#34;&#62;The caller already holds the lock&#60;&#47;exception&#62;&#10; public void Wait&#40;CancellationToken cancellationToken &#61; default&#40;CancellationToken&#41;&#41;&#10; &#123;&#10; if &#40;this.IsOwnedByMe&#41;&#10; &#123;&#10; throw new LockRecursionException&#40;&#41;&#59;&#10; &#125;&#10;&#10; CancellationTokenRegistration cancellationTokenRegistration &#61; default&#40;CancellationTokenRegistration&#41;&#59;&#10;&#10; if &#40;cancellationToken.CanBeCanceled&#41;&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; Fast path to try and avoid allocations in callback registration.&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; if &#40;&#33;this.IsLocked&#41;&#10; &#123;&#10; this.TakeOwnership&#40;&#41;&#59;&#10; return&#59;&#10; &#125;&#10; &#125;&#10;&#10; cancellationTokenRegistration &#61; cancellationToken.Register&#40;cancellationTokenCanceledEventHandler, this.syncLock, useSynchronizationContext&#58; false&#41;&#59;&#10; &#125;&#10;&#10; using &#40;cancellationTokenRegistration&#41;&#10; &#123;&#10; &#47;&#47; PERF&#58; First spin wait for the lock to become available, but only up to the first planned yield.&#10; &#47;&#47; This additional amount of spinwaiting was inherited from SemaphoreSlim&#39;s implementation where&#10; &#47;&#47; it showed measurable perf gains in test scenarios.&#10; SpinWait spin &#61; new SpinWait&#40;&#41;&#59;&#10; while &#40;this.IsLocked &#38;&#38; &#33;spin.NextSpinWillYield&#41;&#10; &#123;&#10; spin.SpinOnce&#40;&#41;&#59;&#10; &#125;&#10;&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; while &#40;this.IsLocked&#41;&#10; &#123;&#10; &#47;&#47; If cancelled, we throw. Trying to wait could lead to deadlock.&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; using &#40;Logger.LogBlock&#40;FeatureId.WaitIndicator, FunctionId.Utilities_NonReentrantLock_BlockingWait, cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; Another thread holds the lock. Wait until we get awoken either&#10; &#47;&#47; by some code calling &#34;Release&#34; or by cancellation.&#10; Monitor.Wait&#40;this.syncLock&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; We now hold the lock&#10; this.TakeOwnership&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Exit the mutual exclusion.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; The calling thread must currently hold the lock.&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;Roslyn.Utilities.ContractFailureException&#34;&#62;The lock is not currently held by the calling thread.&#60;&#47;exception&#62;&#10; public void Release&#40;&#41;&#10; &#123;&#10; AssertHasLock&#40;&#41;&#59;&#10;&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; this.ReleaseOwnership&#40;&#41;&#59;&#10;&#10; &#47;&#47; Release one waiter&#10; Monitor.Pulse&#40;this.syncLock&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Determine if the lock is currently held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;returns&#62;True if the lock is currently held by the calling thread.&#60;&#47;returns&#62;&#10; public bool LockHeldByMe&#40;&#41;&#10; &#123;&#10; return this.IsOwnedByMe&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Throw an exception if the lock is not held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;Roslyn.Utilities.ContractFailureException&#34;&#62;The lock is not currently held by the calling thread.&#60;&#47;exception&#62;&#10; public void AssertHasLock&#40;&#41;&#10; &#123;&#10; Contract.ThrowIfFalse&#40;LockHeldByMe&#40;&#41;&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Checks if the lock is currently held.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool IsLocked&#10; &#123;&#10; get&#10; &#123;&#10; return this.owningThread &#33;&#61; null&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Checks if the lock is currently held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool IsOwnedByMe&#10; &#123;&#10; get&#10; &#123;&#10; return this.owningThread &#61;&#61; Thread.CurrentThread&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Take ownership of the lock &#40;by the calling thread&#41;. The lock may not already&#10; &#47;&#47;&#47; be held by any other code.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private void TakeOwnership&#40;&#41;&#10; &#123;&#10; Contract.Assert&#40;&#33;this.IsLocked&#41;&#59;&#10; this.owningThread &#61; Thread.CurrentThread&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Release ownership of the lock. The lock must already be held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private void ReleaseOwnership&#40;&#41;&#10; &#123;&#10; Contract.Assert&#40;this.IsOwnedByMe&#41;&#59;&#10; this.owningThread &#61; null&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Action object passed to a cancellation token registration.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private static readonly Action&#60;object&#62; cancellationTokenCanceledEventHandler &#61; new Action&#60;object&#62;&#40;CancellationTokenCanceledEventHandler&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Callback executed when a cancellation token is canceled during a Wait.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;obj&#34;&#62;The syncLock that protects a &#60;see cref&#61;&#34;NonReentrantLock &#34;&#47;&#62; instance.&#60;&#47;param&#62;&#10; private static void CancellationTokenCanceledEventHandler&#40;object obj&#41;&#10; &#123;&#10; lock &#40;obj&#41;&#10; &#123;&#10; &#47;&#47; Release all waiters to check their cancellation tokens.&#10; Monitor.PulseAll&#40;obj&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#125;</pre>2013-05-23T06:05:29-07:00WGhttp://rise4fun.com/Seal/WGSeal/WG<pre>&#13;&#10;aa</pre>2013-05-23T06:05:29-07:00wDhttp://rise4fun.com/Seal/wDSeal/wD<pre>i love travelling and lninetisg to the musici love travelling and lninetisg to the musici love travelling and lninetisg to the musicpandora jewelry pandora sale uk thomas sabo barcklets shop buy cheap online oakley sunglasses charmspandora jewelry pandora sale uk thomas sabo barcklets shop buy cheap online oakley sunglasses charmsthanks for sharing such informative and fantastic post..</pre>2013-05-23T06:05:29-07:00P0http://rise4fun.com/Seal/P0Seal/P0<pre>using System&#59;&#13;&#10;class Test &#123;vbbgn h,v&#13;&#10; public Test f &#61; null&#59;&#13;&#10; public void PureMethod&#40;Test param&#41;&#123;&#13;&#10; var temp &#61; new Test&#40;&#41;&#59;&#13;&#10; temp.f &#61; param&#59; &#13;&#10; &#125; &#13;&#10;&#125;&#13;&#10;</pre>2013-05-23T06:05:29-07:00OVhttp://rise4fun.com/Seal/OVSeal/OV<pre>using System&#59;&#10;using System.Collections.Generic&#59;&#10;using System.Runtime.CompilerServices&#59;&#10;using System.Threading&#59;&#10;using System.Threading.Tasks&#59;&#10;&#10;namespace Roslyn.Utilities&#10;&#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Represents a value that can be retrieved synchronously or asynchronously by many clients.&#10; &#47;&#47;&#47; The value will be computed on-demand the moment the first client asks for it. While being&#10; &#47;&#47;&#47; computed, more clients can request the value. As long as there are outstanding clients the&#10; &#47;&#47;&#47; underlying computation will proceed. If all outstanding clients cancel their request then&#10; &#47;&#47;&#47; the underlying value computation will be cancelled as well.&#10; &#47;&#47;&#47; &#10; &#47;&#47;&#47; Creators of an &#60;see cref&#61;&#34;AsyncLazy&#123;T&#125;&#34; &#47;&#62; can specify whether the result of the computation is&#10; &#47;&#47;&#47; cached for future requests or not. Choosing to not cache means the computation functions are kept&#10; &#47;&#47;&#47; alive, whereas caching means the value &#40;but not functions&#41; are kept alive once complete.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; internal sealed class AsyncLazy&#60;T&#62;&#10; &#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The underlying function that starts an asynchronous computation of the resulting value.&#10; &#47;&#47;&#47; Null&#39;ed out once we&#39;ve computed the result and we&#39;ve been asked to cache it. Otherwise,&#10; &#47;&#47;&#47; it is kept around in case the value needs to be computed again.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The underlying function that starts an synchronous computation of the resulting value.&#10; &#47;&#47;&#47; Null&#39;ed out once we&#39;ve computed the result and we&#39;ve been asked to cache it, or if we&#10; &#47;&#47;&#47; didn&#39;t get any synchronous function given to us in the first place.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Func&#60;CancellationToken, T&#62; synchronousComputeFunction&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Whether or not we should keep the value around once we&#39;ve computed it.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private readonly bool cacheResult&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The Task that holds the cached result.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Task&#60;T&#62; cachedResult&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Mutex used to protect reading and writing to all mutable objects and fields. Traces&#10; &#47;&#47;&#47; indicate that there&#39;s negligible contention on this lock, hence we can save some memory&#10; &#47;&#47;&#47; by using a single lock for all AsyncLazy instances. Only trivial and non-reentrant work&#10; &#47;&#47;&#47; should be done while holding the lock.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private static readonly NonReentrantLock gate &#61; new NonReentrantLock&#40;useThisInstanceForSynchronization&#58; true&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The hash set of all currently outstanding asynchronous requests. Null if there are no requests,&#10; &#47;&#47;&#47; and will never be empty.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private HashSet&#60;Request&#62; requests &#61; null&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; If an asynchronous request is active, the CancellationTokenSource that allows for&#10; &#47;&#47;&#47; cancelling the underlying computation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private CancellationTokenSource asynchronousComputationCancellationSource &#61; null&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Whether a computation is active or queued on any thread, whether synchronous or&#10; &#47;&#47;&#47; asynchronous.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool computationActive &#61; false&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Creates an AsyncLazy that always returns the value, analogous to &#60;see cref&#61;&#34;Task.FromResult&#123;T&#125;&#34; &#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public AsyncLazy&#40;T value&#41;&#10; &#123;&#10; this.cacheResult &#61; true&#59;&#10; this.cachedResult &#61; new Task&#60;T&#62;&#40;&#40;&#41; &#61;&#62; &#123;return value&#59;&#125;&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Important&#58; callers of this constructor should ensure that the compute function returns&#10; &#47;&#47;&#47; a task in a non-blocking fashion. i.e. the function should &#42;not&#42; synchronously compute&#10; &#47;&#47;&#47; a value and then return it using Task.FromResult. Instead, it should return an actual&#10; &#47;&#47;&#47; task that operates asynchronously. If this function synchronously computes a value&#10; &#47;&#47;&#47; then that will cause locks to be held in this type for excessive periods of time.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public AsyncLazy&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, bool cacheResult&#41;&#10; &#58; this&#40;asynchronousComputeFunction, synchronousComputeFunction&#58; null, cacheResult&#58; cacheResult&#41;&#10; &#123;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Creates an AsyncLazy that supports both asynchronous computation and inline synchronous&#10; &#47;&#47;&#47; computation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;asynchronousComputeFunction&#34;&#62;A function called to start the asynchronous&#10; &#47;&#47;&#47; computation. This function should be cheap and non-blocking.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;synchronousComputeFunction&#34;&#62;A function to do the work synchronously, which&#10; &#47;&#47;&#47; is allowed to block. This function should not be implemented by a simple Wait on the&#10; &#47;&#47;&#47; asynchronous value. If that&#39;s all you are doing, just don&#39;t pass a synchronous function&#10; &#47;&#47;&#47; in the first place.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;cacheResult&#34;&#62;Whether the result should be cached once the computation is&#10; &#47;&#47;&#47; complete.&#60;&#47;param&#62;&#10; public AsyncLazy&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, Func&#60;CancellationToken, T&#62; synchronousComputeFunction, bool cacheResult&#41;&#10; &#123;&#10; this.asynchronousComputeFunction &#61; asynchronousComputeFunction&#59;&#10; this.synchronousComputeFunction &#61; synchronousComputeFunction&#59;&#10; this.cacheResult &#61; cacheResult&#59;&#10; &#125;&#10;&#10; &#35;region Lock Wrapper for Invariant Checking&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Takes the lock for this object and if acquired validates the invariants of this class.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private WaitThatValidatesInvariants TakeLock&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; gate.Wait&#40;cancellationToken&#41;&#59;&#10; AssertInvariants_NoLock&#40;&#41;&#59;&#10; return new WaitThatValidatesInvariants&#40;this&#41;&#59;&#10; &#125;&#10;&#10; private struct WaitThatValidatesInvariants &#58; IDisposable&#10; &#123;&#10; private readonly AsyncLazy&#60;T&#62; asyncLazy&#59;&#10;&#10; public WaitThatValidatesInvariants&#40;AsyncLazy&#60;T&#62; asyncLazy&#41;&#10; &#123;&#10; this.asyncLazy &#61; asyncLazy&#59;&#10; &#125;&#10;&#10; public void Dispose&#40;&#41;&#10; &#123;&#10; asyncLazy.AssertInvariants_NoLock&#40;&#41;&#59;&#10; gate.Release&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void AssertInvariants_NoLock&#40;&#41;&#10; &#123;&#10;&#10; &#125;&#10;&#10; &#35;endregion&#10;&#10; public bool TryGetValue&#40;out T result&#41;&#10; &#123;&#10; &#47;&#47; No need to lock here since this is only a fast check to &#10; &#47;&#47; see if the result is already computed.&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; result &#61; cachedResult.Result&#59;&#10; return true&#59;&#10; &#125;&#10;&#10; result &#61; default&#40;T&#41;&#59;&#10; return false&#59;&#10; &#125;&#10;&#10; public T GetValue&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; Request request &#61; null&#59;&#10; AsynchronousComputationToStart&#63; newAsynchronousComputation &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If cached, get immediately&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return cachedResult.Result&#59;&#10; &#125;&#10;&#10; &#47;&#47; If there is an existing computation active, we&#39;ll just create another request&#10; if &#40;computationActive&#41;&#10; &#123;&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10; &#125;&#10; else if &#40;synchronousComputeFunction &#61;&#61; null&#41;&#10; &#123;&#10; &#47;&#47; A synchronous request, but we have no synchronous function. Start off the async work&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10;&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; else&#10; &#123;&#10; &#47;&#47; We will do the computation here&#10; this.computationActive &#61; true&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; If we simply created a new asynchronous request, so wait for it. Yes, we&#39;re blocking the thread&#10; &#47;&#47; but we don&#39;t want multiple threads attempting to compute the same thing.&#10; if &#40;request &#33;&#61; null&#41;&#10; &#123;&#10; request.RegisterForCancellation&#40;OnAsynchronousRequestCancelled, cancellationToken&#41;&#59;&#10;&#10; &#47;&#47; Since we already registered for cancellation, it&#39;s possible that the registration has&#10; &#47;&#47; cancelled this new computation if we were the only requestor.&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; request&#41;&#59;&#10; &#125;&#10;&#10; return WaitAndGetResult&#40;request.Task, cancellationToken&#41;&#59;&#10; &#125;&#10; else&#10; &#123;&#10; T result&#59;&#10;&#10; &#47;&#47; We are the active computation, so let&#39;s go ahead and compute.&#10; try&#10; &#123;&#10; result &#61; synchronousComputeFunction&#40;cancellationToken&#41;&#59;&#10; &#125;&#10; catch &#40;OperationCanceledException&#41;&#10; &#123;&#10; &#47;&#47; This cancelled for some reason. We don&#39;t care why, but&#10; &#47;&#47; it means anybody else waiting for this result isn&#39;t going to get it&#10; &#47;&#47; from us.&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; this.computationActive &#61; false&#59;&#10;&#10; if &#40;requests &#33;&#61; null&#41;&#10; &#123;&#10; &#47;&#47; There&#39;s a possible improvement here&#58; there might be another synchronous caller who&#10; &#47;&#47; also wants the value. We might consider stealing their thread rather than punting&#10; &#47;&#47; to the thread pool.&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; null&#41;&#59;&#10; &#125;&#10;&#10; throw&#59;&#10; &#125;&#10; catch &#40;Exception ex&#41;&#10; &#123;&#10; &#47;&#47; We faulted for some unknown reason. We should simply fault everything.&#10; TaskCompletionSource&#60;T&#62; tcs &#61; new TaskCompletionSource&#60;T&#62;&#40;&#41;&#59;&#10; tcs.SetException&#40;ex&#41;&#59;&#10; CompleteWithTask&#40;tcs.Task, CancellationToken.None&#41;&#59;&#10;&#10; throw&#59;&#10; &#125;&#10;&#10; &#47;&#47; We have a value, so complete&#10; CompleteWithTask&#40;new Task&#60;T&#62;&#40;&#40;&#41;&#61;&#62; result&#41;, CancellationToken.None&#41;&#59;&#10;&#10; return result&#59;&#10; &#125;&#10; &#125;&#10; &#10; private T WaitAndGetResult&#40;Task&#60;T&#62; task, CancellationToken cancellationToken&#41;&#10; &#123;&#10; task.Wait&#40;cancellationToken&#41;&#59;&#10; return task.Result&#59;&#10; &#125;&#10;&#10; private Request CreateNewRequest_NoLock&#40;&#41;&#10; &#123;&#10; if &#40;this.requests &#61;&#61; null&#41;&#10; &#123;&#10; this.requests &#61; new HashSet&#60;Request&#62;&#40;&#41;&#59;&#10; &#125;&#10;&#10; Request request &#61; new Request&#40;&#41;&#59;&#10; this.requests.Add&#40;request&#41;&#59;&#10; return request&#59;&#10; &#125;&#10;&#10; public Task&#60;T&#62; GetValueAsync&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; &#47;&#47; Optimization&#58; if we&#39;re already cancelled, do not pass go&#10; if &#40;cancellationToken.IsCancellationRequested&#41;&#10; &#123;&#10; return new Task&#60;T&#62;&#40;&#40;&#41; &#61;&#62; default&#40;T&#41;, cancellationToken&#41;&#59;&#10; &#125;&#10;&#10; Request request&#59;&#10; AsynchronousComputationToStart&#63; newAsynchronousComputation &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If cached, get immediately&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return cachedResult&#59;&#10; &#125;&#10;&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10;&#10; &#47;&#47; If we have either synchronous or asynchronous work current in flight, we don&#39;t need to do anything.&#10; &#47;&#47; Otherwise, we shall start an asynchronous computation for this&#10; if &#40;&#33;computationActive&#41;&#10; &#123;&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; We now have the request counted for, register for cancellation. It is critical this is&#10; &#47;&#47; done outside the lock, as our reigstration may immediately fire and we want to avoid the&#10; &#47;&#47; reentrancy&#10; request.RegisterForCancellation&#40;OnAsynchronousRequestCancelled, cancellationToken&#41;&#59;&#10;&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; request&#41;&#59;&#10; &#125;&#10;&#10; return request.Task&#59;&#10; &#125;&#10;&#10; private AsynchronousComputationToStart RegisterAsynchronousComputation_NoLock&#40;&#41;&#10; &#123;&#10; this.asynchronousComputationCancellationSource &#61; new CancellationTokenSource&#40;&#41;&#59;&#10; this.computationActive &#61; true&#59;&#10;&#10; return new AsynchronousComputationToStart&#40;this.asynchronousComputeFunction, this.asynchronousComputationCancellationSource&#41;&#59;&#10; &#125;&#10;&#10; private struct AsynchronousComputationToStart&#10; &#123;&#10; public readonly Func&#60;CancellationToken, Task&#60;T&#62;&#62; AsynchronousComputeFunction&#59;&#10; public readonly CancellationTokenSource CancellationTokenSource&#59;&#10;&#10; public AsynchronousComputationToStart&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, CancellationTokenSource cancellationTokenSource&#41;&#10; &#123;&#10; this.AsynchronousComputeFunction &#61; asynchronousComputeFunction&#59;&#10; this.CancellationTokenSource &#61; cancellationTokenSource&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void StartAsynchronousComputation&#40;AsynchronousComputationToStart computationToStart, Request requestToCompleteSynchronously&#41;&#10; &#123;&#10; var cancellationToken &#61; computationToStart.CancellationTokenSource.Token&#59;&#10;&#10; try&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; DO NOT ACCESS ANY FIELDS OR STATE BEYOND THIS POINT. Since this function&#10; &#47;&#47; runs unsynchronized, it&#39;s possible that during this function this request&#10; &#47;&#47; might be cancelled, and then a whole additional request might start and&#10; &#47;&#47; complete inline, and cache the result. By grabbing state before we check&#10; &#47;&#47; the cancellation token, we can be assured that we are only operating on&#10; &#47;&#47; a state that was complete.&#10;&#10;&#10; &#47;&#47; We avoid creating a full closure just to pass the token along&#10; &#47;&#47; Also, use TaskContinuationOptions.ExecuteSynchronously so that we inline &#10; &#47;&#47; the continuation if asynchronousComputeFunction completes synchronously&#10; var task &#61; computationToStart.AsynchronousComputeFunction&#40;cancellationToken&#41;&#59;&#10;&#10; task.ContinueWith&#40;&#10; &#40;t&#41; &#61;&#62; CompleteWithTask&#40;t, computationToStart.CancellationTokenSource.Token&#41;,&#10; cancellationToken,&#10; TaskContinuationOptions.ExecuteSynchronously,&#10; TaskScheduler.Default&#41;&#59;&#10;&#10; if &#40;requestToCompleteSynchronously &#33;&#61; null &#38;&#38; task.IsCompleted&#41;&#10; &#123;&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; task &#61; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;task&#41;&#59;&#10; &#125;&#10;&#10; requestToCompleteSynchronously.CompleteFromTaskSynchronously&#40;task&#41;&#59;&#10; &#125;&#10;&#10; &#125;&#10; catch &#40;OperationCanceledException oce&#41;&#10; &#123;&#10; &#47;&#47; As long as it&#39;s the right token, this means that our thread was the first thread&#10; &#47;&#47; to start an asynchronous computation, but the requestor cancelled as we were starting up&#10; &#47;&#47; the computation.&#10; if &#40;oce.CancellationToken &#33;&#61; cancellationToken&#41;&#10; &#123;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; private void CompleteWithTask&#40;Task&#60;T&#62; task, CancellationToken cancellationToken&#41;&#10; &#123;&#10; IEnumerable&#60;Request&#62; requestsToComplete&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If the underlying computation was cancelled, then all state was already updated in OnAsynchronousRequestCancelled&#10; &#47;&#47; and there is no new work to do here. We &#42;must&#42; use the local one since this completion may be running far after&#10; &#47;&#47; the background computation was cancelled and a new one might have already been enqueued. We must do this&#10; &#47;&#47; check here under the lock to ensure proper synchronization with OnAsynchronousRequestCancelled.&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; The computation is complete, so get all requests to complete and null out the list. We&#39;ll create another one&#10; &#47;&#47; later if it&#39;s needed&#10; requestsToComplete &#61; this.requests &#63;&#63; new List&#60;Request&#62;&#40;&#41; as IEnumerable&#60;Request&#62;&#59;&#10; this.requests &#61; null&#59;&#10;&#10; &#47;&#47; The computations are done&#10; this.asynchronousComputationCancellationSource &#61; null&#59;&#10; this.computationActive &#61; false&#59;&#10; task &#61; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;task&#41;&#59;&#10; &#125;&#10;&#10; foreach &#40;var requestToComplete in requestsToComplete&#41;&#10; &#123;&#10; requestToComplete.CompleteFromTaskAsynchronously&#40;task&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; private Task&#60;T&#62; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; if &#40;this.cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return this.cachedResult&#59;&#10; &#125;&#10; else&#10; &#123;&#10; if &#40;cacheResult &#38;&#38; task.Status &#61;&#61; TaskStatus.RanToCompletion&#41;&#10; &#123;&#10; &#47;&#47; Hold onto the completed task. We can get rid of the computation functions for good&#10; this.cachedResult &#61; task&#59;&#10; this.asynchronousComputeFunction &#61; null&#59;&#10; this.synchronousComputeFunction &#61; null&#59;&#10; &#125;&#10;&#10; return task&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void OnAsynchronousRequestCancelled&#40;object state&#41;&#10; &#123;&#10; var request &#61; &#40;Request&#41;state&#59;&#10; CancellationTokenSource cancellationTokenSource &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; &#47;&#47; Now try to remove it. It&#39;s possible that requests may already be null. You could&#10; &#47;&#47; imagine that cancellation was requested, but before we could aquire the lock&#10; &#47;&#47; here the computation completed and the entire CompleteWithTask synchronized&#10; &#47;&#47; block ran. In that case, the requests collection may already be null, or it&#10; &#47;&#47; &#40;even scarier&#33;&#41; may have been replaced with another collection because another&#10; &#47;&#47; computation has started.&#10; if &#40;this.requests &#33;&#61; null&#41;&#10; &#123;&#10; if &#40;this.requests.Remove&#40;request&#41;&#41;&#10; &#123;&#10; if &#40;this.requests.Count &#61;&#61; 0&#41;&#10; &#123;&#10; this.requests &#61; null&#59;&#10;&#10; if &#40;this.asynchronousComputationCancellationSource &#33;&#61; null&#41;&#10; &#123;&#10; cancellationTokenSource &#61; this.asynchronousComputationCancellationSource&#59;&#10; this.asynchronousComputationCancellationSource &#61; null&#59;&#10; this.computationActive &#61; false&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; request.CancelAsynchronously&#40;&#41;&#59;&#10;&#10; if &#40;cancellationTokenSource &#33;&#61; null&#41;&#10; &#123;&#10; cancellationTokenSource.Cancel&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; Using inheritance instead of wrapping a TaskCompletionSource to avoid a second allocation&#10; private class Request &#58; TaskCompletionSource&#60;T&#62;&#10; &#123;&#10; private CancellationTokenRegistration cancellationTokenRegistration&#59;&#10;&#10; public void RegisterForCancellation&#40;Action&#60;object&#62; callback, CancellationToken cancellationToken&#41;&#10; &#123;&#10; cancellationTokenRegistration &#61; cancellationToken.Register&#40;callback, this&#41;&#59;&#10; &#125;&#10;&#10; public void CompleteFromTaskAsynchronously&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; System.Threading.Tasks.Task.Factory.StartNew&#40;CompleteFromTaskSynchronouslyStub, task, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default&#41;&#59;&#10; &#125;&#10;&#10; private void CompleteFromTaskSynchronouslyStub&#40;object task&#41;&#10; &#123;&#10; CompleteFromTaskSynchronously&#40;&#40;Task&#60;T&#62;&#41;task&#41;&#59;&#10; &#125;&#10;&#10; public void CompleteFromTaskSynchronously&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; if &#40;task.Status &#61;&#61; TaskStatus.RanToCompletion&#41;&#10; &#123;&#10; if &#40;TrySetResult&#40;task.Result&#41;&#41;&#10; &#123;&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; else if &#40;task.Status &#61;&#61; TaskStatus.Faulted&#41;&#10; &#123;&#10; if &#40;TrySetException&#40;task.Exception&#41;&#41;&#10; &#123;&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; else&#10; &#123;&#10; CancelSynchronously&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; public void CancelAsynchronously&#40;&#41;&#10; &#123;&#10; &#47;&#47; Since there could be synchronous continuations on the TaskCancellationSource, we queue this to the threadpool&#10; &#47;&#47; to avoid inline running of other operations.&#10; System.Threading.Tasks.Task.Factory.StartNew&#40;CancelSynchronously, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default&#41;&#59;&#10; &#125;&#10;&#10; private void CancelSynchronously&#40;&#41;&#10; &#123;&#10; if &#40;TrySetCanceled&#40;&#41;&#41;&#10; &#123;&#10; &#47;&#47; Paranoia&#58; the only reason we should ever get here is if the CancellationToken that&#10; &#47;&#47; we registered against was cancelled, but just in case, dispose the registration&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; A lightweight mutual exclusion object which supports waiting with cancellation and prevents&#10; &#47;&#47;&#47; recursion &#40;i.e. you may not call Wait if you already hold the lock&#41;&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; The &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62; provides a lightweight mutual exclusion class that doesn&#39;t&#10; &#47;&#47;&#47; use Windows kernel synchronization primitives.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; The implementation is distilled from the workings of &#60;see cref&#61;&#34;T&#58;System.Threading.SemaphoreSlim&#34;&#47;&#62;&#10; &#47;&#47;&#47; The basic idea is that we use a regular sync object &#40;Monitor.Enter&#47;Exit&#41; to guard the setting&#10; &#47;&#47;&#47; of an &#39;owning thread&#39; field. If, during the Wait, we find the lock is held by someone else&#10; &#47;&#47;&#47; then we register a cancellation callback and enter a &#34;Monitor.Wait&#34; loop. If the cancellation&#10; &#47;&#47;&#47; callback fires, then it &#34;pulses&#34; all the waiters to wake them up and check for cancellation.&#10; &#47;&#47;&#47; Waiters are also &#34;pulsed&#34; when leaving the lock.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; All public members of &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62; are thread-safe and may be used concurrently&#10; &#47;&#47;&#47; from multiple threads.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; internal sealed class NonReentrantLock&#10; &#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; A synchronization object to protect access to the &#60;see cref&#61;&#34;F&#58;owningThread&#34;&#47;&#62; field and to be pulsed&#10; &#47;&#47;&#47; when &#60;see cref&#61;&#34;M&#58;Release&#34;&#47;&#62; is called and during cancellation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private readonly object syncLock&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Indicates which thread currently holds the lock. If null, then the lock is available.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private volatile Thread owningThread&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Constructor.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;useThisInstanceForSynchronization&#34;&#62;If false &#40;the default&#41;, then the class&#10; &#47;&#47;&#47; allocates an internal object to be used as a sync lock.&#10; &#47;&#47;&#47; If true, then the sync lock object will be the NonReentrantLock instance itself. This&#10; &#47;&#47;&#47; saves an allocation but a client may not safely further use this instance in a call to&#10; &#47;&#47;&#47; Monitor.Enter&#47;Exit or in a &#34;lock&#34; statement.&#10; &#47;&#47;&#47; &#60;&#47;param&#62;&#10; public NonReentrantLock&#40;bool useThisInstanceForSynchronization &#61; false&#41;&#10; &#123;&#10; this.syncLock &#61; useThisInstanceForSynchronization &#63; this &#58; new object&#40;&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Shared factory for use in lazy initialization.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public static readonly Func&#60;NonReentrantLock&#62; Factory &#61; &#40;&#41; &#61;&#62; new NonReentrantLock&#40;useThisInstanceForSynchronization&#58; true&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Blocks the current thread until it can enter the &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62;, while observing a&#10; &#47;&#47;&#47; &#60;see cref&#61;&#34;T&#58;System.Threading.CancellationToken&#34;&#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; Recursive locking is not supported. i.e. A thread may not call Wait successfully twice without an&#10; &#47;&#47;&#47; intervening &#60;see cref&#61;&#34;Release&#34;&#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;cancellationToken&#34;&#62;The &#60;see cref&#61;&#34;T&#58;System.Threading.CancellationToken&#34;&#47;&#62; token to&#10; &#47;&#47;&#47; observe.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;System.OperationCanceledException&#34;&#62;&#60;paramref name&#61;&#34;cancellationToken&#34;&#47;&#62; was&#10; &#47;&#47;&#47; canceled.&#60;&#47;exception&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;System.LockRecursionException&#34;&#62;The caller already holds the lock&#60;&#47;exception&#62;&#10; public void Wait&#40;CancellationToken cancellationToken &#61; default&#40;CancellationToken&#41;&#41;&#10; &#123;&#10; if &#40;this.IsOwnedByMe&#41;&#10; &#123;&#10; throw new LockRecursionException&#40;&#41;&#59;&#10; &#125;&#10;&#10; CancellationTokenRegistration cancellationTokenRegistration &#61; default&#40;CancellationTokenRegistration&#41;&#59;&#10;&#10; if &#40;cancellationToken.CanBeCanceled&#41;&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; Fast path to try and avoid allocations in callback registration.&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; if &#40;&#33;this.IsLocked&#41;&#10; &#123;&#10; this.TakeOwnership&#40;&#41;&#59;&#10; return&#59;&#10; &#125;&#10; &#125;&#10;&#10; cancellationTokenRegistration &#61; cancellationToken.Register&#40;cancellationTokenCanceledEventHandler, this.syncLock, useSynchronizationContext&#58; false&#41;&#59;&#10; &#125;&#10;&#10; using &#40;cancellationTokenRegistration&#41;&#10; &#123;&#10; &#47;&#47; PERF&#58; First spin wait for the lock to become available, but only up to the first planned yield.&#10; &#47;&#47; This additional amount of spinwaiting was inherited from SemaphoreSlim&#39;s implementation where&#10; &#47;&#47; it showed measurable perf gains in test scenarios.&#10; SpinWait spin &#61; new SpinWait&#40;&#41;&#59;&#10; while &#40;this.IsLocked &#38;&#38; &#33;spin.NextSpinWillYield&#41;&#10; &#123;&#10; spin.SpinOnce&#40;&#41;&#59;&#10; &#125;&#10;&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; while &#40;this.IsLocked&#41;&#10; &#123;&#10; &#47;&#47; If cancelled, we throw. Trying to wait could lead to deadlock.&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; using &#40;Logger.LogBlock&#40;FeatureId.WaitIndicator, FunctionId.Utilities_NonReentrantLock_BlockingWait, cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; Another thread holds the lock. Wait until we get awoken either&#10; &#47;&#47; by some code calling &#34;Release&#34; or by cancellation.&#10; Monitor.Wait&#40;this.syncLock&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; We now hold the lock&#10; this.TakeOwnership&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Exit the mutual exclusion.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; The calling thread must currently hold the lock.&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;Roslyn.Utilities.ContractFailureException&#34;&#62;The lock is not currently held by the calling thread.&#60;&#47;exception&#62;&#10; public void Release&#40;&#41;&#10; &#123;&#10; AssertHasLock&#40;&#41;&#59;&#10;&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; this.ReleaseOwnership&#40;&#41;&#59;&#10;&#10; &#47;&#47; Release one waiter&#10; Monitor.Pulse&#40;this.syncLock&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Determine if the lock is currently held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;returns&#62;True if the lock is currently held by the calling thread.&#60;&#47;returns&#62;&#10; public bool LockHeldByMe&#40;&#41;&#10; &#123;&#10; return this.IsOwnedByMe&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Throw an exception if the lock is not held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;Roslyn.Utilities.ContractFailureException&#34;&#62;The lock is not currently held by the calling thread.&#60;&#47;exception&#62;&#10; public void AssertHasLock&#40;&#41;&#10; &#123;&#10;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Checks if the lock is currently held.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool IsLocked&#10; &#123;&#10; get&#10; &#123;&#10; return this.owningThread &#33;&#61; null&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Checks if the lock is currently held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool IsOwnedByMe&#10; &#123;&#10; get&#10; &#123;&#10; return this.owningThread &#61;&#61; Thread.CurrentThread&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Take ownership of the lock &#40;by the calling thread&#41;. The lock may not already&#10; &#47;&#47;&#47; be held by any other code.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private void TakeOwnership&#40;&#41;&#10;&#123;&#10; this.owningThread &#61; Thread.CurrentThread&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Release ownership of the lock. The lock must already be held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private void ReleaseOwnership&#40;&#41;&#10; &#123;&#10;&#10; this.owningThread &#61; null&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Action object passed to a cancellation token registration.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private static readonly Action&#60;object&#62; cancellationTokenCanceledEventHandler &#61; new Action&#60;object&#62;&#40;CancellationTokenCanceledEventHandler&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Callback executed when a cancellation token is canceled during a Wait.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;obj&#34;&#62;The syncLock that protects a &#60;see cref&#61;&#34;NonReentrantLock &#34;&#47;&#62; instance.&#60;&#47;param&#62;&#10; private static void CancellationTokenCanceledEventHandler&#40;object obj&#41;&#10; &#123;&#10; lock &#40;obj&#41;&#10; &#123;&#10; &#47;&#47; Release all waiters to check their cancellation tokens.&#10; Monitor.PulseAll&#40;obj&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#125;</pre>2013-05-23T06:05:29-07:00oPhttp://rise4fun.com/Seal/oPSeal/oP<pre>using System&#59;&#10;class Test &#123;&#10; public Test f &#61; null&#59;&#10; public void PureMethod&#40;Test param&#41;&#123;&#10; &#47;&#47;var temp &#61; new Test&#40;&#41;&#59;&#10; param.f &#61; 1&#59; &#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00aKhttp://rise4fun.com/Seal/aKSeal/aK<pre>using System&#59;&#10;using System.Collections.Generic&#59;&#10;using System.Linq&#59;&#10;using System.Text&#59;&#10;&#10;namespace TestMscorlibCalls&#10;&#123;&#10; public class TestMscorlibCalls&#10; &#123;&#10; String f &#61; &#34;Hello&#34;&#59; &#10; public void foo&#40;List&#60;String&#62; paramList&#41;&#10; &#123;&#10; List&#60;TestMscorlibCalls&#62; l &#61; new List&#60;TestMscorlibCalls&#62;&#40;&#41;&#59;&#10; l.Add&#40;this&#41;&#59;&#10; foreach &#40;var elem in l&#41;&#10; &#123;&#10; elem.f &#61; &#34;Hi&#34;&#59; &#10; &#125;&#10; &#125;&#10;&#10; public void bar&#40;List&#60;String&#62; paramSet&#41;&#10; &#123;&#10; Dictionary&#60;TestMscorlibCalls, String&#62; d &#61; new Dictionary&#60;TestMscorlibCalls, string&#62;&#40;&#41;&#59;&#10; d.Add&#40;this, this.f&#41;&#59;&#10; foreach &#40;var key in d.Keys&#41;&#10; &#123;&#10; key.f &#61; &#34;Hi&#34;&#59; &#10; &#125;&#10; &#125;&#10;&#10; &#125;&#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00jhttp://rise4fun.com/Seal/jSeal/j<pre>using System&#59;&#10;class Test &#123;&#10; public Test f &#61; null&#59;&#10; public void PureMethod&#40;Test param&#10;&#41;&#123;&#10; var temp &#61; new Test&#40;&#41;&#59;&#10; temp.f &#61; param&#59; &#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00dkhttp://rise4fun.com/Seal/dkSeal/dk<pre>using System&#59;&#10;using System.Collections.Generic&#59;&#10;using System.Linq&#59;&#10;&#10;public class LinqTest&#10;&#123;&#10; int f&#59;&#10; public void foo&#40;LinqTest param&#41;&#123;&#10; var col &#61; from i in IntegerGenerator&#40;new LinqTest&#40;&#41;&#41;&#10; select i&#59;&#10; foreach &#40;var y in col&#41;&#10; &#123;&#10; y.f &#61; 1&#59;&#10; &#125;&#10; &#125;&#10;&#10; private IEnumerable&#60;LinqTest&#62; IntegerGenerator&#40;LinqTest r&#41; &#123;&#10; yield return r&#59;&#10; &#125;&#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00dHhttp://rise4fun.com/Seal/dHSeal/dH<pre>using System&#59;&#10;using System.Collections.Generic&#59;&#10;using System.Runtime.CompilerServices&#59;&#10;using System.Threading&#59;&#10;using System.Threading.Tasks&#59;&#10;&#10;namespace Roslyn.Utilities&#10;&#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Represents a value that can be retrieved synchronously or asynchronously by many clients.&#10; &#47;&#47;&#47; The value will be computed on-demand the moment the first client asks for it. While being&#10; &#47;&#47;&#47; computed, more clients can request the value. As long as there are outstanding clients the&#10; &#47;&#47;&#47; underlying computation will proceed. If all outstanding clients cancel their request then&#10; &#47;&#47;&#47; the underlying value computation will be cancelled as well.&#10; &#47;&#47;&#47; &#10; &#47;&#47;&#47; Creators of an &#60;see cref&#61;&#34;AsyncLazy&#123;T&#125;&#34; &#47;&#62; can specify whether the result of the computation is&#10; &#47;&#47;&#47; cached for future requests or not. Choosing to not cache means the computation functions are kept&#10; &#47;&#47;&#47; alive, whereas caching means the value &#40;but not functions&#41; are kept alive once complete.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; internal sealed class AsyncLazy&#60;T&#62;&#10; &#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The underlying function that starts an asynchronous computation of the resulting value.&#10; &#47;&#47;&#47; Null&#39;ed out once we&#39;ve computed the result and we&#39;ve been asked to cache it. Otherwise,&#10; &#47;&#47;&#47; it is kept around in case the value needs to be computed again.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The underlying function that starts an synchronous computation of the resulting value.&#10; &#47;&#47;&#47; Null&#39;ed out once we&#39;ve computed the result and we&#39;ve been asked to cache it, or if we&#10; &#47;&#47;&#47; didn&#39;t get any synchronous function given to us in the first place.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Func&#60;CancellationToken, T&#62; synchronousComputeFunction&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Whether or not we should keep the value around once we&#39;ve computed it.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private readonly bool cacheResult&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The Task that holds the cached result.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Task&#60;T&#62; cachedResult&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Mutex used to protect reading and writing to all mutable objects and fields. Traces&#10; &#47;&#47;&#47; indicate that there&#39;s negligible contention on this lock, hence we can save some memory&#10; &#47;&#47;&#47; by using a single lock for all AsyncLazy instances. Only trivial and non-reentrant work&#10; &#47;&#47;&#47; should be done while holding the lock.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private static readonly NonReentrantLock gate &#61; new NonReentrantLock&#40;useThisInstanceForSynchronization&#58; true&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The hash set of all currently outstanding asynchronous requests. Null if there are no requests,&#10; &#47;&#47;&#47; and will never be empty.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private HashSet&#60;Request&#62; requests &#61; null&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; If an asynchronous request is active, the CancellationTokenSource that allows for&#10; &#47;&#47;&#47; cancelling the underlying computation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private CancellationTokenSource asynchronousComputationCancellationSource &#61; null&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Whether a computation is active or queued on any thread, whether synchronous or&#10; &#47;&#47;&#47; asynchronous.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool computationActive &#61; false&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Creates an AsyncLazy that always returns the value, analogous to &#60;see cref&#61;&#34;Task.FromResult&#123;T&#125;&#34; &#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public AsyncLazy&#40;T value&#41;&#10; &#123;&#10; this.cacheResult &#61; true&#59;&#10; this.cachedResult &#61; Task.FromResult&#40;value&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Important&#58; callers of this constructor should ensure that the compute function returns&#10; &#47;&#47;&#47; a task in a non-blocking fashion. i.e. the function should &#42;not&#42; synchronously compute&#10; &#47;&#47;&#47; a value and then return it using Task.FromResult. Instead, it should return an actual&#10; &#47;&#47;&#47; task that operates asynchronously. If this function synchronously computes a value&#10; &#47;&#47;&#47; then that will cause locks to be held in this type for excessive periods of time.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public AsyncLazy&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, bool cacheResult&#41;&#10; &#58; this&#40;asynchronousComputeFunction, synchronousComputeFunction&#58; null, cacheResult&#58; cacheResult&#41;&#10; &#123;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Creates an AsyncLazy that supports both asynchronous computation and inline synchronous&#10; &#47;&#47;&#47; computation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;asynchronousComputeFunction&#34;&#62;A function called to start the asynchronous&#10; &#47;&#47;&#47; computation. This function should be cheap and non-blocking.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;synchronousComputeFunction&#34;&#62;A function to do the work synchronously, which&#10; &#47;&#47;&#47; is allowed to block. This function should not be implemented by a simple Wait on the&#10; &#47;&#47;&#47; asynchronous value. If that&#39;s all you are doing, just don&#39;t pass a synchronous function&#10; &#47;&#47;&#47; in the first place.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;cacheResult&#34;&#62;Whether the result should be cached once the computation is&#10; &#47;&#47;&#47; complete.&#60;&#47;param&#62;&#10; public AsyncLazy&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, Func&#60;CancellationToken, T&#62; synchronousComputeFunction, bool cacheResult&#41;&#10; &#123;&#10; Contract.ThrowIfNull&#40;asynchronousComputeFunction&#41;&#59;&#10;&#10; this.asynchronousComputeFunction &#61; asynchronousComputeFunction&#59;&#10; this.synchronousComputeFunction &#61; synchronousComputeFunction&#59;&#10; this.cacheResult &#61; cacheResult&#59;&#10; &#125;&#10;&#10; &#35;region Lock Wrapper for Invariant Checking&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Takes the lock for this object and if acquired validates the invariants of this class.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private WaitThatValidatesInvariants TakeLock&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; gate.Wait&#40;cancellationToken&#41;&#59;&#10; AssertInvariants_NoLock&#40;&#41;&#59;&#10; return new WaitThatValidatesInvariants&#40;this&#41;&#59;&#10; &#125;&#10;&#10; private struct WaitThatValidatesInvariants &#58; IDisposable&#10; &#123;&#10; private readonly AsyncLazy&#60;T&#62; asyncLazy&#59;&#10;&#10; public WaitThatValidatesInvariants&#40;AsyncLazy&#60;T&#62; asyncLazy&#41;&#10; &#123;&#10; this.asyncLazy &#61; asyncLazy&#59;&#10; &#125;&#10;&#10; public void Dispose&#40;&#41;&#10; &#123;&#10; asyncLazy.AssertInvariants_NoLock&#40;&#41;&#59;&#10; gate.Release&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void AssertInvariants_NoLock&#40;&#41;&#10; &#123;&#10; &#47;&#47; Invariant &#35;1&#58; thou shalt never have an asynchronous computation running without it&#10; &#47;&#47; being considered a computation&#10; Contract.ThrowIfTrue&#40;this.asynchronousComputationCancellationSource &#33;&#61; null &#38;&#38;&#10; &#33;this.computationActive&#41;&#59;&#10;&#10; &#47;&#47; Invariant &#35;2&#58; thou shalt never waste memory holding onto empty HashSets&#10; Contract.ThrowIfTrue&#40;this.requests &#33;&#61; null &#38;&#38;&#10; this.requests.Count &#61;&#61; 0&#41;&#59;&#10;&#10; &#47;&#47; Invariant &#35;3&#58; thou shalt never have an request if there is not&#10; &#47;&#47; something trying to compute it&#10; Contract.ThrowIfTrue&#40;this.requests &#33;&#61; null &#38;&#38;&#10; &#33;this.computationActive&#41;&#59;&#10;&#10; &#47;&#47; Invariant &#35;4&#58; thou shalt never have a cached value and any computation function&#10; Contract.ThrowIfTrue&#40;this.cachedResult &#33;&#61; null &#38;&#38;&#10; &#40;this.synchronousComputeFunction &#33;&#61; null &#124;&#124; this.asynchronousComputeFunction &#33;&#61; null&#41;&#41;&#59;&#10;&#10; &#47;&#47; Invariant &#35;5&#58; thou shalt never have a synchronous computation function but not an&#10; &#47;&#47; asynchronous one&#10; Contract.ThrowIfTrue&#40;this.asynchronousComputeFunction &#61;&#61; null &#38;&#38; this.synchronousComputeFunction &#33;&#61; null&#41;&#59;&#10; &#125;&#10;&#10; &#35;endregion&#10;&#10; public bool TryGetValue&#40;out T result&#41;&#10; &#123;&#10; &#47;&#47; No need to lock here since this is only a fast check to &#10; &#47;&#47; see if the result is already computed.&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; result &#61; cachedResult.Result&#59;&#10; return true&#59;&#10; &#125;&#10;&#10; result &#61; default&#40;T&#41;&#59;&#10; return false&#59;&#10; &#125;&#10;&#10; public T GetValue&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; Request request &#61; null&#59;&#10; AsynchronousComputationToStart&#63; newAsynchronousComputation &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If cached, get immediately&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return cachedResult.Result&#59;&#10; &#125;&#10;&#10; &#47;&#47; If there is an existing computation active, we&#39;ll just create another request&#10; if &#40;computationActive&#41;&#10; &#123;&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10; &#125;&#10; else if &#40;synchronousComputeFunction &#61;&#61; null&#41;&#10; &#123;&#10; &#47;&#47; A synchronous request, but we have no synchronous function. Start off the async work&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10;&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; else&#10; &#123;&#10; &#47;&#47; We will do the computation here&#10; this.computationActive &#61; true&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; If we simply created a new asynchronous request, so wait for it. Yes, we&#39;re blocking the thread&#10; &#47;&#47; but we don&#39;t want multiple threads attempting to compute the same thing.&#10; if &#40;request &#33;&#61; null&#41;&#10; &#123;&#10; request.RegisterForCancellation&#40;OnAsynchronousRequestCancelled, cancellationToken&#41;&#59;&#10;&#10; &#47;&#47; Since we already registered for cancellation, it&#39;s possible that the registration has&#10; &#47;&#47; cancelled this new computation if we were the only requestor.&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; request&#41;&#59;&#10; &#125;&#10;&#10; return request.Task.WaitAndGetResult&#40;cancellationToken&#41;&#59;&#10; &#125;&#10; else&#10; &#123;&#10; T result&#59;&#10;&#10; &#47;&#47; We are the active computation, so let&#39;s go ahead and compute.&#10; try&#10; &#123;&#10; result &#61; synchronousComputeFunction&#40;cancellationToken&#41;&#59;&#10; &#125;&#10; catch &#40;OperationCanceledException&#41;&#10; &#123;&#10; &#47;&#47; This cancelled for some reason. We don&#39;t care why, but&#10; &#47;&#47; it means anybody else waiting for this result isn&#39;t going to get it&#10; &#47;&#47; from us.&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; this.computationActive &#61; false&#59;&#10;&#10; if &#40;requests &#33;&#61; null&#41;&#10; &#123;&#10; &#47;&#47; There&#39;s a possible improvement here&#58; there might be another synchronous caller who&#10; &#47;&#47; also wants the value. We might consider stealing their thread rather than punting&#10; &#47;&#47; to the thread pool.&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; null&#41;&#59;&#10; &#125;&#10;&#10; throw&#59;&#10; &#125;&#10; catch &#40;Exception ex&#41;&#10; &#123;&#10; &#47;&#47; We faulted for some unknown reason. We should simply fault everything.&#10; TaskCompletionSource&#60;T&#62; tcs &#61; new TaskCompletionSource&#60;T&#62;&#40;&#41;&#59;&#10; tcs.SetException&#40;ex&#41;&#59;&#10; CompleteWithTask&#40;tcs.Task, CancellationToken.None&#41;&#59;&#10;&#10; throw&#59;&#10; &#125;&#10;&#10; &#47;&#47; We have a value, so complete&#10; CompleteWithTask&#40;Task.FromResult&#40;result&#41;, CancellationToken.None&#41;&#59;&#10;&#10; return result&#59;&#10; &#125;&#10; &#125;&#10;&#10; private Request CreateNewRequest_NoLock&#40;&#41;&#10; &#123;&#10; if &#40;this.requests &#61;&#61; null&#41;&#10; &#123;&#10; this.requests &#61; new HashSet&#60;Request&#62;&#40;&#41;&#59;&#10; &#125;&#10;&#10; Request request &#61; new Request&#40;&#41;&#59;&#10; this.requests.Add&#40;request&#41;&#59;&#10; return request&#59;&#10; &#125;&#10;&#10; public Task&#60;T&#62; GetValueAsync&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; &#47;&#47; Optimization&#58; if we&#39;re already cancelled, do not pass go&#10; if &#40;cancellationToken.IsCancellationRequested&#41;&#10; &#123;&#10; return new Task&#60;T&#62;&#40;&#40;&#41; &#61;&#62; default&#40;T&#41;, cancellationToken&#41;&#59;&#10; &#125;&#10;&#10; Request request&#59;&#10; AsynchronousComputationToStart&#63; newAsynchronousComputation &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If cached, get immediately&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return cachedResult&#59;&#10; &#125;&#10;&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10;&#10; &#47;&#47; If we have either synchronous or asynchronous work current in flight, we don&#39;t need to do anything.&#10; &#47;&#47; Otherwise, we shall start an asynchronous computation for this&#10; if &#40;&#33;computationActive&#41;&#10; &#123;&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; We now have the request counted for, register for cancellation. It is critical this is&#10; &#47;&#47; done outside the lock, as our reigstration may immediately fire and we want to avoid the&#10; &#47;&#47; reentrancy&#10; request.RegisterForCancellation&#40;OnAsynchronousRequestCancelled, cancellationToken&#41;&#59;&#10;&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; request&#41;&#59;&#10; &#125;&#10;&#10; return request.Task&#59;&#10; &#125;&#10;&#10; private AsynchronousComputationToStart RegisterAsynchronousComputation_NoLock&#40;&#41;&#10; &#123;&#10; Contract.ThrowIfTrue&#40;this.computationActive&#41;&#59;&#10;&#10; this.asynchronousComputationCancellationSource &#61; new CancellationTokenSource&#40;&#41;&#59;&#10; this.computationActive &#61; true&#59;&#10;&#10; return new AsynchronousComputationToStart&#40;this.asynchronousComputeFunction, this.asynchronousComputationCancellationSource&#41;&#59;&#10; &#125;&#10;&#10; private struct AsynchronousComputationToStart&#10; &#123;&#10; public readonly Func&#60;CancellationToken, Task&#60;T&#62;&#62; AsynchronousComputeFunction&#59;&#10; public readonly CancellationTokenSource CancellationTokenSource&#59;&#10;&#10; public AsynchronousComputationToStart&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, CancellationTokenSource cancellationTokenSource&#41;&#10; &#123;&#10; this.AsynchronousComputeFunction &#61; asynchronousComputeFunction&#59;&#10; this.CancellationTokenSource &#61; cancellationTokenSource&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void StartAsynchronousComputation&#40;AsynchronousComputationToStart computationToStart, Request requestToCompleteSynchronously&#41;&#10; &#123;&#10; var cancellationToken &#61; computationToStart.CancellationTokenSource.Token&#59;&#10;&#10; try&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; DO NOT ACCESS ANY FIELDS OR STATE BEYOND THIS POINT. Since this function&#10; &#47;&#47; runs unsynchronized, it&#39;s possible that during this function this request&#10; &#47;&#47; might be cancelled, and then a whole additional request might start and&#10; &#47;&#47; complete inline, and cache the result. By grabbing state before we check&#10; &#47;&#47; the cancellation token, we can be assured that we are only operating on&#10; &#47;&#47; a state that was complete.&#10;&#10; ExceptionUtilities.ExecuteWithErrorReporting&#40;&#40;&#41; &#61;&#62;&#10; &#123;&#10; &#47;&#47; We avoid creating a full closure just to pass the token along&#10; &#47;&#47; Also, use TaskContinuationOptions.ExecuteSynchronously so that we inline &#10; &#47;&#47; the continuation if asynchronousComputeFunction completes synchronously&#10; var task &#61; computationToStart.AsynchronousComputeFunction&#40;cancellationToken&#41;&#59;&#10;&#10; task.ContinueWith&#40;&#10; &#40;t, s&#41; &#61;&#62; CompleteWithTask&#40;t, &#40;&#40;CancellationTokenSource&#41;s&#41;.Token&#41;,&#10; computationToStart.CancellationTokenSource,&#10; cancellationToken,&#10; TaskContinuationOptions.ExecuteSynchronously,&#10; TaskScheduler.Default&#41;&#59;&#10;&#10; if &#40;requestToCompleteSynchronously &#33;&#61; null &#38;&#38; task.IsCompleted&#41;&#10; &#123;&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; task &#61; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;task&#41;&#59;&#10; &#125;&#10;&#10; requestToCompleteSynchronously.CompleteFromTaskSynchronously&#40;task&#41;&#59;&#10; &#125;&#10; &#125;&#41;&#59;&#10; &#125;&#10; catch &#40;OperationCanceledException oce&#41;&#10; &#123;&#10; &#47;&#47; As long as it&#39;s the right token, this means that our thread was the first thread&#10; &#47;&#47; to start an asynchronous computation, but the requestor cancelled as we were starting up&#10; &#47;&#47; the computation.&#10; if &#40;oce.CancellationToken &#33;&#61; cancellationToken&#41;&#10; &#123;&#10; ExceptionUtilities.FailFast&#40;oce&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; private void CompleteWithTask&#40;Task&#60;T&#62; task, CancellationToken cancellationToken&#41;&#10; &#123;&#10; IEnumerable&#60;Request&#62; requestsToComplete&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If the underlying computation was cancelled, then all state was already updated in OnAsynchronousRequestCancelled&#10; &#47;&#47; and there is no new work to do here. We &#42;must&#42; use the local one since this completion may be running far after&#10; &#47;&#47; the background computation was cancelled and a new one might have already been enqueued. We must do this&#10; &#47;&#47; check here under the lock to ensure proper synchronization with OnAsynchronousRequestCancelled.&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; The computation is complete, so get all requests to complete and null out the list. We&#39;ll create another one&#10; &#47;&#47; later if it&#39;s needed&#10; requestsToComplete &#61; this.requests &#63;&#63; SpecializedCollections.EmptyEnumerable&#60;Request&#62;&#40;&#41;&#59;&#10; this.requests &#61; null&#59;&#10;&#10; &#47;&#47; The computations are done&#10; this.asynchronousComputationCancellationSource &#61; null&#59;&#10; this.computationActive &#61; false&#59;&#10; task &#61; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;task&#41;&#59;&#10; &#125;&#10;&#10; foreach &#40;var requestToComplete in requestsToComplete&#41;&#10; &#123;&#10; requestToComplete.CompleteFromTaskAsynchronously&#40;task&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; private Task&#60;T&#62; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; if &#40;this.cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return this.cachedResult&#59;&#10; &#125;&#10; else&#10; &#123;&#10; if &#40;cacheResult &#38;&#38; task.Status &#61;&#61; TaskStatus.RanToCompletion&#41;&#10; &#123;&#10; &#47;&#47; Hold onto the completed task. We can get rid of the computation functions for good&#10; this.cachedResult &#61; task&#59;&#10; this.asynchronousComputeFunction &#61; null&#59;&#10; this.synchronousComputeFunction &#61; null&#59;&#10; &#125;&#10;&#10; return task&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void OnAsynchronousRequestCancelled&#40;object state&#41;&#10; &#123;&#10; var request &#61; &#40;Request&#41;state&#59;&#10; CancellationTokenSource cancellationTokenSource &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; &#47;&#47; Now try to remove it. It&#39;s possible that requests may already be null. You could&#10; &#47;&#47; imagine that cancellation was requested, but before we could aquire the lock&#10; &#47;&#47; here the computation completed and the entire CompleteWithTask synchronized&#10; &#47;&#47; block ran. In that case, the requests collection may already be null, or it&#10; &#47;&#47; &#40;even scarier&#33;&#41; may have been replaced with another collection because another&#10; &#47;&#47; computation has started.&#10; if &#40;this.requests &#33;&#61; null&#41;&#10; &#123;&#10; if &#40;this.requests.Remove&#40;request&#41;&#41;&#10; &#123;&#10; if &#40;this.requests.Count &#61;&#61; 0&#41;&#10; &#123;&#10; this.requests &#61; null&#59;&#10;&#10; if &#40;this.asynchronousComputationCancellationSource &#33;&#61; null&#41;&#10; &#123;&#10; cancellationTokenSource &#61; this.asynchronousComputationCancellationSource&#59;&#10; this.asynchronousComputationCancellationSource &#61; null&#59;&#10; this.computationActive &#61; false&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; request.CancelAsynchronously&#40;&#41;&#59;&#10;&#10; if &#40;cancellationTokenSource &#33;&#61; null&#41;&#10; &#123;&#10; cancellationTokenSource.Cancel&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; Using inheritance instead of wrapping a TaskCompletionSource to avoid a second allocation&#10; private class Request &#58; TaskCompletionSource&#60;T&#62;&#10; &#123;&#10; private CancellationTokenRegistration cancellationTokenRegistration&#59;&#10;&#10; public void RegisterForCancellation&#40;Action&#60;object&#62; callback, CancellationToken cancellationToken&#41;&#10; &#123;&#10; cancellationTokenRegistration &#61; cancellationToken.Register&#40;callback, this&#41;&#59;&#10; &#125;&#10;&#10; public void CompleteFromTaskAsynchronously&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; System.Threading.Tasks.Task.Factory.StartNew&#40;CompleteFromTaskSynchronouslyStub, task, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default&#41;&#59;&#10; &#125;&#10;&#10; private void CompleteFromTaskSynchronouslyStub&#40;object task&#41;&#10; &#123;&#10; CompleteFromTaskSynchronously&#40;&#40;Task&#60;T&#62;&#41;task&#41;&#59;&#10; &#125;&#10;&#10; public void CompleteFromTaskSynchronously&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; if &#40;task.Status &#61;&#61; TaskStatus.RanToCompletion&#41;&#10; &#123;&#10; if &#40;TrySetResult&#40;task.Result&#41;&#41;&#10; &#123;&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; else if &#40;task.Status &#61;&#61; TaskStatus.Faulted&#41;&#10; &#123;&#10; if &#40;TrySetException&#40;task.Exception&#41;&#41;&#10; &#123;&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; else&#10; &#123;&#10; CancelSynchronously&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; public void CancelAsynchronously&#40;&#41;&#10; &#123;&#10; &#47;&#47; Since there could be synchronous continuations on the TaskCancellationSource, we queue this to the threadpool&#10; &#47;&#47; to avoid inline running of other operations.&#10; System.Threading.Tasks.Task.Factory.StartNew&#40;CancelSynchronously, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default&#41;&#59;&#10; &#125;&#10;&#10; private void CancelSynchronously&#40;&#41;&#10; &#123;&#10; if &#40;TrySetCanceled&#40;&#41;&#41;&#10; &#123;&#10; &#47;&#47; Paranoia&#58; the only reason we should ever get here is if the CancellationToken that&#10; &#47;&#47; we registered against was cancelled, but just in case, dispose the registration&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; A lightweight mutual exclusion object which supports waiting with cancellation and prevents&#10; &#47;&#47;&#47; recursion &#40;i.e. you may not call Wait if you already hold the lock&#41;&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; The &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62; provides a lightweight mutual exclusion class that doesn&#39;t&#10; &#47;&#47;&#47; use Windows kernel synchronization primitives.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; The implementation is distilled from the workings of &#60;see cref&#61;&#34;T&#58;System.Threading.SemaphoreSlim&#34;&#47;&#62;&#10; &#47;&#47;&#47; The basic idea is that we use a regular sync object &#40;Monitor.Enter&#47;Exit&#41; to guard the setting&#10; &#47;&#47;&#47; of an &#39;owning thread&#39; field. If, during the Wait, we find the lock is held by someone else&#10; &#47;&#47;&#47; then we register a cancellation callback and enter a &#34;Monitor.Wait&#34; loop. If the cancellation&#10; &#47;&#47;&#47; callback fires, then it &#34;pulses&#34; all the waiters to wake them up and check for cancellation.&#10; &#47;&#47;&#47; Waiters are also &#34;pulsed&#34; when leaving the lock.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; All public members of &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62; are thread-safe and may be used concurrently&#10; &#47;&#47;&#47; from multiple threads.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; internal sealed class NonReentrantLock&#10; &#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; A synchronization object to protect access to the &#60;see cref&#61;&#34;F&#58;owningThread&#34;&#47;&#62; field and to be pulsed&#10; &#47;&#47;&#47; when &#60;see cref&#61;&#34;M&#58;Release&#34;&#47;&#62; is called and during cancellation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private readonly object syncLock&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Indicates which thread currently holds the lock. If null, then the lock is available.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private volatile Thread owningThread&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Constructor.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;useThisInstanceForSynchronization&#34;&#62;If false &#40;the default&#41;, then the class&#10; &#47;&#47;&#47; allocates an internal object to be used as a sync lock.&#10; &#47;&#47;&#47; If true, then the sync lock object will be the NonReentrantLock instance itself. This&#10; &#47;&#47;&#47; saves an allocation but a client may not safely further use this instance in a call to&#10; &#47;&#47;&#47; Monitor.Enter&#47;Exit or in a &#34;lock&#34; statement.&#10; &#47;&#47;&#47; &#60;&#47;param&#62;&#10; public NonReentrantLock&#40;bool useThisInstanceForSynchronization &#61; false&#41;&#10; &#123;&#10; this.syncLock &#61; useThisInstanceForSynchronization &#63; this &#58; new object&#40;&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Shared factory for use in lazy initialization.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public static readonly Func&#60;NonReentrantLock&#62; Factory &#61; &#40;&#41; &#61;&#62; new NonReentrantLock&#40;useThisInstanceForSynchronization&#58; true&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Blocks the current thread until it can enter the &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62;, while observing a&#10; &#47;&#47;&#47; &#60;see cref&#61;&#34;T&#58;System.Threading.CancellationToken&#34;&#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; Recursive locking is not supported. i.e. A thread may not call Wait successfully twice without an&#10; &#47;&#47;&#47; intervening &#60;see cref&#61;&#34;Release&#34;&#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;cancellationToken&#34;&#62;The &#60;see cref&#61;&#34;T&#58;System.Threading.CancellationToken&#34;&#47;&#62; token to&#10; &#47;&#47;&#47; observe.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;System.OperationCanceledException&#34;&#62;&#60;paramref name&#61;&#34;cancellationToken&#34;&#47;&#62; was&#10; &#47;&#47;&#47; canceled.&#60;&#47;exception&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;System.LockRecursionException&#34;&#62;The caller already holds the lock&#60;&#47;exception&#62;&#10; public void Wait&#40;CancellationToken cancellationToken &#61; default&#40;CancellationToken&#41;&#41;&#10; &#123;&#10; if &#40;this.IsOwnedByMe&#41;&#10; &#123;&#10; throw new LockRecursionException&#40;&#41;&#59;&#10; &#125;&#10;&#10; CancellationTokenRegistration cancellationTokenRegistration &#61; default&#40;CancellationTokenRegistration&#41;&#59;&#10;&#10; if &#40;cancellationToken.CanBeCanceled&#41;&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; Fast path to try and avoid allocations in callback registration.&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; if &#40;&#33;this.IsLocked&#41;&#10; &#123;&#10; this.TakeOwnership&#40;&#41;&#59;&#10; return&#59;&#10; &#125;&#10; &#125;&#10;&#10; cancellationTokenRegistration &#61; cancellationToken.Register&#40;cancellationTokenCanceledEventHandler, this.syncLock, useSynchronizationContext&#58; false&#41;&#59;&#10; &#125;&#10;&#10; using &#40;cancellationTokenRegistration&#41;&#10; &#123;&#10; &#47;&#47; PERF&#58; First spin wait for the lock to become available, but only up to the first planned yield.&#10; &#47;&#47; This additional amount of spinwaiting was inherited from SemaphoreSlim&#39;s implementation where&#10; &#47;&#47; it showed measurable perf gains in test scenarios.&#10; SpinWait spin &#61; new SpinWait&#40;&#41;&#59;&#10; while &#40;this.IsLocked &#38;&#38; &#33;spin.NextSpinWillYield&#41;&#10; &#123;&#10; spin.SpinOnce&#40;&#41;&#59;&#10; &#125;&#10;&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; while &#40;this.IsLocked&#41;&#10; &#123;&#10; &#47;&#47; If cancelled, we throw. Trying to wait could lead to deadlock.&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; using &#40;Logger.LogBlock&#40;FeatureId.WaitIndicator, FunctionId.Utilities_NonReentrantLock_BlockingWait, cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; Another thread holds the lock. Wait until we get awoken either&#10; &#47;&#47; by some code calling &#34;Release&#34; or by cancellation.&#10; Monitor.Wait&#40;this.syncLock&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; We now hold the lock&#10; this.TakeOwnership&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Exit the mutual exclusion.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; The calling thread must currently hold the lock.&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;Roslyn.Utilities.ContractFailureException&#34;&#62;The lock is not currently held by the calling thread.&#60;&#47;exception&#62;&#10; public void Release&#40;&#41;&#10; &#123;&#10; AssertHasLock&#40;&#41;&#59;&#10;&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; this.ReleaseOwnership&#40;&#41;&#59;&#10;&#10; &#47;&#47; Release one waiter&#10; Monitor.Pulse&#40;this.syncLock&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Determine if the lock is currently held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;returns&#62;True if the lock is currently held by the calling thread.&#60;&#47;returns&#62;&#10; public bool LockHeldByMe&#40;&#41;&#10; &#123;&#10; return this.IsOwnedByMe&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Throw an exception if the lock is not held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;Roslyn.Utilities.ContractFailureException&#34;&#62;The lock is not currently held by the calling thread.&#60;&#47;exception&#62;&#10; public void AssertHasLock&#40;&#41;&#10; &#123;&#10; Contract.ThrowIfFalse&#40;LockHeldByMe&#40;&#41;&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Checks if the lock is currently held.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool IsLocked&#10; &#123;&#10; get&#10; &#123;&#10; return this.owningThread &#33;&#61; null&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Checks if the lock is currently held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool IsOwnedByMe&#10; &#123;&#10; get&#10; &#123;&#10; return this.owningThread &#61;&#61; Thread.CurrentThread&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Take ownership of the lock &#40;by the calling thread&#41;. The lock may not already&#10; &#47;&#47;&#47; be held by any other code.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private void TakeOwnership&#40;&#41;&#10; &#123;&#10; Contract.Assert&#40;&#33;this.IsLocked&#41;&#59;&#10; this.owningThread &#61; Thread.CurrentThread&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Release ownership of the lock. The lock must already be held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private void ReleaseOwnership&#40;&#41;&#10; &#123;&#10; Contract.Assert&#40;this.IsOwnedByMe&#41;&#59;&#10; this.owningThread &#61; null&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Action object passed to a cancellation token registration.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private static readonly Action&#60;object&#62; cancellationTokenCanceledEventHandler &#61; new Action&#60;object&#62;&#40;CancellationTokenCanceledEventHandler&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Callback executed when a cancellation token is canceled during a Wait.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;obj&#34;&#62;The syncLock that protects a &#60;see cref&#61;&#34;NonReentrantLock &#34;&#47;&#62; instance.&#60;&#47;param&#62;&#10; private static void CancellationTokenCanceledEventHandler&#40;object obj&#41;&#10; &#123;&#10; lock &#40;obj&#41;&#10; &#123;&#10; &#47;&#47; Release all waiters to check their cancellation tokens.&#10; Monitor.PulseAll&#40;obj&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#125;</pre>2013-05-23T06:05:29-07:009Dhttp://rise4fun.com/Seal/9DSeal/9D<pre>表示英文太菜&#65292;可否把Guy的评论也稍微翻译下&#12290;&#12290;&#12290;My version of the qiotsuen starts out by restricting the input to seven letters &#40;for Scrabble&#41; and it was assumed you&#39;d discard any word longer than the rack or shorter than the current best solution so a 32-bit word suffices with some finesses hinted below.Candidates who think a bitmask alone would suffice, don&#39;t understand the distinction between a bag and a set. &#47;&#47;set俺懂&#65292;bag是啥意思&#65311;关键字眼不理解真是害死人啊&#12290;&#12290;&#12290;</pre>2013-05-23T06:05:29-07:002Rhttp://rise4fun.com/Seal/2RSeal/2R<pre>using System&#59;&#10;class Test &#123;&#10; public Test f &#61; null&#59;&#10; public void PureMethod&#40;Test param&#41;&#123;&#10; var temp &#61; new Test&#40;&#41;&#59;&#10; Console.write&#40;&#34;Hello World&#34;&#41;&#59;&#10; temp.f &#61; param&#59; &#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:000ihttp://rise4fun.com/Seal/0iSeal/0i<pre>using System&#59;&#10;class Test &#123;&#10; public Test f &#61; null&#59;&#10; public void PureMethod&#40;Test param&#41;&#123;&#10; &#47;&#47;var temp &#61; new Test&#40;&#41;&#59;&#10; param&#59; &#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00UFhttp://rise4fun.com/Seal/UFSeal/UF<pre>using System&#59;&#10;class Test &#123;&#10; public Test f &#61; 1&#59;&#10; public Test q &#61; null&#59;&#10; public void PureMethod&#40;Test param, Test param2&#41;&#123;&#10; var temp &#61; new Test&#40;&#41;&#59;&#10; temp.f &#61; param&#59;&#10; temp.q &#61; param2&#59; &#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00u5http://rise4fun.com/Seal/u5Seal/u5<pre>using System&#59;&#10;class XYZ &#123;&#10;&#10; public static int add1&#40;int x, int y&#41; &#123; return x&#43;y&#59; &#125;&#10;&#10; int f1, f2&#59;&#10;&#10; public int add2&#40;&#41; &#123; return f1&#43;f2&#59; &#125;&#10;&#10; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00uhttp://rise4fun.com/Seal/uSeal/u<pre>using System&#59;&#10;class Test &#123;&#10; public Test f &#61; null&#59;&#10; public void PureMethod&#40;Test param&#41;&#123;&#10; var temp &#61; new Test&#40;&#41;&#59;&#10; temp.f &#61; param&#59; &#10; f&#61;temp&#59; &#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00Txhttp://rise4fun.com/Seal/TxSeal/Tx<pre>using System&#59;&#10;using System.Collections.Generic&#59;&#10;using System.Runtime.CompilerServices&#59;&#10;using System.Threading&#59;&#10;using System.Threading.Tasks&#59;&#10;&#10;namespace Roslyn.Utilities&#10;&#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Represents a value that can be retrieved synchronously or asynchronously by many clients.&#10; &#47;&#47;&#47; The value will be computed on-demand the moment the first client asks for it. While being&#10; &#47;&#47;&#47; computed, more clients can request the value. As long as there are outstanding clients the&#10; &#47;&#47;&#47; underlying computation will proceed. If all outstanding clients cancel their request then&#10; &#47;&#47;&#47; the underlying value computation will be cancelled as well.&#10; &#47;&#47;&#47; &#10; &#47;&#47;&#47; Creators of an &#60;see cref&#61;&#34;AsyncLazy&#123;T&#125;&#34; &#47;&#62; can specify whether the result of the computation is&#10; &#47;&#47;&#47; cached for future requests or not. Choosing to not cache means the computation functions are kept&#10; &#47;&#47;&#47; alive, whereas caching means the value &#40;but not functions&#41; are kept alive once complete.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; internal sealed class AsyncLazy&#60;T&#62;&#10; &#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The underlying function that starts an asynchronous computation of the resulting value.&#10; &#47;&#47;&#47; Null&#39;ed out once we&#39;ve computed the result and we&#39;ve been asked to cache it. Otherwise,&#10; &#47;&#47;&#47; it is kept around in case the value needs to be computed again.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The underlying function that starts an synchronous computation of the resulting value.&#10; &#47;&#47;&#47; Null&#39;ed out once we&#39;ve computed the result and we&#39;ve been asked to cache it, or if we&#10; &#47;&#47;&#47; didn&#39;t get any synchronous function given to us in the first place.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Func&#60;CancellationToken, T&#62; synchronousComputeFunction&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Whether or not we should keep the value around once we&#39;ve computed it.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private readonly bool cacheResult&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The Task that holds the cached result.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Task&#60;T&#62; cachedResult&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Mutex used to protect reading and writing to all mutable objects and fields. Traces&#10; &#47;&#47;&#47; indicate that there&#39;s negligible contention on this lock, hence we can save some memory&#10; &#47;&#47;&#47; by using a single lock for all AsyncLazy instances. Only trivial and non-reentrant work&#10; &#47;&#47;&#47; should be done while holding the lock.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private static readonly NonReentrantLock gate &#61; new NonReentrantLock&#40;useThisInstanceForSynchronization&#58; true&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The hash set of all currently outstanding asynchronous requests. Null if there are no requests,&#10; &#47;&#47;&#47; and will never be empty.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private HashSet&#60;Request&#62; requests &#61; null&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; If an asynchronous request is active, the CancellationTokenSource that allows for&#10; &#47;&#47;&#47; cancelling the underlying computation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private CancellationTokenSource asynchronousComputationCancellationSource &#61; null&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Whether a computation is active or queued on any thread, whether synchronous or&#10; &#47;&#47;&#47; asynchronous.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool computationActive &#61; false&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Creates an AsyncLazy that always returns the value, analogous to &#60;see cref&#61;&#34;Task.FromResult&#123;T&#125;&#34; &#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public AsyncLazy&#40;T value&#41;&#10; &#123;&#10; this.cacheResult &#61; true&#59;&#10; this.cachedResult &#61; new Task&#60;T&#62;&#40;&#40;&#41; &#61;&#62; &#123;return value&#59;&#125;&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Important&#58; callers of this constructor should ensure that the compute function returns&#10; &#47;&#47;&#47; a task in a non-blocking fashion. i.e. the function should &#42;not&#42; synchronously compute&#10; &#47;&#47;&#47; a value and then return it using Task.FromResult. Instead, it should return an actual&#10; &#47;&#47;&#47; task that operates asynchronously. If this function synchronously computes a value&#10; &#47;&#47;&#47; then that will cause locks to be held in this type for excessive periods of time.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public AsyncLazy&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, bool cacheResult&#41;&#10; &#58; this&#40;asynchronousComputeFunction, synchronousComputeFunction&#58; null, cacheResult&#58; cacheResult&#41;&#10; &#123;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Creates an AsyncLazy that supports both asynchronous computation and inline synchronous&#10; &#47;&#47;&#47; computation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;asynchronousComputeFunction&#34;&#62;A function called to start the asynchronous&#10; &#47;&#47;&#47; computation. This function should be cheap and non-blocking.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;synchronousComputeFunction&#34;&#62;A function to do the work synchronously, which&#10; &#47;&#47;&#47; is allowed to block. This function should not be implemented by a simple Wait on the&#10; &#47;&#47;&#47; asynchronous value. If that&#39;s all you are doing, just don&#39;t pass a synchronous function&#10; &#47;&#47;&#47; in the first place.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;cacheResult&#34;&#62;Whether the result should be cached once the computation is&#10; &#47;&#47;&#47; complete.&#60;&#47;param&#62;&#10; public AsyncLazy&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, Func&#60;CancellationToken, T&#62; synchronousComputeFunction, bool cacheResult&#41;&#10; &#123;&#10; this.asynchronousComputeFunction &#61; asynchronousComputeFunction&#59;&#10; this.synchronousComputeFunction &#61; synchronousComputeFunction&#59;&#10; this.cacheResult &#61; cacheResult&#59;&#10; &#125;&#10;&#10; &#35;region Lock Wrapper for Invariant Checking&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Takes the lock for this object and if acquired validates the invariants of this class.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private WaitThatValidatesInvariants TakeLock&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; gate.Wait&#40;cancellationToken&#41;&#59;&#10; AssertInvariants_NoLock&#40;&#41;&#59;&#10; return new WaitThatValidatesInvariants&#40;this&#41;&#59;&#10; &#125;&#10;&#10; private struct WaitThatValidatesInvariants &#58; IDisposable&#10; &#123;&#10; private readonly AsyncLazy&#60;T&#62; asyncLazy&#59;&#10;&#10; public WaitThatValidatesInvariants&#40;AsyncLazy&#60;T&#62; asyncLazy&#41;&#10; &#123;&#10; this.asyncLazy &#61; asyncLazy&#59;&#10; &#125;&#10;&#10; public void Dispose&#40;&#41;&#10; &#123;&#10; asyncLazy.AssertInvariants_NoLock&#40;&#41;&#59;&#10; gate.Release&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void AssertInvariants_NoLock&#40;&#41;&#10; &#123;&#10;&#10; &#125;&#10;&#10; &#35;endregion&#10;&#10; public bool TryGetValue&#40;out T result&#41;&#10; &#123;&#10; &#47;&#47; No need to lock here since this is only a fast check to &#10; &#47;&#47; see if the result is already computed.&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; result &#61; cachedResult.Result&#59;&#10; return true&#59;&#10; &#125;&#10;&#10; result &#61; default&#40;T&#41;&#59;&#10; return false&#59;&#10; &#125;&#10;&#10; public T GetValue&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; Request request &#61; null&#59;&#10; AsynchronousComputationToStart&#63; newAsynchronousComputation &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If cached, get immediately&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return cachedResult.Result&#59;&#10; &#125;&#10;&#10; &#47;&#47; If there is an existing computation active, we&#39;ll just create another request&#10; if &#40;computationActive&#41;&#10; &#123;&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10; &#125;&#10; else if &#40;synchronousComputeFunction &#61;&#61; null&#41;&#10; &#123;&#10; &#47;&#47; A synchronous request, but we have no synchronous function. Start off the async work&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10;&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; else&#10; &#123;&#10; &#47;&#47; We will do the computation here&#10; this.computationActive &#61; true&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; If we simply created a new asynchronous request, so wait for it. Yes, we&#39;re blocking the thread&#10; &#47;&#47; but we don&#39;t want multiple threads attempting to compute the same thing.&#10; if &#40;request &#33;&#61; null&#41;&#10; &#123;&#10; request.RegisterForCancellation&#40;OnAsynchronousRequestCancelled, cancellationToken&#41;&#59;&#10;&#10; &#47;&#47; Since we already registered for cancellation, it&#39;s possible that the registration has&#10; &#47;&#47; cancelled this new computation if we were the only requestor.&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; request&#41;&#59;&#10; &#125;&#10;&#10; return WaitAndGetResult&#40;request, cancellationToken&#41;&#59;&#10; &#125;&#10; else&#10; &#123;&#10; T result&#59;&#10;&#10; &#47;&#47; We are the active computation, so let&#39;s go ahead and compute.&#10; try&#10; &#123;&#10; result &#61; synchronousComputeFunction&#40;cancellationToken&#41;&#59;&#10; &#125;&#10; catch &#40;OperationCanceledException&#41;&#10; &#123;&#10; &#47;&#47; This cancelled for some reason. We don&#39;t care why, but&#10; &#47;&#47; it means anybody else waiting for this result isn&#39;t going to get it&#10; &#47;&#47; from us.&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; this.computationActive &#61; false&#59;&#10;&#10; if &#40;requests &#33;&#61; null&#41;&#10; &#123;&#10; &#47;&#47; There&#39;s a possible improvement here&#58; there might be another synchronous caller who&#10; &#47;&#47; also wants the value. We might consider stealing their thread rather than punting&#10; &#47;&#47; to the thread pool.&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; null&#41;&#59;&#10; &#125;&#10;&#10; throw&#59;&#10; &#125;&#10; catch &#40;Exception ex&#41;&#10; &#123;&#10; &#47;&#47; We faulted for some unknown reason. We should simply fault everything.&#10; TaskCompletionSource&#60;T&#62; tcs &#61; new TaskCompletionSource&#60;T&#62;&#40;&#41;&#59;&#10; tcs.SetException&#40;ex&#41;&#59;&#10; CompleteWithTask&#40;tcs.Task, CancellationToken.None&#41;&#59;&#10;&#10; throw&#59;&#10; &#125;&#10;&#10; &#47;&#47; We have a value, so complete&#10; CompleteWithTask&#40;Task.FromResult&#40;result&#41;, CancellationToken.None&#41;&#59;&#10;&#10; return result&#59;&#10; &#125;&#10; &#125;&#10; &#10; private T WaitAndGetResult&#40;Task&#60;T&#62; task, CancellationToken cancellationToken&#41;&#10; &#123;&#10; task.Wait&#40;cancellationToken&#41;&#59;&#10; return task.Result&#59;&#10; &#125;&#10;&#10; private Request CreateNewRequest_NoLock&#40;&#41;&#10; &#123;&#10; if &#40;this.requests &#61;&#61; null&#41;&#10; &#123;&#10; this.requests &#61; new HashSet&#60;Request&#62;&#40;&#41;&#59;&#10; &#125;&#10;&#10; Request request &#61; new Request&#40;&#41;&#59;&#10; this.requests.Add&#40;request&#41;&#59;&#10; return request&#59;&#10; &#125;&#10;&#10; public Task&#60;T&#62; GetValueAsync&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; &#47;&#47; Optimization&#58; if we&#39;re already cancelled, do not pass go&#10; if &#40;cancellationToken.IsCancellationRequested&#41;&#10; &#123;&#10; return new Task&#60;T&#62;&#40;&#40;&#41; &#61;&#62; default&#40;T&#41;, cancellationToken&#41;&#59;&#10; &#125;&#10;&#10; Request request&#59;&#10; AsynchronousComputationToStart&#63; newAsynchronousComputation &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If cached, get immediately&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return cachedResult&#59;&#10; &#125;&#10;&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10;&#10; &#47;&#47; If we have either synchronous or asynchronous work current in flight, we don&#39;t need to do anything.&#10; &#47;&#47; Otherwise, we shall start an asynchronous computation for this&#10; if &#40;&#33;computationActive&#41;&#10; &#123;&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; We now have the request counted for, register for cancellation. It is critical this is&#10; &#47;&#47; done outside the lock, as our reigstration may immediately fire and we want to avoid the&#10; &#47;&#47; reentrancy&#10; request.RegisterForCancellation&#40;OnAsynchronousRequestCancelled, cancellationToken&#41;&#59;&#10;&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; request&#41;&#59;&#10; &#125;&#10;&#10; return request.Task&#59;&#10; &#125;&#10;&#10; private AsynchronousComputationToStart RegisterAsynchronousComputation_NoLock&#40;&#41;&#10; &#123;&#10; Contract.ThrowIfTrue&#40;this.computationActive&#41;&#59;&#10;&#10; this.asynchronousComputationCancellationSource &#61; new CancellationTokenSource&#40;&#41;&#59;&#10; this.computationActive &#61; true&#59;&#10;&#10; return new AsynchronousComputationToStart&#40;this.asynchronousComputeFunction, this.asynchronousComputationCancellationSource&#41;&#59;&#10; &#125;&#10;&#10; private struct AsynchronousComputationToStart&#10; &#123;&#10; public readonly Func&#60;CancellationToken, Task&#60;T&#62;&#62; AsynchronousComputeFunction&#59;&#10; public readonly CancellationTokenSource CancellationTokenSource&#59;&#10;&#10; public AsynchronousComputationToStart&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, CancellationTokenSource cancellationTokenSource&#41;&#10; &#123;&#10; this.AsynchronousComputeFunction &#61; asynchronousComputeFunction&#59;&#10; this.CancellationTokenSource &#61; cancellationTokenSource&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void StartAsynchronousComputation&#40;AsynchronousComputationToStart computationToStart, Request requestToCompleteSynchronously&#41;&#10; &#123;&#10; var cancellationToken &#61; computationToStart.CancellationTokenSource.Token&#59;&#10;&#10; try&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; DO NOT ACCESS ANY FIELDS OR STATE BEYOND THIS POINT. Since this function&#10; &#47;&#47; runs unsynchronized, it&#39;s possible that during this function this request&#10; &#47;&#47; might be cancelled, and then a whole additional request might start and&#10; &#47;&#47; complete inline, and cache the result. By grabbing state before we check&#10; &#47;&#47; the cancellation token, we can be assured that we are only operating on&#10; &#47;&#47; a state that was complete.&#10;&#10; ExceptionUtilities.ExecuteWithErrorReporting&#40;&#40;&#41; &#61;&#62;&#10; &#123;&#10; &#47;&#47; We avoid creating a full closure just to pass the token along&#10; &#47;&#47; Also, use TaskContinuationOptions.ExecuteSynchronously so that we inline &#10; &#47;&#47; the continuation if asynchronousComputeFunction completes synchronously&#10; var task &#61; computationToStart.AsynchronousComputeFunction&#40;cancellationToken&#41;&#59;&#10;&#10; task.ContinueWith&#40;&#10; &#40;t, s&#41; &#61;&#62; CompleteWithTask&#40;t, &#40;&#40;CancellationTokenSource&#41;s&#41;.Token&#41;,&#10; computationToStart.CancellationTokenSource,&#10; cancellationToken,&#10; TaskContinuationOptions.ExecuteSynchronously,&#10; TaskScheduler.Default&#41;&#59;&#10;&#10; if &#40;requestToCompleteSynchronously &#33;&#61; null &#38;&#38; task.IsCompleted&#41;&#10; &#123;&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; task &#61; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;task&#41;&#59;&#10; &#125;&#10;&#10; requestToCompleteSynchronously.CompleteFromTaskSynchronously&#40;task&#41;&#59;&#10; &#125;&#10; &#125;&#41;&#59;&#10; &#125;&#10; catch &#40;OperationCanceledException oce&#41;&#10; &#123;&#10; &#47;&#47; As long as it&#39;s the right token, this means that our thread was the first thread&#10; &#47;&#47; to start an asynchronous computation, but the requestor cancelled as we were starting up&#10; &#47;&#47; the computation.&#10; if &#40;oce.CancellationToken &#33;&#61; cancellationToken&#41;&#10; &#123;&#10; ExceptionUtilities.FailFast&#40;oce&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; private void CompleteWithTask&#40;Task&#60;T&#62; task, CancellationToken cancellationToken&#41;&#10; &#123;&#10; IEnumerable&#60;Request&#62; requestsToComplete&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If the underlying computation was cancelled, then all state was already updated in OnAsynchronousRequestCancelled&#10; &#47;&#47; and there is no new work to do here. We &#42;must&#42; use the local one since this completion may be running far after&#10; &#47;&#47; the background computation was cancelled and a new one might have already been enqueued. We must do this&#10; &#47;&#47; check here under the lock to ensure proper synchronization with OnAsynchronousRequestCancelled.&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; The computation is complete, so get all requests to complete and null out the list. We&#39;ll create another one&#10; &#47;&#47; later if it&#39;s needed&#10; requestsToComplete &#61; this.requests &#63;&#63; SpecializedCollections.EmptyEnumerable&#60;Request&#62;&#40;&#41;&#59;&#10; this.requests &#61; null&#59;&#10;&#10; &#47;&#47; The computations are done&#10; this.asynchronousComputationCancellationSource &#61; null&#59;&#10; this.computationActive &#61; false&#59;&#10; task &#61; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;task&#41;&#59;&#10; &#125;&#10;&#10; foreach &#40;var requestToComplete in requestsToComplete&#41;&#10; &#123;&#10; requestToComplete.CompleteFromTaskAsynchronously&#40;task&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; private Task&#60;T&#62; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; if &#40;this.cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return this.cachedResult&#59;&#10; &#125;&#10; else&#10; &#123;&#10; if &#40;cacheResult &#38;&#38; task.Status &#61;&#61; TaskStatus.RanToCompletion&#41;&#10; &#123;&#10; &#47;&#47; Hold onto the completed task. We can get rid of the computation functions for good&#10; this.cachedResult &#61; task&#59;&#10; this.asynchronousComputeFunction &#61; null&#59;&#10; this.synchronousComputeFunction &#61; null&#59;&#10; &#125;&#10;&#10; return task&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void OnAsynchronousRequestCancelled&#40;object state&#41;&#10; &#123;&#10; var request &#61; &#40;Request&#41;state&#59;&#10; CancellationTokenSource cancellationTokenSource &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; &#47;&#47; Now try to remove it. It&#39;s possible that requests may already be null. You could&#10; &#47;&#47; imagine that cancellation was requested, but before we could aquire the lock&#10; &#47;&#47; here the computation completed and the entire CompleteWithTask synchronized&#10; &#47;&#47; block ran. In that case, the requests collection may already be null, or it&#10; &#47;&#47; &#40;even scarier&#33;&#41; may have been replaced with another collection because another&#10; &#47;&#47; computation has started.&#10; if &#40;this.requests &#33;&#61; null&#41;&#10; &#123;&#10; if &#40;this.requests.Remove&#40;request&#41;&#41;&#10; &#123;&#10; if &#40;this.requests.Count &#61;&#61; 0&#41;&#10; &#123;&#10; this.requests &#61; null&#59;&#10;&#10; if &#40;this.asynchronousComputationCancellationSource &#33;&#61; null&#41;&#10; &#123;&#10; cancellationTokenSource &#61; this.asynchronousComputationCancellationSource&#59;&#10; this.asynchronousComputationCancellationSource &#61; null&#59;&#10; this.computationActive &#61; false&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; request.CancelAsynchronously&#40;&#41;&#59;&#10;&#10; if &#40;cancellationTokenSource &#33;&#61; null&#41;&#10; &#123;&#10; cancellationTokenSource.Cancel&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; Using inheritance instead of wrapping a TaskCompletionSource to avoid a second allocation&#10; private class Request &#58; TaskCompletionSource&#60;T&#62;&#10; &#123;&#10; private CancellationTokenRegistration cancellationTokenRegistration&#59;&#10;&#10; public void RegisterForCancellation&#40;Action&#60;object&#62; callback, CancellationToken cancellationToken&#41;&#10; &#123;&#10; cancellationTokenRegistration &#61; cancellationToken.Register&#40;callback, this&#41;&#59;&#10; &#125;&#10;&#10; public void CompleteFromTaskAsynchronously&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; System.Threading.Tasks.Task.Factory.StartNew&#40;CompleteFromTaskSynchronouslyStub, task, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default&#41;&#59;&#10; &#125;&#10;&#10; private void CompleteFromTaskSynchronouslyStub&#40;object task&#41;&#10; &#123;&#10; CompleteFromTaskSynchronously&#40;&#40;Task&#60;T&#62;&#41;task&#41;&#59;&#10; &#125;&#10;&#10; public void CompleteFromTaskSynchronously&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; if &#40;task.Status &#61;&#61; TaskStatus.RanToCompletion&#41;&#10; &#123;&#10; if &#40;TrySetResult&#40;task.Result&#41;&#41;&#10; &#123;&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; else if &#40;task.Status &#61;&#61; TaskStatus.Faulted&#41;&#10; &#123;&#10; if &#40;TrySetException&#40;task.Exception&#41;&#41;&#10; &#123;&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; else&#10; &#123;&#10; CancelSynchronously&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; public void CancelAsynchronously&#40;&#41;&#10; &#123;&#10; &#47;&#47; Since there could be synchronous continuations on the TaskCancellationSource, we queue this to the threadpool&#10; &#47;&#47; to avoid inline running of other operations.&#10; System.Threading.Tasks.Task.Factory.StartNew&#40;CancelSynchronously, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default&#41;&#59;&#10; &#125;&#10;&#10; private void CancelSynchronously&#40;&#41;&#10; &#123;&#10; if &#40;TrySetCanceled&#40;&#41;&#41;&#10; &#123;&#10; &#47;&#47; Paranoia&#58; the only reason we should ever get here is if the CancellationToken that&#10; &#47;&#47; we registered against was cancelled, but just in case, dispose the registration&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; A lightweight mutual exclusion object which supports waiting with cancellation and prevents&#10; &#47;&#47;&#47; recursion &#40;i.e. you may not call Wait if you already hold the lock&#41;&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; The &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62; provides a lightweight mutual exclusion class that doesn&#39;t&#10; &#47;&#47;&#47; use Windows kernel synchronization primitives.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; The implementation is distilled from the workings of &#60;see cref&#61;&#34;T&#58;System.Threading.SemaphoreSlim&#34;&#47;&#62;&#10; &#47;&#47;&#47; The basic idea is that we use a regular sync object &#40;Monitor.Enter&#47;Exit&#41; to guard the setting&#10; &#47;&#47;&#47; of an &#39;owning thread&#39; field. If, during the Wait, we find the lock is held by someone else&#10; &#47;&#47;&#47; then we register a cancellation callback and enter a &#34;Monitor.Wait&#34; loop. If the cancellation&#10; &#47;&#47;&#47; callback fires, then it &#34;pulses&#34; all the waiters to wake them up and check for cancellation.&#10; &#47;&#47;&#47; Waiters are also &#34;pulsed&#34; when leaving the lock.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; All public members of &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62; are thread-safe and may be used concurrently&#10; &#47;&#47;&#47; from multiple threads.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; internal sealed class NonReentrantLock&#10; &#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; A synchronization object to protect access to the &#60;see cref&#61;&#34;F&#58;owningThread&#34;&#47;&#62; field and to be pulsed&#10; &#47;&#47;&#47; when &#60;see cref&#61;&#34;M&#58;Release&#34;&#47;&#62; is called and during cancellation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private readonly object syncLock&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Indicates which thread currently holds the lock. If null, then the lock is available.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private volatile Thread owningThread&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Constructor.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;useThisInstanceForSynchronization&#34;&#62;If false &#40;the default&#41;, then the class&#10; &#47;&#47;&#47; allocates an internal object to be used as a sync lock.&#10; &#47;&#47;&#47; If true, then the sync lock object will be the NonReentrantLock instance itself. This&#10; &#47;&#47;&#47; saves an allocation but a client may not safely further use this instance in a call to&#10; &#47;&#47;&#47; Monitor.Enter&#47;Exit or in a &#34;lock&#34; statement.&#10; &#47;&#47;&#47; &#60;&#47;param&#62;&#10; public NonReentrantLock&#40;bool useThisInstanceForSynchronization &#61; false&#41;&#10; &#123;&#10; this.syncLock &#61; useThisInstanceForSynchronization &#63; this &#58; new object&#40;&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Shared factory for use in lazy initialization.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public static readonly Func&#60;NonReentrantLock&#62; Factory &#61; &#40;&#41; &#61;&#62; new NonReentrantLock&#40;useThisInstanceForSynchronization&#58; true&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Blocks the current thread until it can enter the &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62;, while observing a&#10; &#47;&#47;&#47; &#60;see cref&#61;&#34;T&#58;System.Threading.CancellationToken&#34;&#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; Recursive locking is not supported. i.e. A thread may not call Wait successfully twice without an&#10; &#47;&#47;&#47; intervening &#60;see cref&#61;&#34;Release&#34;&#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;cancellationToken&#34;&#62;The &#60;see cref&#61;&#34;T&#58;System.Threading.CancellationToken&#34;&#47;&#62; token to&#10; &#47;&#47;&#47; observe.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;System.OperationCanceledException&#34;&#62;&#60;paramref name&#61;&#34;cancellationToken&#34;&#47;&#62; was&#10; &#47;&#47;&#47; canceled.&#60;&#47;exception&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;System.LockRecursionException&#34;&#62;The caller already holds the lock&#60;&#47;exception&#62;&#10; public void Wait&#40;CancellationToken cancellationToken &#61; default&#40;CancellationToken&#41;&#41;&#10; &#123;&#10; if &#40;this.IsOwnedByMe&#41;&#10; &#123;&#10; throw new LockRecursionException&#40;&#41;&#59;&#10; &#125;&#10;&#10; CancellationTokenRegistration cancellationTokenRegistration &#61; default&#40;CancellationTokenRegistration&#41;&#59;&#10;&#10; if &#40;cancellationToken.CanBeCanceled&#41;&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; Fast path to try and avoid allocations in callback registration.&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; if &#40;&#33;this.IsLocked&#41;&#10; &#123;&#10; this.TakeOwnership&#40;&#41;&#59;&#10; return&#59;&#10; &#125;&#10; &#125;&#10;&#10; cancellationTokenRegistration &#61; cancellationToken.Register&#40;cancellationTokenCanceledEventHandler, this.syncLock, useSynchronizationContext&#58; false&#41;&#59;&#10; &#125;&#10;&#10; using &#40;cancellationTokenRegistration&#41;&#10; &#123;&#10; &#47;&#47; PERF&#58; First spin wait for the lock to become available, but only up to the first planned yield.&#10; &#47;&#47; This additional amount of spinwaiting was inherited from SemaphoreSlim&#39;s implementation where&#10; &#47;&#47; it showed measurable perf gains in test scenarios.&#10; SpinWait spin &#61; new SpinWait&#40;&#41;&#59;&#10; while &#40;this.IsLocked &#38;&#38; &#33;spin.NextSpinWillYield&#41;&#10; &#123;&#10; spin.SpinOnce&#40;&#41;&#59;&#10; &#125;&#10;&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; while &#40;this.IsLocked&#41;&#10; &#123;&#10; &#47;&#47; If cancelled, we throw. Trying to wait could lead to deadlock.&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; using &#40;Logger.LogBlock&#40;FeatureId.WaitIndicator, FunctionId.Utilities_NonReentrantLock_BlockingWait, cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; Another thread holds the lock. Wait until we get awoken either&#10; &#47;&#47; by some code calling &#34;Release&#34; or by cancellation.&#10; Monitor.Wait&#40;this.syncLock&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; We now hold the lock&#10; this.TakeOwnership&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Exit the mutual exclusion.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; The calling thread must currently hold the lock.&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;Roslyn.Utilities.ContractFailureException&#34;&#62;The lock is not currently held by the calling thread.&#60;&#47;exception&#62;&#10; public void Release&#40;&#41;&#10; &#123;&#10; AssertHasLock&#40;&#41;&#59;&#10;&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; this.ReleaseOwnership&#40;&#41;&#59;&#10;&#10; &#47;&#47; Release one waiter&#10; Monitor.Pulse&#40;this.syncLock&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Determine if the lock is currently held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;returns&#62;True if the lock is currently held by the calling thread.&#60;&#47;returns&#62;&#10; public bool LockHeldByMe&#40;&#41;&#10; &#123;&#10; return this.IsOwnedByMe&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Throw an exception if the lock is not held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;Roslyn.Utilities.ContractFailureException&#34;&#62;The lock is not currently held by the calling thread.&#60;&#47;exception&#62;&#10; public void AssertHasLock&#40;&#41;&#10; &#123;&#10; Contract.ThrowIfFalse&#40;LockHeldByMe&#40;&#41;&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Checks if the lock is currently held.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool IsLocked&#10; &#123;&#10; get&#10; &#123;&#10; return this.owningThread &#33;&#61; null&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Checks if the lock is currently held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool IsOwnedByMe&#10; &#123;&#10; get&#10; &#123;&#10; return this.owningThread &#61;&#61; Thread.CurrentThread&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Take ownership of the lock &#40;by the calling thread&#41;. The lock may not already&#10; &#47;&#47;&#47; be held by any other code.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private void TakeOwnership&#40;&#41;&#10; &#123;&#10; Contract.Assert&#40;&#33;this.IsLocked&#41;&#59;&#10; this.owningThread &#61; Thread.CurrentThread&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Release ownership of the lock. The lock must already be held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private void ReleaseOwnership&#40;&#41;&#10; &#123;&#10; Contract.Assert&#40;this.IsOwnedByMe&#41;&#59;&#10; this.owningThread &#61; null&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Action object passed to a cancellation token registration.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private static readonly Action&#60;object&#62; cancellationTokenCanceledEventHandler &#61; new Action&#60;object&#62;&#40;CancellationTokenCanceledEventHandler&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Callback executed when a cancellation token is canceled during a Wait.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;obj&#34;&#62;The syncLock that protects a &#60;see cref&#61;&#34;NonReentrantLock &#34;&#47;&#62; instance.&#60;&#47;param&#62;&#10; private static void CancellationTokenCanceledEventHandler&#40;object obj&#41;&#10; &#123;&#10; lock &#40;obj&#41;&#10; &#123;&#10; &#47;&#47; Release all waiters to check their cancellation tokens.&#10; Monitor.PulseAll&#40;obj&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#125;</pre>2013-05-23T06:05:29-07:00NDhttp://rise4fun.com/Seal/NDSeal/ND<pre>using System&#59;&#10;class Test &#123;&#10; public Test f &#61; null&#59;&#10; public void PureMethod&#40;Test param&#41;&#123;&#10; var temp &#61; new Test&#40;&#41;&#59;&#10; Console.Write&#40;1&#41;&#59;&#10; temp.f &#61; param&#59; &#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00hEhttp://rise4fun.com/Seal/hESeal/hE<pre>using System&#59;&#10;class Test &#123;&#10; public Test f &#61; null&#59;&#10; public void PureMethod&#40;Test param&#41;&#123;&#10; var temp &#61; new Test&#40;&#41;&#59;&#10; param.f &#61; param&#59; &#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00h1http://rise4fun.com/Seal/h1Seal/h1<pre>using System&#59;&#10;using System.Collections.Generic&#59;&#10;using System.Runtime.CompilerServices&#59;&#10;using System.Threading&#59;&#10;using System.Threading.Tasks&#59;&#10;&#10;namespace Roslyn.Utilities&#10;&#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Represents a value that can be retrieved synchronously or asynchronously by many clients.&#10; &#47;&#47;&#47; The value will be computed on-demand the moment the first client asks for it. While being&#10; &#47;&#47;&#47; computed, more clients can request the value. As long as there are outstanding clients the&#10; &#47;&#47;&#47; underlying computation will proceed. If all outstanding clients cancel their request then&#10; &#47;&#47;&#47; the underlying value computation will be cancelled as well.&#10; &#47;&#47;&#47; &#10; &#47;&#47;&#47; Creators of an &#60;see cref&#61;&#34;AsyncLazy&#123;T&#125;&#34; &#47;&#62; can specify whether the result of the computation is&#10; &#47;&#47;&#47; cached for future requests or not. Choosing to not cache means the computation functions are kept&#10; &#47;&#47;&#47; alive, whereas caching means the value &#40;but not functions&#41; are kept alive once complete.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; internal sealed class AsyncLazy&#60;T&#62;&#10; &#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The underlying function that starts an asynchronous computation of the resulting value.&#10; &#47;&#47;&#47; Null&#39;ed out once we&#39;ve computed the result and we&#39;ve been asked to cache it. Otherwise,&#10; &#47;&#47;&#47; it is kept around in case the value needs to be computed again.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The underlying function that starts an synchronous computation of the resulting value.&#10; &#47;&#47;&#47; Null&#39;ed out once we&#39;ve computed the result and we&#39;ve been asked to cache it, or if we&#10; &#47;&#47;&#47; didn&#39;t get any synchronous function given to us in the first place.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Func&#60;CancellationToken, T&#62; synchronousComputeFunction&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Whether or not we should keep the value around once we&#39;ve computed it.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private readonly bool cacheResult&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The Task that holds the cached result.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Task&#60;T&#62; cachedResult&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Mutex used to protect reading and writing to all mutable objects and fields. Traces&#10; &#47;&#47;&#47; indicate that there&#39;s negligible contention on this lock, hence we can save some memory&#10; &#47;&#47;&#47; by using a single lock for all AsyncLazy instances. Only trivial and non-reentrant work&#10; &#47;&#47;&#47; should be done while holding the lock.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private static readonly NonReentrantLock gate &#61; new NonReentrantLock&#40;useThisInstanceForSynchronization&#58; true&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The hash set of all currently outstanding asynchronous requests. Null if there are no requests,&#10; &#47;&#47;&#47; and will never be empty.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private HashSet&#60;Request&#62; requests &#61; null&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; If an asynchronous request is active, the CancellationTokenSource that allows for&#10; &#47;&#47;&#47; cancelling the underlying computation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private CancellationTokenSource asynchronousComputationCancellationSource &#61; null&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Whether a computation is active or queued on any thread, whether synchronous or&#10; &#47;&#47;&#47; asynchronous.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool computationActive &#61; false&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Creates an AsyncLazy that always returns the value, analogous to &#60;see cref&#61;&#34;Task.FromResult&#123;T&#125;&#34; &#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public AsyncLazy&#40;T value&#41;&#10; &#123;&#10; this.cacheResult &#61; true&#59;&#10; this.cachedResult &#61; new Task&#60;T&#62;&#40;&#40;&#41; &#61;&#62; &#123;return value&#59;&#125;&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Important&#58; callers of this constructor should ensure that the compute function returns&#10; &#47;&#47;&#47; a task in a non-blocking fashion. i.e. the function should &#42;not&#42; synchronously compute&#10; &#47;&#47;&#47; a value and then return it using Task.FromResult. Instead, it should return an actual&#10; &#47;&#47;&#47; task that operates asynchronously. If this function synchronously computes a value&#10; &#47;&#47;&#47; then that will cause locks to be held in this type for excessive periods of time.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public AsyncLazy&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, bool cacheResult&#41;&#10; &#58; this&#40;asynchronousComputeFunction, synchronousComputeFunction&#58; null, cacheResult&#58; cacheResult&#41;&#10; &#123;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Creates an AsyncLazy that supports both asynchronous computation and inline synchronous&#10; &#47;&#47;&#47; computation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;asynchronousComputeFunction&#34;&#62;A function called to start the asynchronous&#10; &#47;&#47;&#47; computation. This function should be cheap and non-blocking.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;synchronousComputeFunction&#34;&#62;A function to do the work synchronously, which&#10; &#47;&#47;&#47; is allowed to block. This function should not be implemented by a simple Wait on the&#10; &#47;&#47;&#47; asynchronous value. If that&#39;s all you are doing, just don&#39;t pass a synchronous function&#10; &#47;&#47;&#47; in the first place.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;cacheResult&#34;&#62;Whether the result should be cached once the computation is&#10; &#47;&#47;&#47; complete.&#60;&#47;param&#62;&#10; public AsyncLazy&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, Func&#60;CancellationToken, T&#62; synchronousComputeFunction, bool cacheResult&#41;&#10; &#123;&#10; this.asynchronousComputeFunction &#61; asynchronousComputeFunction&#59;&#10; this.synchronousComputeFunction &#61; synchronousComputeFunction&#59;&#10; this.cacheResult &#61; cacheResult&#59;&#10; &#125;&#10;&#10; &#35;region Lock Wrapper for Invariant Checking&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Takes the lock for this object and if acquired validates the invariants of this class.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private WaitThatValidatesInvariants TakeLock&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; gate.Wait&#40;cancellationToken&#41;&#59;&#10; AssertInvariants_NoLock&#40;&#41;&#59;&#10; return new WaitThatValidatesInvariants&#40;this&#41;&#59;&#10; &#125;&#10;&#10; private struct WaitThatValidatesInvariants &#58; IDisposable&#10; &#123;&#10; private readonly AsyncLazy&#60;T&#62; asyncLazy&#59;&#10;&#10; public WaitThatValidatesInvariants&#40;AsyncLazy&#60;T&#62; asyncLazy&#41;&#10; &#123;&#10; this.asyncLazy &#61; asyncLazy&#59;&#10; &#125;&#10;&#10; public void Dispose&#40;&#41;&#10; &#123;&#10; asyncLazy.AssertInvariants_NoLock&#40;&#41;&#59;&#10; gate.Release&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void AssertInvariants_NoLock&#40;&#41;&#10; &#123;&#10;&#10; &#125;&#10;&#10; &#35;endregion&#10;&#10; public bool TryGetValue&#40;out T result&#41;&#10; &#123;&#10; &#47;&#47; No need to lock here since this is only a fast check to &#10; &#47;&#47; see if the result is already computed.&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; result &#61; cachedResult.Result&#59;&#10; return true&#59;&#10; &#125;&#10;&#10; result &#61; default&#40;T&#41;&#59;&#10; return false&#59;&#10; &#125;&#10;&#10; public T GetValue&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; Request request &#61; null&#59;&#10; AsynchronousComputationToStart&#63; newAsynchronousComputation &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If cached, get immediately&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return cachedResult.Result&#59;&#10; &#125;&#10;&#10; &#47;&#47; If there is an existing computation active, we&#39;ll just create another request&#10; if &#40;computationActive&#41;&#10; &#123;&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10; &#125;&#10; else if &#40;synchronousComputeFunction &#61;&#61; null&#41;&#10; &#123;&#10; &#47;&#47; A synchronous request, but we have no synchronous function. Start off the async work&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10;&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; else&#10; &#123;&#10; &#47;&#47; We will do the computation here&#10; this.computationActive &#61; true&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; If we simply created a new asynchronous request, so wait for it. Yes, we&#39;re blocking the thread&#10; &#47;&#47; but we don&#39;t want multiple threads attempting to compute the same thing.&#10; if &#40;request &#33;&#61; null&#41;&#10; &#123;&#10; request.RegisterForCancellation&#40;OnAsynchronousRequestCancelled, cancellationToken&#41;&#59;&#10;&#10; &#47;&#47; Since we already registered for cancellation, it&#39;s possible that the registration has&#10; &#47;&#47; cancelled this new computation if we were the only requestor.&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; request&#41;&#59;&#10; &#125;&#10;&#10; return WaitAndGetResult&#40;request.Task, cancellationToken&#41;&#59;&#10; &#125;&#10; else&#10; &#123;&#10; T result&#59;&#10;&#10; &#47;&#47; We are the active computation, so let&#39;s go ahead and compute.&#10; try&#10; &#123;&#10; result &#61; synchronousComputeFunction&#40;cancellationToken&#41;&#59;&#10; &#125;&#10; catch &#40;OperationCanceledException&#41;&#10; &#123;&#10; &#47;&#47; This cancelled for some reason. We don&#39;t care why, but&#10; &#47;&#47; it means anybody else waiting for this result isn&#39;t going to get it&#10; &#47;&#47; from us.&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; this.computationActive &#61; false&#59;&#10;&#10; if &#40;requests &#33;&#61; null&#41;&#10; &#123;&#10; &#47;&#47; There&#39;s a possible improvement here&#58; there might be another synchronous caller who&#10; &#47;&#47; also wants the value. We might consider stealing their thread rather than punting&#10; &#47;&#47; to the thread pool.&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; null&#41;&#59;&#10; &#125;&#10;&#10; throw&#59;&#10; &#125;&#10; catch &#40;Exception ex&#41;&#10; &#123;&#10; &#47;&#47; We faulted for some unknown reason. We should simply fault everything.&#10; TaskCompletionSource&#60;T&#62; tcs &#61; new TaskCompletionSource&#60;T&#62;&#40;&#41;&#59;&#10; tcs.SetException&#40;ex&#41;&#59;&#10; CompleteWithTask&#40;tcs.Task, CancellationToken.None&#41;&#59;&#10;&#10; throw&#59;&#10; &#125;&#10;&#10; &#47;&#47; We have a value, so complete&#10; CompleteWithTask&#40;new Task&#60;T&#62;&#40;&#40;&#41;&#61;&#62; result&#41;, CancellationToken.None&#41;&#59;&#10;&#10; return result&#59;&#10; &#125;&#10; &#125;&#10; &#10; private T WaitAndGetResult&#40;Task&#60;T&#62; task, CancellationToken cancellationToken&#41;&#10; &#123;&#10; task.Wait&#40;cancellationToken&#41;&#59;&#10; return task.Result&#59;&#10; &#125;&#10;&#10; private Request CreateNewRequest_NoLock&#40;&#41;&#10; &#123;&#10; if &#40;this.requests &#61;&#61; null&#41;&#10; &#123;&#10; this.requests &#61; new HashSet&#60;Request&#62;&#40;&#41;&#59;&#10; &#125;&#10;&#10; Request request &#61; new Request&#40;&#41;&#59;&#10; this.requests.Add&#40;request&#41;&#59;&#10; return request&#59;&#10; &#125;&#10;&#10; public Task&#60;T&#62; GetValueAsync&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; &#47;&#47; Optimization&#58; if we&#39;re already cancelled, do not pass go&#10; if &#40;cancellationToken.IsCancellationRequested&#41;&#10; &#123;&#10; return new Task&#60;T&#62;&#40;&#40;&#41; &#61;&#62; default&#40;T&#41;, cancellationToken&#41;&#59;&#10; &#125;&#10;&#10; Request request&#59;&#10; AsynchronousComputationToStart&#63; newAsynchronousComputation &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If cached, get immediately&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return cachedResult&#59;&#10; &#125;&#10;&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10;&#10; &#47;&#47; If we have either synchronous or asynchronous work current in flight, we don&#39;t need to do anything.&#10; &#47;&#47; Otherwise, we shall start an asynchronous computation for this&#10; if &#40;&#33;computationActive&#41;&#10; &#123;&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; We now have the request counted for, register for cancellation. It is critical this is&#10; &#47;&#47; done outside the lock, as our reigstration may immediately fire and we want to avoid the&#10; &#47;&#47; reentrancy&#10; request.RegisterForCancellation&#40;OnAsynchronousRequestCancelled, cancellationToken&#41;&#59;&#10;&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; request&#41;&#59;&#10; &#125;&#10;&#10; return request.Task&#59;&#10; &#125;&#10;&#10; private AsynchronousComputationToStart RegisterAsynchronousComputation_NoLock&#40;&#41;&#10; &#123;&#10; this.asynchronousComputationCancellationSource &#61; new CancellationTokenSource&#40;&#41;&#59;&#10; this.computationActive &#61; true&#59;&#10;&#10; return new AsynchronousComputationToStart&#40;this.asynchronousComputeFunction, this.asynchronousComputationCancellationSource&#41;&#59;&#10; &#125;&#10;&#10; private struct AsynchronousComputationToStart&#10; &#123;&#10; public readonly Func&#60;CancellationToken, Task&#60;T&#62;&#62; AsynchronousComputeFunction&#59;&#10; public readonly CancellationTokenSource CancellationTokenSource&#59;&#10;&#10; public AsynchronousComputationToStart&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, CancellationTokenSource cancellationTokenSource&#41;&#10; &#123;&#10; this.AsynchronousComputeFunction &#61; asynchronousComputeFunction&#59;&#10; this.CancellationTokenSource &#61; cancellationTokenSource&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void StartAsynchronousComputation&#40;AsynchronousComputationToStart computationToStart, Request requestToCompleteSynchronously&#41;&#10; &#123;&#10; var cancellationToken &#61; computationToStart.CancellationTokenSource.Token&#59;&#10;&#10; try&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; DO NOT ACCESS ANY FIELDS OR STATE BEYOND THIS POINT. Since this function&#10; &#47;&#47; runs unsynchronized, it&#39;s possible that during this function this request&#10; &#47;&#47; might be cancelled, and then a whole additional request might start and&#10; &#47;&#47; complete inline, and cache the result. By grabbing state before we check&#10; &#47;&#47; the cancellation token, we can be assured that we are only operating on&#10; &#47;&#47; a state that was complete.&#10;&#10;&#10; &#47;&#47; We avoid creating a full closure just to pass the token along&#10; &#47;&#47; Also, use TaskContinuationOptions.ExecuteSynchronously so that we inline &#10; &#47;&#47; the continuation if asynchronousComputeFunction completes synchronously&#10; var task &#61; computationToStart.AsynchronousComputeFunction&#40;cancellationToken&#41;&#59;&#10;&#10; task.ContinueWith&#40;&#10; &#40;t, s&#41; &#61;&#62; CompleteWithTask&#40;t, &#40;&#40;CancellationTokenSource&#41;s&#41;.Token&#41;,&#10; computationToStart.CancellationTokenSource,&#10; cancellationToken,&#10; TaskContinuationOptions.ExecuteSynchronously,&#10; TaskScheduler.Default&#41;&#59;&#10;&#10; if &#40;requestToCompleteSynchronously &#33;&#61; null &#38;&#38; task.IsCompleted&#41;&#10; &#123;&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; task &#61; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;task&#41;&#59;&#10; &#125;&#10;&#10; requestToCompleteSynchronously.CompleteFromTaskSynchronously&#40;task&#41;&#59;&#10; &#125;&#10;&#10; &#125;&#10; catch &#40;OperationCanceledException oce&#41;&#10; &#123;&#10; &#47;&#47; As long as it&#39;s the right token, this means that our thread was the first thread&#10; &#47;&#47; to start an asynchronous computation, but the requestor cancelled as we were starting up&#10; &#47;&#47; the computation.&#10; if &#40;oce.CancellationToken &#33;&#61; cancellationToken&#41;&#10; &#123;&#10; ExceptionUtilities.FailFast&#40;oce&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; private void CompleteWithTask&#40;Task&#60;T&#62; task, CancellationToken cancellationToken&#41;&#10; &#123;&#10; IEnumerable&#60;Request&#62; requestsToComplete&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If the underlying computation was cancelled, then all state was already updated in OnAsynchronousRequestCancelled&#10; &#47;&#47; and there is no new work to do here. We &#42;must&#42; use the local one since this completion may be running far after&#10; &#47;&#47; the background computation was cancelled and a new one might have already been enqueued. We must do this&#10; &#47;&#47; check here under the lock to ensure proper synchronization with OnAsynchronousRequestCancelled.&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; The computation is complete, so get all requests to complete and null out the list. We&#39;ll create another one&#10; &#47;&#47; later if it&#39;s needed&#10; requestsToComplete &#61; this.requests &#63;&#63; SpecializedCollections.EmptyEnumerable&#60;Request&#62;&#40;&#41;&#59;&#10; this.requests &#61; null&#59;&#10;&#10; &#47;&#47; The computations are done&#10; this.asynchronousComputationCancellationSource &#61; null&#59;&#10; this.computationActive &#61; false&#59;&#10; task &#61; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;task&#41;&#59;&#10; &#125;&#10;&#10; foreach &#40;var requestToComplete in requestsToComplete&#41;&#10; &#123;&#10; requestToComplete.CompleteFromTaskAsynchronously&#40;task&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; private Task&#60;T&#62; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; if &#40;this.cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return this.cachedResult&#59;&#10; &#125;&#10; else&#10; &#123;&#10; if &#40;cacheResult &#38;&#38; task.Status &#61;&#61; TaskStatus.RanToCompletion&#41;&#10; &#123;&#10; &#47;&#47; Hold onto the completed task. We can get rid of the computation functions for good&#10; this.cachedResult &#61; task&#59;&#10; this.asynchronousComputeFunction &#61; null&#59;&#10; this.synchronousComputeFunction &#61; null&#59;&#10; &#125;&#10;&#10; return task&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void OnAsynchronousRequestCancelled&#40;object state&#41;&#10; &#123;&#10; var request &#61; &#40;Request&#41;state&#59;&#10; CancellationTokenSource cancellationTokenSource &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; &#47;&#47; Now try to remove it. It&#39;s possible that requests may already be null. You could&#10; &#47;&#47; imagine that cancellation was requested, but before we could aquire the lock&#10; &#47;&#47; here the computation completed and the entire CompleteWithTask synchronized&#10; &#47;&#47; block ran. In that case, the requests collection may already be null, or it&#10; &#47;&#47; &#40;even scarier&#33;&#41; may have been replaced with another collection because another&#10; &#47;&#47; computation has started.&#10; if &#40;this.requests &#33;&#61; null&#41;&#10; &#123;&#10; if &#40;this.requests.Remove&#40;request&#41;&#41;&#10; &#123;&#10; if &#40;this.requests.Count &#61;&#61; 0&#41;&#10; &#123;&#10; this.requests &#61; null&#59;&#10;&#10; if &#40;this.asynchronousComputationCancellationSource &#33;&#61; null&#41;&#10; &#123;&#10; cancellationTokenSource &#61; this.asynchronousComputationCancellationSource&#59;&#10; this.asynchronousComputationCancellationSource &#61; null&#59;&#10; this.computationActive &#61; false&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; request.CancelAsynchronously&#40;&#41;&#59;&#10;&#10; if &#40;cancellationTokenSource &#33;&#61; null&#41;&#10; &#123;&#10; cancellationTokenSource.Cancel&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; Using inheritance instead of wrapping a TaskCompletionSource to avoid a second allocation&#10; private class Request &#58; TaskCompletionSource&#60;T&#62;&#10; &#123;&#10; private CancellationTokenRegistration cancellationTokenRegistration&#59;&#10;&#10; public void RegisterForCancellation&#40;Action&#60;object&#62; callback, CancellationToken cancellationToken&#41;&#10; &#123;&#10; cancellationTokenRegistration &#61; cancellationToken.Register&#40;callback, this&#41;&#59;&#10; &#125;&#10;&#10; public void CompleteFromTaskAsynchronously&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; System.Threading.Tasks.Task.Factory.StartNew&#40;CompleteFromTaskSynchronouslyStub, task, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default&#41;&#59;&#10; &#125;&#10;&#10; private void CompleteFromTaskSynchronouslyStub&#40;object task&#41;&#10; &#123;&#10; CompleteFromTaskSynchronously&#40;&#40;Task&#60;T&#62;&#41;task&#41;&#59;&#10; &#125;&#10;&#10; public void CompleteFromTaskSynchronously&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; if &#40;task.Status &#61;&#61; TaskStatus.RanToCompletion&#41;&#10; &#123;&#10; if &#40;TrySetResult&#40;task.Result&#41;&#41;&#10; &#123;&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; else if &#40;task.Status &#61;&#61; TaskStatus.Faulted&#41;&#10; &#123;&#10; if &#40;TrySetException&#40;task.Exception&#41;&#41;&#10; &#123;&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; else&#10; &#123;&#10; CancelSynchronously&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; public void CancelAsynchronously&#40;&#41;&#10; &#123;&#10; &#47;&#47; Since there could be synchronous continuations on the TaskCancellationSource, we queue this to the threadpool&#10; &#47;&#47; to avoid inline running of other operations.&#10; System.Threading.Tasks.Task.Factory.StartNew&#40;CancelSynchronously, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default&#41;&#59;&#10; &#125;&#10;&#10; private void CancelSynchronously&#40;&#41;&#10; &#123;&#10; if &#40;TrySetCanceled&#40;&#41;&#41;&#10; &#123;&#10; &#47;&#47; Paranoia&#58; the only reason we should ever get here is if the CancellationToken that&#10; &#47;&#47; we registered against was cancelled, but just in case, dispose the registration&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; A lightweight mutual exclusion object which supports waiting with cancellation and prevents&#10; &#47;&#47;&#47; recursion &#40;i.e. you may not call Wait if you already hold the lock&#41;&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; The &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62; provides a lightweight mutual exclusion class that doesn&#39;t&#10; &#47;&#47;&#47; use Windows kernel synchronization primitives.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; The implementation is distilled from the workings of &#60;see cref&#61;&#34;T&#58;System.Threading.SemaphoreSlim&#34;&#47;&#62;&#10; &#47;&#47;&#47; The basic idea is that we use a regular sync object &#40;Monitor.Enter&#47;Exit&#41; to guard the setting&#10; &#47;&#47;&#47; of an &#39;owning thread&#39; field. If, during the Wait, we find the lock is held by someone else&#10; &#47;&#47;&#47; then we register a cancellation callback and enter a &#34;Monitor.Wait&#34; loop. If the cancellation&#10; &#47;&#47;&#47; callback fires, then it &#34;pulses&#34; all the waiters to wake them up and check for cancellation.&#10; &#47;&#47;&#47; Waiters are also &#34;pulsed&#34; when leaving the lock.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; All public members of &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62; are thread-safe and may be used concurrently&#10; &#47;&#47;&#47; from multiple threads.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; internal sealed class NonReentrantLock&#10; &#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; A synchronization object to protect access to the &#60;see cref&#61;&#34;F&#58;owningThread&#34;&#47;&#62; field and to be pulsed&#10; &#47;&#47;&#47; when &#60;see cref&#61;&#34;M&#58;Release&#34;&#47;&#62; is called and during cancellation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private readonly object syncLock&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Indicates which thread currently holds the lock. If null, then the lock is available.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private volatile Thread owningThread&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Constructor.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;useThisInstanceForSynchronization&#34;&#62;If false &#40;the default&#41;, then the class&#10; &#47;&#47;&#47; allocates an internal object to be used as a sync lock.&#10; &#47;&#47;&#47; If true, then the sync lock object will be the NonReentrantLock instance itself. This&#10; &#47;&#47;&#47; saves an allocation but a client may not safely further use this instance in a call to&#10; &#47;&#47;&#47; Monitor.Enter&#47;Exit or in a &#34;lock&#34; statement.&#10; &#47;&#47;&#47; &#60;&#47;param&#62;&#10; public NonReentrantLock&#40;bool useThisInstanceForSynchronization &#61; false&#41;&#10; &#123;&#10; this.syncLock &#61; useThisInstanceForSynchronization &#63; this &#58; new object&#40;&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Shared factory for use in lazy initialization.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public static readonly Func&#60;NonReentrantLock&#62; Factory &#61; &#40;&#41; &#61;&#62; new NonReentrantLock&#40;useThisInstanceForSynchronization&#58; true&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Blocks the current thread until it can enter the &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62;, while observing a&#10; &#47;&#47;&#47; &#60;see cref&#61;&#34;T&#58;System.Threading.CancellationToken&#34;&#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; Recursive locking is not supported. i.e. A thread may not call Wait successfully twice without an&#10; &#47;&#47;&#47; intervening &#60;see cref&#61;&#34;Release&#34;&#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;cancellationToken&#34;&#62;The &#60;see cref&#61;&#34;T&#58;System.Threading.CancellationToken&#34;&#47;&#62; token to&#10; &#47;&#47;&#47; observe.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;System.OperationCanceledException&#34;&#62;&#60;paramref name&#61;&#34;cancellationToken&#34;&#47;&#62; was&#10; &#47;&#47;&#47; canceled.&#60;&#47;exception&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;System.LockRecursionException&#34;&#62;The caller already holds the lock&#60;&#47;exception&#62;&#10; public void Wait&#40;CancellationToken cancellationToken &#61; default&#40;CancellationToken&#41;&#41;&#10; &#123;&#10; if &#40;this.IsOwnedByMe&#41;&#10; &#123;&#10; throw new LockRecursionException&#40;&#41;&#59;&#10; &#125;&#10;&#10; CancellationTokenRegistration cancellationTokenRegistration &#61; default&#40;CancellationTokenRegistration&#41;&#59;&#10;&#10; if &#40;cancellationToken.CanBeCanceled&#41;&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; Fast path to try and avoid allocations in callback registration.&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; if &#40;&#33;this.IsLocked&#41;&#10; &#123;&#10; this.TakeOwnership&#40;&#41;&#59;&#10; return&#59;&#10; &#125;&#10; &#125;&#10;&#10; cancellationTokenRegistration &#61; cancellationToken.Register&#40;cancellationTokenCanceledEventHandler, this.syncLock, useSynchronizationContext&#58; false&#41;&#59;&#10; &#125;&#10;&#10; using &#40;cancellationTokenRegistration&#41;&#10; &#123;&#10; &#47;&#47; PERF&#58; First spin wait for the lock to become available, but only up to the first planned yield.&#10; &#47;&#47; This additional amount of spinwaiting was inherited from SemaphoreSlim&#39;s implementation where&#10; &#47;&#47; it showed measurable perf gains in test scenarios.&#10; SpinWait spin &#61; new SpinWait&#40;&#41;&#59;&#10; while &#40;this.IsLocked &#38;&#38; &#33;spin.NextSpinWillYield&#41;&#10; &#123;&#10; spin.SpinOnce&#40;&#41;&#59;&#10; &#125;&#10;&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; while &#40;this.IsLocked&#41;&#10; &#123;&#10; &#47;&#47; If cancelled, we throw. Trying to wait could lead to deadlock.&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; using &#40;Logger.LogBlock&#40;FeatureId.WaitIndicator, FunctionId.Utilities_NonReentrantLock_BlockingWait, cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; Another thread holds the lock. Wait until we get awoken either&#10; &#47;&#47; by some code calling &#34;Release&#34; or by cancellation.&#10; Monitor.Wait&#40;this.syncLock&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; We now hold the lock&#10; this.TakeOwnership&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Exit the mutual exclusion.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; The calling thread must currently hold the lock.&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;Roslyn.Utilities.ContractFailureException&#34;&#62;The lock is not currently held by the calling thread.&#60;&#47;exception&#62;&#10; public void Release&#40;&#41;&#10; &#123;&#10; AssertHasLock&#40;&#41;&#59;&#10;&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; this.ReleaseOwnership&#40;&#41;&#59;&#10;&#10; &#47;&#47; Release one waiter&#10; Monitor.Pulse&#40;this.syncLock&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Determine if the lock is currently held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;returns&#62;True if the lock is currently held by the calling thread.&#60;&#47;returns&#62;&#10; public bool LockHeldByMe&#40;&#41;&#10; &#123;&#10; return this.IsOwnedByMe&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Throw an exception if the lock is not held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;Roslyn.Utilities.ContractFailureException&#34;&#62;The lock is not currently held by the calling thread.&#60;&#47;exception&#62;&#10; public void AssertHasLock&#40;&#41;&#10; &#123;&#10; Contract.ThrowIfFalse&#40;LockHeldByMe&#40;&#41;&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Checks if the lock is currently held.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool IsLocked&#10; &#123;&#10; get&#10; &#123;&#10; return this.owningThread &#33;&#61; null&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Checks if the lock is currently held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool IsOwnedByMe&#10; &#123;&#10; get&#10; &#123;&#10; return this.owningThread &#61;&#61; Thread.CurrentThread&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Take ownership of the lock &#40;by the calling thread&#41;. The lock may not already&#10; &#47;&#47;&#47; be held by any other code.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private void TakeOwnership&#40;&#41;&#10; &#123;&#10; Contract.Assert&#40;&#33;this.IsLocked&#41;&#59;&#10; this.owningThread &#61; Thread.CurrentThread&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Release ownership of the lock. The lock must already be held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private void ReleaseOwnership&#40;&#41;&#10; &#123;&#10; Contract.Assert&#40;this.IsOwnedByMe&#41;&#59;&#10; this.owningThread &#61; null&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Action object passed to a cancellation token registration.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private static readonly Action&#60;object&#62; cancellationTokenCanceledEventHandler &#61; new Action&#60;object&#62;&#40;CancellationTokenCanceledEventHandler&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Callback executed when a cancellation token is canceled during a Wait.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;obj&#34;&#62;The syncLock that protects a &#60;see cref&#61;&#34;NonReentrantLock &#34;&#47;&#62; instance.&#60;&#47;param&#62;&#10; private static void CancellationTokenCanceledEventHandler&#40;object obj&#41;&#10; &#123;&#10; lock &#40;obj&#41;&#10; &#123;&#10; &#47;&#47; Release all waiters to check their cancellation tokens.&#10; Monitor.PulseAll&#40;obj&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#125;</pre>2013-05-23T06:05:29-07:00hhttp://rise4fun.com/Seal/hSeal/h<pre>using System&#59;&#10;class Test &#123;&#10; public Test f &#61; null&#59;&#10; public void PureMethod&#40;Test param&#41;&#123;&#10; var temp &#61; new Test&#40;&#41;&#59;&#10; temp.f &#61; param&#59; &#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00dhttp://rise4fun.com/Seal/dSeal/d<pre>using System&#59;&#10;class Test &#123;&#10; public Test f &#61; null&#59;&#10; public void PureMethod&#40;Test param&#41;&#123;&#10; var temp &#61; new Test&#40;&#41;&#59;&#10; Console.write&#40;1&#41;&#59;&#10; temp.f &#61; param&#59; &#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00HXhttp://rise4fun.com/Seal/HXSeal/HX<pre>using System&#59;&#13;&#10;public class TestHeapCloning&#123;&#13;&#10; TestHeapCloning f&#59;&#13;&#10; int h&#59;&#13;&#10; public void foo&#40;TestHeapCloning param&#41;&#123;&#13;&#10; TestHeapCloning local1 &#61; bar&#40;&#41;&#59;&#13;&#10; TestHeapCloning local2 &#61; bar&#40;&#41;&#59;&#13;&#10; local1.f &#61; param&#59;&#13;&#10; local2.f &#61; new TestHeapCloning&#40;&#41;&#59;&#13;&#10; local2.f.h &#61; 1&#59;&#13;&#10; &#125;&#13;&#10; public TestHeapCloning bar&#40;&#41;&#123;&#13;&#10; TestHeapCloning r &#61; new TestHeapCloning&#40;&#41;&#59;&#13;&#10; return r&#59;&#13;&#10; &#125;&#13;&#10;&#125;&#13;&#10;</pre>2013-05-23T06:05:29-07:009http://rise4fun.com/Seal/9Seal/9<pre>using System&#59;&#10;class Test &#123;&#10; public Test f &#61; null&#59;&#10; private static Test g &#61; null&#59;&#10; public void PureMethod&#40;Test param&#41;&#123;&#10; var temp &#61; new Test&#40;&#41;&#59;&#10; temp.f &#61; param&#59; &#10; Console.WriteLine&#40;&#34;foo&#34;&#41;&#59;&#10; g &#61; this&#59;&#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:006http://rise4fun.com/Seal/6Seal/6<pre>using System&#59;&#10;class Test &#123;&#10; public Test f &#61; null&#59;&#10; public void PureMethod&#40;Test param&#41;&#123;&#10;f &#61; param&#59; &#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00vhttp://rise4fun.com/Seal/vSeal/v<pre>using System.Collections.Generic&#59;&#10;using System&#59;&#10;&#10;Class S&#10;&#123;&#10; public void foo&#40;List&#60;String&#62; paramList&#41;&#10; &#123;&#10; List&#60;S&#62; l &#61; new List&#60;S&#62;&#40;&#41;&#59;&#10; &#125;&#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00ohttp://rise4fun.com/Seal/oSeal/o<pre>using System&#59;&#10;class Test &#123;&#10; public Test f &#61; null&#59;&#10; public string g &#61; &#34;&#34;&#59;&#10; public void ImpureMethod&#40;Test param&#41;&#123;&#10; var temp &#61; new Test&#40;&#41;&#59;&#10; temp.g &#61; &#34;Hello&#34;&#59; &#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00Nphttp://rise4fun.com/Seal/NpSeal/Np<pre>using System&#59;&#10;class Test &#123;&#10; public Test f &#61; null&#59;&#10; public void PureMethod&#40;Test param&#41;&#123;&#10; &#47;&#47;var temp &#61; new Test&#40;&#41;&#59;&#10; Test param &#61; new Test&#40;&#41;&#59; &#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00ibhttp://rise4fun.com/Seal/ibSeal/ib<pre>using System&#59;&#10;class Test &#123;&#10; Test f&#59;&#10; String g&#59;&#10; static Test param&#59;&#10; public void ImpureMethod&#40;&#41;&#123; &#10; var x &#61; .f&#59;&#10; x.g &#61; &#34;Hello&#34;&#59;&#10; &#125; &#10;&#125;</pre>2013-05-23T06:05:29-07:00Ihttp://rise4fun.com/Seal/ISeal/I<pre>using System&#59;&#10;class Test &#123;&#10; Test f&#59;&#10; String g&#59;&#10; public void ImpureMethod&#40;Test param&#41;&#123; &#10; var x &#61; param.f&#59;&#10; while &#40;x &#33;&#61; null&#41;&#10; &#123;&#10; x.g &#61; &#34;Hello&#34;&#59;&#10; x &#61; x.f&#59;&#10; &#125; &#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00hvhttp://rise4fun.com/Seal/hvSeal/hv<pre>using System&#59;&#10;class Test &#123;&#10; public Test f &#61; null&#59;&#10; public void PureMethod&#40;Test param&#41;&#123;&#10; var temp &#61; new Test&#40;&#41;&#59;&#10; temp.f &#61; param&#59;&#10; temp.f &#61; null&#59; &#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00Dahttp://rise4fun.com/Seal/DaSeal/Da<pre>using System&#59;&#10;class Test &#123;&#10; public Test f &#61; null&#59;&#10; int a&#59;&#10; public void PureMethod&#40;Test param&#41;&#123;&#10; var temp &#61; new Test&#40;&#41;&#59;&#10; temp.f &#61; param&#59;&#10; param.a&#43;&#43;&#59;&#10;&#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00d8http://rise4fun.com/Seal/d8Seal/d8<pre>using System&#59;&#10;class Test &#123;&#10; public Test f &#61; null&#59;&#10; public void PureMethod&#40;Test param&#41;&#123;&#10; var temp &#61; new Test&#40;&#41;&#59;&#10; temp.f &#61; param&#59; &#10; &#125;&#10; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:003http://rise4fun.com/Seal/3Seal/3<pre>Class S&#10;&#123;&#10; public void foo&#40;List&#60;String&#62; paramList&#41;&#10; &#123;&#10; List&#60;S&#62; l &#61; new List&#60;S&#62;&#40;&#41;&#59;&#10; &#125;&#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00XKhttp://rise4fun.com/Seal/XKSeal/XK<pre>&#10;using System&#59;&#10;using System.Collections.Generic&#59;&#10;using System.Runtime.CompilerServices&#59;&#10;using System.Threading&#59;&#10;using System.Threading.Tasks&#59;&#10;&#10;namespace Roslyn.Utilities&#10;&#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Represents a value that can be retrieved synchronously or asynchronously by many clients.&#10; &#47;&#47;&#47; The value will be computed on-demand the moment the first client asks for it. While being&#10; &#47;&#47;&#47; computed, more clients can request the value. As long as there are outstanding clients the&#10; &#47;&#47;&#47; underlying computation will proceed. If all outstanding clients cancel their request then&#10; &#47;&#47;&#47; the underlying value computation will be cancelled as well.&#10; &#47;&#47;&#47; &#10; &#47;&#47;&#47; Creators of an &#60;see cref&#61;&#34;AsyncLazy&#123;T&#125;&#34; &#47;&#62; can specify whether the result of the computation is&#10; &#47;&#47;&#47; cached for future requests or not. Choosing to not cache means the computation functions are kept&#10; &#47;&#47;&#47; alive, whereas caching means the value &#40;but not functions&#41; are kept alive once complete.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; internal sealed class AsyncLazy&#60;T&#62;&#10; &#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The underlying function that starts an asynchronous computation of the resulting value.&#10; &#47;&#47;&#47; Null&#39;ed out once we&#39;ve computed the result and we&#39;ve been asked to cache it. Otherwise,&#10; &#47;&#47;&#47; it is kept around in case the value needs to be computed again.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The underlying function that starts an synchronous computation of the resulting value.&#10; &#47;&#47;&#47; Null&#39;ed out once we&#39;ve computed the result and we&#39;ve been asked to cache it, or if we&#10; &#47;&#47;&#47; didn&#39;t get any synchronous function given to us in the first place.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Func&#60;CancellationToken, T&#62; synchronousComputeFunction&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Whether or not we should keep the value around once we&#39;ve computed it.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private readonly bool cacheResult&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The Task that holds the cached result.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Task&#60;T&#62; cachedResult&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Mutex used to protect reading and writing to all mutable objects and fields. Traces&#10; &#47;&#47;&#47; indicate that there&#39;s negligible contention on this lock, hence we can save some memory&#10; &#47;&#47;&#47; by using a single lock for all AsyncLazy instances. Only trivial and non-reentrant work&#10; &#47;&#47;&#47; should be done while holding the lock.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private static readonly NonReentrantLock gate &#61; new NonReentrantLock&#40;useThisInstanceForSynchronization&#58; true&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The hash set of all currently outstanding asynchronous requests. Null if there are no requests,&#10; &#47;&#47;&#47; and will never be empty.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private HashSet&#60;Request&#62; requests &#61; null&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; If an asynchronous request is active, the CancellationTokenSource that allows for&#10; &#47;&#47;&#47; cancelling the underlying computation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private CancellationTokenSource asynchronousComputationCancellationSource &#61; null&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Whether a computation is active or queued on any thread, whether synchronous or&#10; &#47;&#47;&#47; asynchronous.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool computationActive &#61; false&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Creates an AsyncLazy that always returns the value, analogous to &#60;see cref&#61;&#34;Task.FromResult&#123;T&#125;&#34; &#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public AsyncLazy&#40;T value&#41;&#10; &#123;&#10; this.cacheResult &#61; true&#59;&#10; this.cachedResult &#61; Task.FromResult&#40;value&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Important&#58; callers of this constructor should ensure that the compute function returns&#10; &#47;&#47;&#47; a task in a non-blocking fashion. i.e. the function should &#42;not&#42; synchronously compute&#10; &#47;&#47;&#47; a value and then return it using Task.FromResult. Instead, it should return an actual&#10; &#47;&#47;&#47; task that operates asynchronously. If this function synchronously computes a value&#10; &#47;&#47;&#47; then that will cause locks to be held in this type for excessive periods of time.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public AsyncLazy&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, bool cacheResult&#41;&#10; &#58; this&#40;asynchronousComputeFunction, synchronousComputeFunction&#58; null, cacheResult&#58; cacheResult&#41;&#10; &#123;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Creates an AsyncLazy that supports both asynchronous computation and inline synchronous&#10; &#47;&#47;&#47; computation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;asynchronousComputeFunction&#34;&#62;A function called to start the asynchronous&#10; &#47;&#47;&#47; computation. This function should be cheap and non-blocking.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;synchronousComputeFunction&#34;&#62;A function to do the work synchronously, which&#10; &#47;&#47;&#47; is allowed to block. This function should not be implemented by a simple Wait on the&#10; &#47;&#47;&#47; asynchronous value. If that&#39;s all you are doing, just don&#39;t pass a synchronous function&#10; &#47;&#47;&#47; in the first place.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;cacheResult&#34;&#62;Whether the result should be cached once the computation is&#10; &#47;&#47;&#47; complete.&#60;&#47;param&#62;&#10; public AsyncLazy&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, Func&#60;CancellationToken, T&#62; synchronousComputeFunction, bool cacheResult&#41;&#10; &#123;&#10; Contract.ThrowIfNull&#40;asynchronousComputeFunction&#41;&#59;&#10;&#10; this.asynchronousComputeFunction &#61; asynchronousComputeFunction&#59;&#10; this.synchronousComputeFunction &#61; synchronousComputeFunction&#59;&#10; this.cacheResult &#61; cacheResult&#59;&#10; &#125;&#10;&#10; &#35;region Lock Wrapper for Invariant Checking&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Takes the lock for this object and if acquired validates the invariants of this class.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private WaitThatValidatesInvariants TakeLock&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; gate.Wait&#40;cancellationToken&#41;&#59;&#10; AssertInvariants_NoLock&#40;&#41;&#59;&#10; return new WaitThatValidatesInvariants&#40;this&#41;&#59;&#10; &#125;&#10;&#10; private struct WaitThatValidatesInvariants &#58; IDisposable&#10; &#123;&#10; private readonly AsyncLazy&#60;T&#62; asyncLazy&#59;&#10;&#10; public WaitThatValidatesInvariants&#40;AsyncLazy&#60;T&#62; asyncLazy&#41;&#10; &#123;&#10; this.asyncLazy &#61; asyncLazy&#59;&#10; &#125;&#10;&#10; public void Dispose&#40;&#41;&#10; &#123;&#10; asyncLazy.AssertInvariants_NoLock&#40;&#41;&#59;&#10; gate.Release&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void AssertInvariants_NoLock&#40;&#41;&#10; &#123;&#10; &#47;&#47; Invariant &#35;1&#58; thou shalt never have an asynchronous computation running without it&#10; &#47;&#47; being considered a computation&#10; Contract.ThrowIfTrue&#40;this.asynchronousComputationCancellationSource &#33;&#61; null &#38;&#38;&#10; &#33;this.computationActive&#41;&#59;&#10;&#10; &#47;&#47; Invariant &#35;2&#58; thou shalt never waste memory holding onto empty HashSets&#10; Contract.ThrowIfTrue&#40;this.requests &#33;&#61; null &#38;&#38;&#10; this.requests.Count &#61;&#61; 0&#41;&#59;&#10;&#10; &#47;&#47; Invariant &#35;3&#58; thou shalt never have an request if there is not&#10; &#47;&#47; something trying to compute it&#10; Contract.ThrowIfTrue&#40;this.requests &#33;&#61; null &#38;&#38;&#10; &#33;this.computationActive&#41;&#59;&#10;&#10; &#47;&#47; Invariant &#35;4&#58; thou shalt never have a cached value and any computation function&#10; Contract.ThrowIfTrue&#40;this.cachedResult &#33;&#61; null &#38;&#38;&#10; &#40;this.synchronousComputeFunction &#33;&#61; null &#124;&#124; this.asynchronousComputeFunction &#33;&#61; null&#41;&#41;&#59;&#10;&#10; &#47;&#47; Invariant &#35;5&#58; thou shalt never have a synchronous computation function but not an&#10; &#47;&#47; asynchronous one&#10; Contract.ThrowIfTrue&#40;this.asynchronousComputeFunction &#61;&#61; null &#38;&#38; this.synchronousComputeFunction &#33;&#61; null&#41;&#59;&#10; &#125;&#10;&#10; &#35;endregion&#10;&#10; public bool TryGetValue&#40;out T result&#41;&#10; &#123;&#10; &#47;&#47; No need to lock here since this is only a fast check to &#10; &#47;&#47; see if the result is already computed.&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; result &#61; cachedResult.Result&#59;&#10; return true&#59;&#10; &#125;&#10;&#10; result &#61; default&#40;T&#41;&#59;&#10; return false&#59;&#10; &#125;&#10;&#10; public T GetValue&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; Request request &#61; null&#59;&#10; AsynchronousComputationToStart&#63; newAsynchronousComputation &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If cached, get immediately&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return cachedResult.Result&#59;&#10; &#125;&#10;&#10; &#47;&#47; If there is an existing computation active, we&#39;ll just create another request&#10; if &#40;computationActive&#41;&#10; &#123;&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10; &#125;&#10; else if &#40;synchronousComputeFunction &#61;&#61; null&#41;&#10; &#123;&#10; &#47;&#47; A synchronous request, but we have no synchronous function. Start off the async work&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10;&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; else&#10; &#123;&#10; &#47;&#47; We will do the computation here&#10; this.computationActive &#61; true&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; If we simply created a new asynchronous request, so wait for it. Yes, we&#39;re blocking the thread&#10; &#47;&#47; but we don&#39;t want multiple threads attempting to compute the same thing.&#10; if &#40;request &#33;&#61; null&#41;&#10; &#123;&#10; request.RegisterForCancellation&#40;OnAsynchronousRequestCancelled, cancellationToken&#41;&#59;&#10;&#10; &#47;&#47; Since we already registered for cancellation, it&#39;s possible that the registration has&#10; &#47;&#47; cancelled this new computation if we were the only requestor.&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; request&#41;&#59;&#10; &#125;&#10;&#10; return request.Task.WaitAndGetResult&#40;cancellationToken&#41;&#59;&#10; &#125;&#10; else&#10; &#123;&#10; T result&#59;&#10;&#10; &#47;&#47; We are the active computation, so let&#39;s go ahead and compute.&#10; try&#10; &#123;&#10; result &#61; synchronousComputeFunction&#40;cancellationToken&#41;&#59;&#10; &#125;&#10; catch &#40;OperationCanceledException&#41;&#10; &#123;&#10; &#47;&#47; This cancelled for some reason. We don&#39;t care why, but&#10; &#47;&#47; it means anybody else waiting for this result isn&#39;t going to get it&#10; &#47;&#47; from us.&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; this.computationActive &#61; false&#59;&#10;&#10; if &#40;requests &#33;&#61; null&#41;&#10; &#123;&#10; &#47;&#47; There&#39;s a possible improvement here&#58; there might be another synchronous caller who&#10; &#47;&#47; also wants the value. We might consider stealing their thread rather than punting&#10; &#47;&#47; to the thread pool.&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; null&#41;&#59;&#10; &#125;&#10;&#10; throw&#59;&#10; &#125;&#10; catch &#40;Exception ex&#41;&#10; &#123;&#10; &#47;&#47; We faulted for some unknown reason. We should simply fault everything.&#10; TaskCompletionSource&#60;T&#62; tcs &#61; new TaskCompletionSource&#60;T&#62;&#40;&#41;&#59;&#10; tcs.SetException&#40;ex&#41;&#59;&#10; CompleteWithTask&#40;tcs.Task, CancellationToken.None&#41;&#59;&#10;&#10; throw&#59;&#10; &#125;&#10;&#10; &#47;&#47; We have a value, so complete&#10; CompleteWithTask&#40;Task.FromResult&#40;result&#41;, CancellationToken.None&#41;&#59;&#10;&#10; return result&#59;&#10; &#125;&#10; &#125;&#10;&#10; private Request CreateNewRequest_NoLock&#40;&#41;&#10; &#123;&#10; if &#40;this.requests &#61;&#61; null&#41;&#10; &#123;&#10; this.requests &#61; new HashSet&#60;Request&#62;&#40;&#41;&#59;&#10; &#125;&#10;&#10; Request request &#61; new Request&#40;&#41;&#59;&#10; this.requests.Add&#40;request&#41;&#59;&#10; return request&#59;&#10; &#125;&#10;&#10; public Task&#60;T&#62; GetValueAsync&#40;CancellationToken cancellationToken&#41;&#10; &#123;&#10; &#47;&#47; Optimization&#58; if we&#39;re already cancelled, do not pass go&#10; if &#40;cancellationToken.IsCancellationRequested&#41;&#10; &#123;&#10; return new Task&#60;T&#62;&#40;&#40;&#41; &#61;&#62; default&#40;T&#41;, cancellationToken&#41;&#59;&#10; &#125;&#10;&#10; Request request&#59;&#10; AsynchronousComputationToStart&#63; newAsynchronousComputation &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If cached, get immediately&#10; if &#40;cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return cachedResult&#59;&#10; &#125;&#10;&#10; request &#61; CreateNewRequest_NoLock&#40;&#41;&#59;&#10;&#10; &#47;&#47; If we have either synchronous or asynchronous work current in flight, we don&#39;t need to do anything.&#10; &#47;&#47; Otherwise, we shall start an asynchronous computation for this&#10; if &#40;&#33;computationActive&#41;&#10; &#123;&#10; newAsynchronousComputation &#61; RegisterAsynchronousComputation_NoLock&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; We now have the request counted for, register for cancellation. It is critical this is&#10; &#47;&#47; done outside the lock, as our reigstration may immediately fire and we want to avoid the&#10; &#47;&#47; reentrancy&#10; request.RegisterForCancellation&#40;OnAsynchronousRequestCancelled, cancellationToken&#41;&#59;&#10;&#10; if &#40;newAsynchronousComputation &#33;&#61; null&#41;&#10; &#123;&#10; StartAsynchronousComputation&#40;newAsynchronousComputation.Value, requestToCompleteSynchronously&#58; request&#41;&#59;&#10; &#125;&#10;&#10; return request.Task&#59;&#10; &#125;&#10;&#10; private AsynchronousComputationToStart RegisterAsynchronousComputation_NoLock&#40;&#41;&#10; &#123;&#10; Contract.ThrowIfTrue&#40;this.computationActive&#41;&#59;&#10;&#10; this.asynchronousComputationCancellationSource &#61; new CancellationTokenSource&#40;&#41;&#59;&#10; this.computationActive &#61; true&#59;&#10;&#10; return new AsynchronousComputationToStart&#40;this.asynchronousComputeFunction, this.asynchronousComputationCancellationSource&#41;&#59;&#10; &#125;&#10;&#10; private struct AsynchronousComputationToStart&#10; &#123;&#10; public readonly Func&#60;CancellationToken, Task&#60;T&#62;&#62; AsynchronousComputeFunction&#59;&#10; public readonly CancellationTokenSource CancellationTokenSource&#59;&#10;&#10; public AsynchronousComputationToStart&#40;Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction, CancellationTokenSource cancellationTokenSource&#41;&#10; &#123;&#10; this.AsynchronousComputeFunction &#61; asynchronousComputeFunction&#59;&#10; this.CancellationTokenSource &#61; cancellationTokenSource&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void StartAsynchronousComputation&#40;AsynchronousComputationToStart computationToStart, Request requestToCompleteSynchronously&#41;&#10; &#123;&#10; var cancellationToken &#61; computationToStart.CancellationTokenSource.Token&#59;&#10;&#10; try&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; DO NOT ACCESS ANY FIELDS OR STATE BEYOND THIS POINT. Since this function&#10; &#47;&#47; runs unsynchronized, it&#39;s possible that during this function this request&#10; &#47;&#47; might be cancelled, and then a whole additional request might start and&#10; &#47;&#47; complete inline, and cache the result. By grabbing state before we check&#10; &#47;&#47; the cancellation token, we can be assured that we are only operating on&#10; &#47;&#47; a state that was complete.&#10;&#10; ExceptionUtilities.ExecuteWithErrorReporting&#40;&#40;&#41; &#61;&#62;&#10; &#123;&#10; &#47;&#47; We avoid creating a full closure just to pass the token along&#10; &#47;&#47; Also, use TaskContinuationOptions.ExecuteSynchronously so that we inline &#10; &#47;&#47; the continuation if asynchronousComputeFunction completes synchronously&#10; var task &#61; computationToStart.AsynchronousComputeFunction&#40;cancellationToken&#41;&#59;&#10;&#10; task.ContinueWith&#40;&#10; &#40;t, s&#41; &#61;&#62; CompleteWithTask&#40;t, &#40;&#40;CancellationTokenSource&#41;s&#41;.Token&#41;,&#10; computationToStart.CancellationTokenSource,&#10; cancellationToken,&#10; TaskContinuationOptions.ExecuteSynchronously,&#10; TaskScheduler.Default&#41;&#59;&#10;&#10; if &#40;requestToCompleteSynchronously &#33;&#61; null &#38;&#38; task.IsCompleted&#41;&#10; &#123;&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; task &#61; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;task&#41;&#59;&#10; &#125;&#10;&#10; requestToCompleteSynchronously.CompleteFromTaskSynchronously&#40;task&#41;&#59;&#10; &#125;&#10; &#125;&#41;&#59;&#10; &#125;&#10; catch &#40;OperationCanceledException oce&#41;&#10; &#123;&#10; &#47;&#47; As long as it&#39;s the right token, this means that our thread was the first thread&#10; &#47;&#47; to start an asynchronous computation, but the requestor cancelled as we were starting up&#10; &#47;&#47; the computation.&#10; if &#40;oce.CancellationToken &#33;&#61; cancellationToken&#41;&#10; &#123;&#10; ExceptionUtilities.FailFast&#40;oce&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; private void CompleteWithTask&#40;Task&#60;T&#62; task, CancellationToken cancellationToken&#41;&#10; &#123;&#10; IEnumerable&#60;Request&#62; requestsToComplete&#59;&#10;&#10; using &#40;TakeLock&#40;cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; If the underlying computation was cancelled, then all state was already updated in OnAsynchronousRequestCancelled&#10; &#47;&#47; and there is no new work to do here. We &#42;must&#42; use the local one since this completion may be running far after&#10; &#47;&#47; the background computation was cancelled and a new one might have already been enqueued. We must do this&#10; &#47;&#47; check here under the lock to ensure proper synchronization with OnAsynchronousRequestCancelled.&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; The computation is complete, so get all requests to complete and null out the list. We&#39;ll create another one&#10; &#47;&#47; later if it&#39;s needed&#10; requestsToComplete &#61; this.requests &#63;&#63; SpecializedCollections.EmptyEnumerable&#60;Request&#62;&#40;&#41;&#59;&#10; this.requests &#61; null&#59;&#10;&#10; &#47;&#47; The computations are done&#10; this.asynchronousComputationCancellationSource &#61; null&#59;&#10; this.computationActive &#61; false&#59;&#10; task &#61; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;task&#41;&#59;&#10; &#125;&#10;&#10; foreach &#40;var requestToComplete in requestsToComplete&#41;&#10; &#123;&#10; requestToComplete.CompleteFromTaskAsynchronously&#40;task&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; private Task&#60;T&#62; GetCachedValueAndCacheThisValueIfNoneCached_NoLock&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; if &#40;this.cachedResult &#33;&#61; null&#41;&#10; &#123;&#10; return this.cachedResult&#59;&#10; &#125;&#10; else&#10; &#123;&#10; if &#40;cacheResult &#38;&#38; task.Status &#61;&#61; TaskStatus.RanToCompletion&#41;&#10; &#123;&#10; &#47;&#47; Hold onto the completed task. We can get rid of the computation functions for good&#10; this.cachedResult &#61; task&#59;&#10; this.asynchronousComputeFunction &#61; null&#59;&#10; this.synchronousComputeFunction &#61; null&#59;&#10; &#125;&#10;&#10; return task&#59;&#10; &#125;&#10; &#125;&#10;&#10; private void OnAsynchronousRequestCancelled&#40;object state&#41;&#10; &#123;&#10; var request &#61; &#40;Request&#41;state&#59;&#10; CancellationTokenSource cancellationTokenSource &#61; null&#59;&#10;&#10; using &#40;TakeLock&#40;CancellationToken.None&#41;&#41;&#10; &#123;&#10; &#47;&#47; Now try to remove it. It&#39;s possible that requests may already be null. You could&#10; &#47;&#47; imagine that cancellation was requested, but before we could aquire the lock&#10; &#47;&#47; here the computation completed and the entire CompleteWithTask synchronized&#10; &#47;&#47; block ran. In that case, the requests collection may already be null, or it&#10; &#47;&#47; &#40;even scarier&#33;&#41; may have been replaced with another collection because another&#10; &#47;&#47; computation has started.&#10; if &#40;this.requests &#33;&#61; null&#41;&#10; &#123;&#10; if &#40;this.requests.Remove&#40;request&#41;&#41;&#10; &#123;&#10; if &#40;this.requests.Count &#61;&#61; 0&#41;&#10; &#123;&#10; this.requests &#61; null&#59;&#10;&#10; if &#40;this.asynchronousComputationCancellationSource &#33;&#61; null&#41;&#10; &#123;&#10; cancellationTokenSource &#61; this.asynchronousComputationCancellationSource&#59;&#10; this.asynchronousComputationCancellationSource &#61; null&#59;&#10; this.computationActive &#61; false&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; request.CancelAsynchronously&#40;&#41;&#59;&#10;&#10; if &#40;cancellationTokenSource &#33;&#61; null&#41;&#10; &#123;&#10; cancellationTokenSource.Cancel&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; Using inheritance instead of wrapping a TaskCompletionSource to avoid a second allocation&#10; private class Request &#58; TaskCompletionSource&#60;T&#62;&#10; &#123;&#10; private CancellationTokenRegistration cancellationTokenRegistration&#59;&#10;&#10; public void RegisterForCancellation&#40;Action&#60;object&#62; callback, CancellationToken cancellationToken&#41;&#10; &#123;&#10; cancellationTokenRegistration &#61; cancellationToken.Register&#40;callback, this&#41;&#59;&#10; &#125;&#10;&#10; public void CompleteFromTaskAsynchronously&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; System.Threading.Tasks.Task.Factory.StartNew&#40;CompleteFromTaskSynchronouslyStub, task, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default&#41;&#59;&#10; &#125;&#10;&#10; private void CompleteFromTaskSynchronouslyStub&#40;object task&#41;&#10; &#123;&#10; CompleteFromTaskSynchronously&#40;&#40;Task&#60;T&#62;&#41;task&#41;&#59;&#10; &#125;&#10;&#10; public void CompleteFromTaskSynchronously&#40;Task&#60;T&#62; task&#41;&#10; &#123;&#10; if &#40;task.Status &#61;&#61; TaskStatus.RanToCompletion&#41;&#10; &#123;&#10; if &#40;TrySetResult&#40;task.Result&#41;&#41;&#10; &#123;&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; else if &#40;task.Status &#61;&#61; TaskStatus.Faulted&#41;&#10; &#123;&#10; if &#40;TrySetException&#40;task.Exception&#41;&#41;&#10; &#123;&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; else&#10; &#123;&#10; CancelSynchronously&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; public void CancelAsynchronously&#40;&#41;&#10; &#123;&#10; &#47;&#47; Since there could be synchronous continuations on the TaskCancellationSource, we queue this to the threadpool&#10; &#47;&#47; to avoid inline running of other operations.&#10; System.Threading.Tasks.Task.Factory.StartNew&#40;CancelSynchronously, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default&#41;&#59;&#10; &#125;&#10;&#10; private void CancelSynchronously&#40;&#41;&#10; &#123;&#10; if &#40;TrySetCanceled&#40;&#41;&#41;&#10; &#123;&#10; &#47;&#47; Paranoia&#58; the only reason we should ever get here is if the CancellationToken that&#10; &#47;&#47; we registered against was cancelled, but just in case, dispose the registration&#10; cancellationTokenRegistration.Dispose&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#125;&#10; &#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; A lightweight mutual exclusion object which supports waiting with cancellation and prevents&#10; &#47;&#47;&#47; recursion &#40;i.e. you may not call Wait if you already hold the lock&#41;&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; The &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62; provides a lightweight mutual exclusion class that doesn&#39;t&#10; &#47;&#47;&#47; use Windows kernel synchronization primitives.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; The implementation is distilled from the workings of &#60;see cref&#61;&#34;T&#58;System.Threading.SemaphoreSlim&#34;&#47;&#62;&#10; &#47;&#47;&#47; The basic idea is that we use a regular sync object &#40;Monitor.Enter&#47;Exit&#41; to guard the setting&#10; &#47;&#47;&#47; of an &#39;owning thread&#39; field. If, during the Wait, we find the lock is held by someone else&#10; &#47;&#47;&#47; then we register a cancellation callback and enter a &#34;Monitor.Wait&#34; loop. If the cancellation&#10; &#47;&#47;&#47; callback fires, then it &#34;pulses&#34; all the waiters to wake them up and check for cancellation.&#10; &#47;&#47;&#47; Waiters are also &#34;pulsed&#34; when leaving the lock.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;para&#62;&#10; &#47;&#47;&#47; All public members of &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62; are thread-safe and may be used concurrently&#10; &#47;&#47;&#47; from multiple threads.&#10; &#47;&#47;&#47; &#60;&#47;para&#62;&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; internal sealed class NonReentrantLock&#10; &#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; A synchronization object to protect access to the &#60;see cref&#61;&#34;F&#58;owningThread&#34;&#47;&#62; field and to be pulsed&#10; &#47;&#47;&#47; when &#60;see cref&#61;&#34;M&#58;Release&#34;&#47;&#62; is called and during cancellation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private readonly object syncLock&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Indicates which thread currently holds the lock. If null, then the lock is available.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private volatile Thread owningThread&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Constructor.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;useThisInstanceForSynchronization&#34;&#62;If false &#40;the default&#41;, then the class&#10; &#47;&#47;&#47; allocates an internal object to be used as a sync lock.&#10; &#47;&#47;&#47; If true, then the sync lock object will be the NonReentrantLock instance itself. This&#10; &#47;&#47;&#47; saves an allocation but a client may not safely further use this instance in a call to&#10; &#47;&#47;&#47; Monitor.Enter&#47;Exit or in a &#34;lock&#34; statement.&#10; &#47;&#47;&#47; &#60;&#47;param&#62;&#10; public NonReentrantLock&#40;bool useThisInstanceForSynchronization &#61; false&#41;&#10; &#123;&#10; this.syncLock &#61; useThisInstanceForSynchronization &#63; this &#58; new object&#40;&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Shared factory for use in lazy initialization.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; public static readonly Func&#60;NonReentrantLock&#62; Factory &#61; &#40;&#41; &#61;&#62; new NonReentrantLock&#40;useThisInstanceForSynchronization&#58; true&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Blocks the current thread until it can enter the &#60;see cref&#61;&#34;NonReentrantLock&#34;&#47;&#62;, while observing a&#10; &#47;&#47;&#47; &#60;see cref&#61;&#34;T&#58;System.Threading.CancellationToken&#34;&#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; Recursive locking is not supported. i.e. A thread may not call Wait successfully twice without an&#10; &#47;&#47;&#47; intervening &#60;see cref&#61;&#34;Release&#34;&#47;&#62;.&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;cancellationToken&#34;&#62;The &#60;see cref&#61;&#34;T&#58;System.Threading.CancellationToken&#34;&#47;&#62; token to&#10; &#47;&#47;&#47; observe.&#60;&#47;param&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;System.OperationCanceledException&#34;&#62;&#60;paramref name&#61;&#34;cancellationToken&#34;&#47;&#62; was&#10; &#47;&#47;&#47; canceled.&#60;&#47;exception&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;System.LockRecursionException&#34;&#62;The caller already holds the lock&#60;&#47;exception&#62;&#10; public void Wait&#40;CancellationToken cancellationToken &#61; default&#40;CancellationToken&#41;&#41;&#10; &#123;&#10; if &#40;this.IsOwnedByMe&#41;&#10; &#123;&#10; throw new LockRecursionException&#40;&#41;&#59;&#10; &#125;&#10;&#10; CancellationTokenRegistration cancellationTokenRegistration &#61; default&#40;CancellationTokenRegistration&#41;&#59;&#10;&#10; if &#40;cancellationToken.CanBeCanceled&#41;&#10; &#123;&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; &#47;&#47; Fast path to try and avoid allocations in callback registration.&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; if &#40;&#33;this.IsLocked&#41;&#10; &#123;&#10; this.TakeOwnership&#40;&#41;&#59;&#10; return&#59;&#10; &#125;&#10; &#125;&#10;&#10; cancellationTokenRegistration &#61; cancellationToken.Register&#40;cancellationTokenCanceledEventHandler, this.syncLock, useSynchronizationContext&#58; false&#41;&#59;&#10; &#125;&#10;&#10; using &#40;cancellationTokenRegistration&#41;&#10; &#123;&#10; &#47;&#47; PERF&#58; First spin wait for the lock to become available, but only up to the first planned yield.&#10; &#47;&#47; This additional amount of spinwaiting was inherited from SemaphoreSlim&#39;s implementation where&#10; &#47;&#47; it showed measurable perf gains in test scenarios.&#10; SpinWait spin &#61; new SpinWait&#40;&#41;&#59;&#10; while &#40;this.IsLocked &#38;&#38; &#33;spin.NextSpinWillYield&#41;&#10; &#123;&#10; spin.SpinOnce&#40;&#41;&#59;&#10; &#125;&#10;&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; while &#40;this.IsLocked&#41;&#10; &#123;&#10; &#47;&#47; If cancelled, we throw. Trying to wait could lead to deadlock.&#10; cancellationToken.ThrowIfCancellationRequested&#40;&#41;&#59;&#10;&#10; using &#40;Logger.LogBlock&#40;FeatureId.WaitIndicator, FunctionId.Utilities_NonReentrantLock_BlockingWait, cancellationToken&#41;&#41;&#10; &#123;&#10; &#47;&#47; Another thread holds the lock. Wait until we get awoken either&#10; &#47;&#47; by some code calling &#34;Release&#34; or by cancellation.&#10; Monitor.Wait&#40;this.syncLock&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47; We now hold the lock&#10; this.TakeOwnership&#40;&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Exit the mutual exclusion.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;remarks&#62;&#10; &#47;&#47;&#47; The calling thread must currently hold the lock.&#10; &#47;&#47;&#47; &#60;&#47;remarks&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;Roslyn.Utilities.ContractFailureException&#34;&#62;The lock is not currently held by the calling thread.&#60;&#47;exception&#62;&#10; public void Release&#40;&#41;&#10; &#123;&#10; AssertHasLock&#40;&#41;&#59;&#10;&#10; lock &#40;this.syncLock&#41;&#10; &#123;&#10; this.ReleaseOwnership&#40;&#41;&#59;&#10;&#10; &#47;&#47; Release one waiter&#10; Monitor.Pulse&#40;this.syncLock&#41;&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Determine if the lock is currently held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;returns&#62;True if the lock is currently held by the calling thread.&#60;&#47;returns&#62;&#10; public bool LockHeldByMe&#40;&#41;&#10; &#123;&#10; return this.IsOwnedByMe&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Throw an exception if the lock is not held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;exception cref&#61;&#34;T&#58;Roslyn.Utilities.ContractFailureException&#34;&#62;The lock is not currently held by the calling thread.&#60;&#47;exception&#62;&#10; public void AssertHasLock&#40;&#41;&#10; &#123;&#10; Contract.ThrowIfFalse&#40;LockHeldByMe&#40;&#41;&#41;&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Checks if the lock is currently held.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool IsLocked&#10; &#123;&#10; get&#10; &#123;&#10; return this.owningThread &#33;&#61; null&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Checks if the lock is currently held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private bool IsOwnedByMe&#10; &#123;&#10; get&#10; &#123;&#10; return this.owningThread &#61;&#61; Thread.CurrentThread&#59;&#10; &#125;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Take ownership of the lock &#40;by the calling thread&#41;. The lock may not already&#10; &#47;&#47;&#47; be held by any other code.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private void TakeOwnership&#40;&#41;&#10; &#123;&#10; Contract.Assert&#40;&#33;this.IsLocked&#41;&#59;&#10; this.owningThread &#61; Thread.CurrentThread&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Release ownership of the lock. The lock must already be held by the calling thread.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private void ReleaseOwnership&#40;&#41;&#10; &#123;&#10; Contract.Assert&#40;this.IsOwnedByMe&#41;&#59;&#10; this.owningThread &#61; null&#59;&#10; &#125;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Action object passed to a cancellation token registration.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private static readonly Action&#60;object&#62; cancellationTokenCanceledEventHandler &#61; new Action&#60;object&#62;&#40;CancellationTokenCanceledEventHandler&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Callback executed when a cancellation token is canceled during a Wait.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; &#47;&#47;&#47; &#60;param name&#61;&#34;obj&#34;&#62;The syncLock that protects a &#60;see cref&#61;&#34;NonReentrantLock &#34;&#47;&#62; instance.&#60;&#47;param&#62;&#10; private static void CancellationTokenCanceledEventHandler&#40;object obj&#41;&#10; &#123;&#10; lock &#40;obj&#41;&#10; &#123;&#10; &#47;&#47; Release all waiters to check their cancellation tokens.&#10; Monitor.PulseAll&#40;obj&#41;&#59;&#10; &#125;&#10; &#125;&#10; &#125;&#10;&#125;</pre>2013-05-23T06:05:29-07:00xehttp://rise4fun.com/Seal/xeSeal/xe<pre>I don&#39;t even know what to say, this made tnhigs so much easier&#33;</pre>2013-05-23T06:05:29-07:00Xchttp://rise4fun.com/Seal/XcSeal/Xc<pre>using System&#59;&#13;&#10;class Test &#123;&#13;&#10; public Test f &#61; null&#59;&#13;&#10; public void PureMethod&#40;Test param&#41;&#123;&#13;&#10; var temp &#61; new Test&#40;&#41;&#59;&#13;&#10; temp.f &#61; param&#59; &#13;&#10; &#125; &#13;&#10;&#125;what are you doing&#63;&#13;&#10;</pre>2013-05-23T06:05:29-07:00pNhhttp://rise4fun.com/Seal/pNhSeal/pNh<pre>As web developers we shulod not give any notes about the browsers. The browser developers must react on user interaction and messages and to fix the possible bad software. But the case with IE 6 shows how interesting&#39; is to be a web developer. The problems behind us are giving an extraordinary experience. That&#39;s why we shulod be grateful.</pre>2013-05-23T06:05:29-07:00pnhttp://rise4fun.com/Seal/pnSeal/pn<pre>Sorry, I forgot to reply to this qutesion. Have you had any luck getting it to work&#63; When are you calling stage.displayState&#63; Make sure it is after the application is completely loaded. In my example I added a click handler on a button that set stage.displayState &#61; StageDisplayState.FULL_SCREEN&#59;</pre>2013-05-23T06:05:29-07:00JThttp://rise4fun.com/Seal/JTSeal/JT<pre>using System&#59;&#10;class C &#123;&#10; public static int i &#61; 0&#59;&#10; public void M&#40;&#41;&#123;&#10; i&#43;&#43;&#59; &#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00jPhttp://rise4fun.com/Seal/jPSeal/jP<pre>using System&#59;&#10;class Test &#123;&#10; public Test f &#61; null&#59;&#10; public void PureMethod&#40;Test param&#41;&#123;&#10; var temp &#61; new Test&#40;&#41;&#59;&#10; Console.writeln&#40;&#34;Hello World&#34;&#41;&#59;&#10; temp.f &#61; param&#59; &#10; &#125; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00jishttp://rise4fun.com/Seal/jisSeal/jis<pre>using System&#59;&#10;class XYZ &#123;&#10;&#10; public static add1&#40;int x, int y&#41; &#123; return x&#43;y&#59; &#125;&#10;&#10; int f1, f2&#59;&#10;&#10; public add2&#40;&#41; &#123; return f1&#43;f2&#59; &#125;&#10;&#10; &#10;&#125;&#10;</pre>2013-05-23T06:05:29-07:00DRhttp://rise4fun.com/Seal/DRSeal/DR<pre>I do not remember lenirang to read, and I did not read that much as a child. I spent an inordinate amount of time in front of the boob tube. My mom didn&#39;t know any better back then, like smoking a drinking during pregnancy and bottle feeding. God bless her&#33; She did the best she could without the light she had at the time.</pre>2013-05-23T06:05:29-07:00Dphttp://rise4fun.com/Seal/DpSeal/Dp<pre>using System&#59;&#10;using System.Collections.Generic&#59;&#10;using System.Runtime.CompilerServices&#59;&#10;using System.Threading&#59;&#10;using System.Threading.Tasks&#59;&#10;&#10;namespace Roslyn.Utilities&#10;&#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Represents a value that can be retrieved synchronously or asynchronously by many clients.&#10; &#47;&#47;&#47; The value will be computed on-demand the moment the first client asks for it. While being&#10; &#47;&#47;&#47; computed, more clients can request the value. As long as there are outstanding clients the&#10; &#47;&#47;&#47; underlying computation will proceed. If all outstanding clients cancel their request then&#10; &#47;&#47;&#47; the underlying value computation will be cancelled as well.&#10; &#47;&#47;&#47; &#10; &#47;&#47;&#47; Creators of an &#60;see cref&#61;&#34;AsyncLazy&#123;T&#125;&#34; &#47;&#62; can specify whether the result of the computation is&#10; &#47;&#47;&#47; cached for future requests or not. Choosing to not cache means the computation functions are kept&#10; &#47;&#47;&#47; alive, whereas caching means the value &#40;but not functions&#41; are kept alive once complete.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; internal sealed class AsyncLazy&#60;T&#62;&#10; &#123;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The underlying function that starts an asynchronous computation of the resulting value.&#10; &#47;&#47;&#47; Null&#39;ed out once we&#39;ve computed the result and we&#39;ve been asked to cache it. Otherwise,&#10; &#47;&#47;&#47; it is kept around in case the value needs to be computed again.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Func&#60;CancellationToken, Task&#60;T&#62;&#62; asynchronousComputeFunction&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The underlying function that starts an synchronous computation of the resulting value.&#10; &#47;&#47;&#47; Null&#39;ed out once we&#39;ve computed the result and we&#39;ve been asked to cache it, or if we&#10; &#47;&#47;&#47; didn&#39;t get any synchronous function given to us in the first place.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Func&#60;CancellationToken, T&#62; synchronousComputeFunction&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Whether or not we should keep the value around once we&#39;ve computed it.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private readonly bool cacheResult&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The Task that holds the cached result.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private Task&#60;T&#62; cachedResult&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Mutex used to protect reading and writing to all mutable objects and fields. Traces&#10; &#47;&#47;&#47; indicate that there&#39;s negligible contention on this lock, hence we can save some memory&#10; &#47;&#47;&#47; by using a single lock for all AsyncLazy instances. Only trivial and non-reentrant work&#10; &#47;&#47;&#47; should be done while holding the lock.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private static readonly NonReentrantLock gate &#61; new NonReentrantLock&#40;useThisInstanceForSynchronization&#58; true&#41;&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; The hash set of all currently outstanding asynchronous requests. Null if there are no requests,&#10; &#47;&#47;&#47; and will never be empty.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private HashSet&#60;Request&#62; requests &#61; null&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; If an asynchronous request is active, the CancellationTokenSource that allows for&#10; &#47;&#47;&#47; cancelling the underlying computation.&#10; &#47;&#47;&#47; &#60;&#47;summary&#62;&#10; private CancellationTokenSource asynchronousComputationCancellationSource &#61; null&#59;&#10;&#10; &#47;&#47;&#47; &#60;summary&#62;&#10; &#47;&#47;&#47; Whether a computation is active or q