From: Miguel de Icaza Date: Fri, 26 Apr 2013 16:47:07 +0000 (-0700) Subject: Merge pull request #618 from knocte/aspnet_lru X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=commitdiff_plain;h=675328ef1686ce884326df89f8c570943e2d20d2;hp=edc181c514ddc082ce44cde69f2272a12e942b88;p=mono.git Merge pull request #618 from knocte/aspnet_lru [WebConfigurationManager] Fix memleak by using a LruCache (BXC#5598) --- diff --git a/man/mono.1 b/man/mono.1 index 318c0586d0f..6d9a37f719b 100644 --- a/man/mono.1 +++ b/man/mono.1 @@ -3,10 +3,11 @@ .\" Copyright 2003 Ximian, Inc. .\" Copyright 2004-2011 Novell, Inc. .\" Copyright 2011-2012 Xamarin Inc +.\" Copyright 2013 7digital Media Ltd. .\" Author: .\" Miguel de Icaza (miguel@gnu.org) .\" -.TH Mono "Mono 2.11" +.TH Mono "Mono 3.0" .SH NAME mono \- Mono's ECMA-CLI native code generator (Just-in-Time and Ahead-of-Time) .SH SYNOPSIS @@ -897,6 +898,13 @@ above locations. If you don't want the mapping to be performed you can set this variable in your environment before starting the application and no action will be taken. .TP +\fBMONO_ASPNET_WEBCONFIG_CACHESIZE\fR +Mono has a cache of ConfigSection objects for speeding up WebConfigurationManager +queries. Its default size is 100 items, and when more items are needed, cache +evictions start happening. If evictions are too frequent this could impose +unnecessary overhead, which could be avoided by using this environment variable +to set up a higher cache size (or to lower memory requirements by decreasing it). +.TP \fBMONO_CFG_DIR\fR If set, this variable overrides the default system configuration directory ($PREFIX/etc). It's used to locate machine.config file. diff --git a/mcs/class/System.Web/System.Web-net_4_0.csproj b/mcs/class/System.Web/System.Web-net_4_0.csproj index 1a6861ddef2..84e6a859a12 100644 --- a/mcs/class/System.Web/System.Web-net_4_0.csproj +++ b/mcs/class/System.Web/System.Web-net_4_0.csproj @@ -282,6 +282,7 @@ + diff --git a/mcs/class/System.Web/System.Web.Configuration_2.0/LruCache.cs b/mcs/class/System.Web/System.Web.Configuration_2.0/LruCache.cs new file mode 100644 index 00000000000..3bd959938dc --- /dev/null +++ b/mcs/class/System.Web/System.Web.Configuration_2.0/LruCache.cs @@ -0,0 +1,144 @@ +// +// A simple LRU cache +// +// Authors: +// Miguel de Icaza (miguel@gnome.org) +// Andres G. Aragoneses (andres@7digital.com) +// +// Copyright 2010 Miguel de Icaza +// Copyright 2013 7digital Media Ltd. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +using System; +using System.Collections.Generic; + +namespace System.Web.Configuration { + + class LruCache { + Dictionary> dict; + Dictionary, TKey> revdict; + LinkedList list; + int entry_limit; + + bool eviction_warning_shown; + int evictions; + bool size_overriden; + + internal string EvictionWarning { set; private get; } + + public LruCache (int entryLimit) + { + entry_limit = entryLimit; + dict = new Dictionary> (); + revdict = new Dictionary, TKey> (); + list = new LinkedList (); + } + + //for debugging: public int Count { get { return dict.Count; } } + + void Evict () + { + var last = list.Last; + if (last == null) + return; + + var key = revdict [last]; + + dict.Remove (key); + revdict.Remove (last); + list.RemoveLast (); + DisposeValue (last.Value); + evictions++; + + if (!String.IsNullOrEmpty (EvictionWarning) && !eviction_warning_shown && (evictions >= entry_limit)) { + Console.Error.WriteLine ("WARNING: " + EvictionWarning); + eviction_warning_shown = true; + } + } + + public void Clear () + { + foreach (var element in list) { + DisposeValue (element); + } + + dict.Clear (); + revdict.Clear (); + list.Clear (); + eviction_warning_shown = false; + evictions = 0; + } + + void DisposeValue (TValue value) + { + if (value is IDisposable) { + ((IDisposable)value).Dispose (); + } + } + + public bool TryGetValue (TKey key, out TValue value) + { + LinkedListNode node; + + if (dict.TryGetValue (key, out node)){ + list.Remove (node); + list.AddFirst (node); + + value = node.Value; + return true; + } + value = default (TValue); + return false; + } + + public void Add (TKey key, TValue value) + { + LinkedListNode node; + + if (dict.TryGetValue (key, out node)){ + + // If we already have a key, move it to the front + list.Remove (node); + list.AddFirst (node); + + // Remove the old value + DisposeValue (node.Value); + + node.Value = value; + return; + } + + if (dict.Count >= entry_limit) + Evict (); + + // Adding new node + node = new LinkedListNode (value); + list.AddFirst (node); + dict [key] = node; + revdict [node] = key; + } + + public override string ToString () + { + return "LRUCache dict={0} revdict={1} list={2}"; + } + } +} diff --git a/mcs/class/System.Web/System.Web.Configuration_2.0/WebConfigurationManager.cs b/mcs/class/System.Web/System.Web.Configuration_2.0/WebConfigurationManager.cs index 9c7b8524e48..bc3193d91ea 100644 --- a/mcs/class/System.Web/System.Web.Configuration_2.0/WebConfigurationManager.cs +++ b/mcs/class/System.Web/System.Web.Configuration_2.0/WebConfigurationManager.cs @@ -72,11 +72,10 @@ namespace System.Web.Configuration { // See comment for the cacheLock field at top of System.Web.Caching/Cache.cs static readonly ReaderWriterLockSlim sectionCacheLock; - + #if !TARGET_J2EE static IInternalConfigConfigurationFactory configFactory; static Hashtable configurations = Hashtable.Synchronized (new Hashtable ()); - static Dictionary sectionCache = new Dictionary (); static Hashtable configPaths = Hashtable.Synchronized (new Hashtable ()); static bool suppressAppReload; #else @@ -188,9 +187,28 @@ namespace System.Web.Configuration { } } } + + const int DEFAULT_SECTION_CACHE_SIZE = 100; + const string CACHE_SIZE_OVERRIDING_KEY = "MONO_ASPNET_WEBCONFIG_CACHESIZE"; + static LruCache sectionCache; static WebConfigurationManager () { + var section_cache_size = DEFAULT_SECTION_CACHE_SIZE; + int section_cache_size_override; + bool size_overriden = false; + if (int.TryParse (Environment.GetEnvironmentVariable (CACHE_SIZE_OVERRIDING_KEY), out section_cache_size_override)) { + section_cache_size = section_cache_size_override; + size_overriden = true; + Console.WriteLine ("WebConfigurationManager's LRUcache Size overriden to: {0} (via {1})", section_cache_size_override, CACHE_SIZE_OVERRIDING_KEY); + } + sectionCache = new LruCache (section_cache_size); + string eviction_warning = "WebConfigurationManager's LRUcache evictions count reached its max size"; + if (!size_overriden) + eviction_warning += String.Format ("{0}Cache Size: {1} (overridable via {2})", + Environment.NewLine, section_cache_size, CACHE_SIZE_OVERRIDING_KEY); + sectionCache.EvictionWarning = eviction_warning; + configFactory = ConfigurationManager.ConfigurationFactory; _Configuration.SaveStart += ConfigurationSaveHandler; _Configuration.SaveEnd += ConfigurationSaveHandler; diff --git a/mcs/class/System.Web/System.Web.dll.sources b/mcs/class/System.Web/System.Web.dll.sources index 377652f2aef..449b02e34be 100644 --- a/mcs/class/System.Web/System.Web.dll.sources +++ b/mcs/class/System.Web/System.Web.dll.sources @@ -191,6 +191,7 @@ System.Web.Configuration_2.0/IConfigMapPath.cs System.Web.Configuration_2.0/IConfigMapPathFactory.cs System.Web.Configuration_2.0/IRemoteWebConfigurationHostServer.cs System.Web.Configuration_2.0/LowerCaseStringConverter.cs +System.Web.Configuration_2.0/LruCache.cs System.Web.Configuration_2.0/MachineKeyRegistryStorage.cs System.Web.Configuration_2.0/MachineKeySection.cs System.Web.Configuration_2.0/MachineKeyValidation.cs