コード例 #1
0
ファイル: TaskCacheTests.cs プロジェクト: lethek/task-cache
        public async Task AddOrGetExisting_InvalidatesCompletedResultsWhenExpirationOnCompletionSet()
        {
            string key   = "key";
            string value = "test";

            //Configure so that tasks should be immediately removed from the cache the instant they complete.
            var expirationPolicy = new TaskCacheItemPolicy {
                ExpirationOnCompletion = true
            };

            var valueFactoryStarted  = new ManualResetEvent(false);
            var valueFactoryContinue = new ManualResetEvent(false);

            Func <Task <TestValue> > valueFactory = () => {
                return(Task.Factory.StartNew(() => {
                    valueFactoryStarted.Set();
                    valueFactoryContinue.WaitOne();
                    return new TestValue(value);
                }));
            };

            //First assert that the value is not yet in the cache
            Assert.False(_cache.Contains(key));

            var cacheUserTask = _cache.AddOrGetExisting(key, valueFactory, expirationPolicy);

            // Wait until the value get from cache is in the middle of the value generation.
            // At this point, a Task that is running but not completed has been added to the cache.
            valueFactoryStarted.WaitOne();

            // While the value generation is still running, confirm that it is present in the cache.
            Assert.True(_cache.Contains(key));

            // Let value generation run to completion.
            valueFactoryContinue.Set();

            await cacheUserTask;

            // Assert that the value has now been invalidated and removed from the cache
            Assert.False(_cache.Contains(key));
        }
コード例 #2
0
ファイル: TaskCache.cs プロジェクト: lethek/task-cache
        public async Task <T> AddOrGetExisting <T>(string key, Func <Task <T> > valueFactory, TaskCacheItemPolicy policy = default)
        {
            var asyncLazyValue = _cache.GetOrCreate(key, entry => {
                //Add a common expiration token which is used for clearing the cache
                entry.AddExpirationToken(new CancellationChangeToken(_cts.Token));
                //Customize expiration as per the policy
                entry.AbsoluteExpiration = policy.AbsoluteExpiration;
                entry.AbsoluteExpirationRelativeToNow = policy.AbsoluteExpirationRelativeToNow;
                entry.SlidingExpiration = policy.SlidingExpiration;
                return(new AsyncLazy <T>(
                           factory: () => !policy.ExpirationOnCompletion
                        ? valueFactory()
                        : valueFactory()
                           .ContinueWith(task => {
                    //Add an expiration token which triggers when the Task has completed (succeeded/failed/cancelled)
                    entry.AddExpirationToken(new TaskChangeToken(task));
                    return task;
                })
                           .Unwrap()
                           ));
            });

            try {
                var result = await asyncLazyValue.ConfigureAwait(false);

                // The awaited Task has completed. Check that the task still is the same version
                // that the cache returns (i.e. the awaited task has not been invalidated during the await).
                if (_cache.TryGetValue(key, out var existingValue))
                {
                    if (existingValue != asyncLazyValue)
                    {
                        // The awaited value is no more the most recent one.
                        // Get the most recent value with a recursive call.
                        return(await AddOrGetExisting(key, valueFactory, policy).ConfigureAwait(false));
                    }
                }

                return(result);
            } catch (Exception) {
                // Task object for the given key failed with exception. Remove the task from the cache.
                _cache.Remove(key);
                // Re throw the exception to be handled by the caller.
                throw;
            }
        }