You've already forked linux-packaging-mono
							
							
		
			
				
	
	
		
			149 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			149 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| //---------------------------------------------------------------------
 | |
| // <copyright file="Memoizer.cs" company="Microsoft">
 | |
| //      Copyright (c) Microsoft Corporation.  All rights reserved.
 | |
| // </copyright>
 | |
| //
 | |
| // @owner  	 Microsoft, Microsoft
 | |
| //---------------------------------------------------------------------
 | |
| 
 | |
| using System.Collections.Generic;
 | |
| using System.Threading;
 | |
| using System.Diagnostics;
 | |
| namespace System.Data.Common.Utils
 | |
| {
 | |
|     /// <summary>
 | |
|     /// Remembers the result of evaluating an expensive function so that subsequent
 | |
|     /// evaluations are faster. Thread-safe.
 | |
|     /// </summary>
 | |
|     /// <typeparam name="TArg">Type of the argument to the function.</typeparam>
 | |
|     /// <typeparam name="TResult">Type of the function result.</typeparam>
 | |
|     internal sealed class Memoizer<TArg, TResult>
 | |
|     {
 | |
|         private readonly Func<TArg, TResult> _function;
 | |
|         private readonly Dictionary<TArg, Result> _resultCache;
 | |
|         private readonly ReaderWriterLockSlim _lock;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Constructs
 | |
|         /// </summary>
 | |
|         /// <param name="function">Required. Function whose values are being cached.</param>
 | |
|         /// <param name="argComparer">Optional. Comparer used to determine if two functions arguments
 | |
|         /// are the same.</param>
 | |
|         internal Memoizer(Func<TArg, TResult> function, IEqualityComparer<TArg> argComparer)
 | |
|         {
 | |
|             EntityUtil.CheckArgumentNull(function, "function");
 | |
| 
 | |
|             _function = function;
 | |
|             _resultCache = new Dictionary<TArg, Result>(argComparer);
 | |
|             _lock = new ReaderWriterLockSlim();
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Evaluates the wrapped function for the given argument. If the function has already
 | |
|         /// been evaluated for the given argument, returns cached value. Otherwise, the value
 | |
|         /// is computed and returned.
 | |
|         /// </summary>
 | |
|         /// <param name="arg">Function argument.</param>
 | |
|         /// <returns>Function result.</returns>
 | |
|         internal TResult Evaluate(TArg arg)
 | |
|         {
 | |
|             Result result;
 | |
| 
 | |
|             // Check to see if a result has already been computed
 | |
|             if (!TryGetResult(arg, out result))
 | |
|             {
 | |
|                 // compute the new value
 | |
|                 _lock.EnterWriteLock();
 | |
|                 try
 | |
|                 {
 | |
|                     // see if the value has been computed in the interim
 | |
|                     if (!_resultCache.TryGetValue(arg, out result))
 | |
|                     {
 | |
|                         result = new Result(() => _function(arg));
 | |
|                         _resultCache.Add(arg, result);
 | |
|                     }
 | |
|                 }
 | |
|                 finally
 | |
|                 {
 | |
|                     _lock.ExitWriteLock();
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             // note: you need to release the global cache lock before (potentially) acquiring
 | |
|             // a result lock in result.GetValue()
 | |
|             return result.GetValue();
 | |
|         }
 | |
| 
 | |
|         internal bool TryGetValue(TArg arg, out TResult value)
 | |
|         {
 | |
|             Result result;
 | |
|             if (TryGetResult(arg, out result))
 | |
|             {
 | |
|                 value = result.GetValue();
 | |
|                 return true;
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 value = default(TResult);
 | |
|                 return false;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private bool TryGetResult(TArg arg, out Result result)
 | |
|         {
 | |
|             _lock.EnterReadLock();
 | |
|             try
 | |
|             {
 | |
|                 return _resultCache.TryGetValue(arg, out result);
 | |
|             }
 | |
|             finally
 | |
|             {
 | |
|                 _lock.ExitReadLock();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Encapsulates a 'deferred' result. The result is constructed with a delegate (must not 
 | |
|         /// be null) and when the user requests a value the delegate is invoked and stored.
 | |
|         /// </summary>
 | |
|         private class Result
 | |
|         {
 | |
|             private TResult _value;
 | |
|             private Func<TResult> _delegate;
 | |
| 
 | |
|             internal Result(Func<TResult> createValueDelegate)
 | |
|             {
 | |
|                 Debug.Assert(null != createValueDelegate, "delegate must be given");
 | |
|                 _delegate = createValueDelegate;
 | |
|             }
 | |
| 
 | |
|             internal TResult GetValue()
 | |
|             {
 | |
|                 if (null == _delegate)
 | |
|                 {
 | |
|                     // if the delegate has been cleared, it means we have already computed the value
 | |
|                     return _value;
 | |
|                 }
 | |
| 
 | |
|                 // lock the entry while computing the value so that two threads
 | |
|                 // don't simultaneously do the work
 | |
|                 lock (this)
 | |
|                 {
 | |
|                     if (null == _delegate)
 | |
|                     {
 | |
|                         // between our initial check and our acquisition of the lock, some other
 | |
|                         // thread may have computed the value
 | |
|                         return _value;
 | |
|                     }
 | |
|                     _value = _delegate();
 | |
| 
 | |
|                     // ensure _delegate (and its closure) is garbage collected, and set to null
 | |
|                     // to indicate that the value has been computed
 | |
|                     _delegate = null;
 | |
|                     return _value;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 |