/* Copyright 2008 The 'A Concurrent Hashtable' development team (http://www.codeplex.com/CH/People/ProjectPeople.aspx) This library is licensed under the GNU Library General Public License (LGPL). You should have received a copy of the license along with the source code. If not, an online copy of the license can be found at http://www.codeplex.com/CH/license. */ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace Orvid.Concurrent.Collections { /// /// Helper class for ConcurrentWeakHashtable. Makes sure that its DoMaintenance method /// gets called when the GarbageCollector has collected garbage. /// internal static class MaintenanceWorker { #region Garbage Collection tracking /// /// Check all table registrations for continued existence. Removes the entries for /// tables that have been GC'd /// private static void RemoveVoidTables() { //empty head may remain.. not bad. var pos = _TableList; if (pos != null) while (true) { var next = pos.Next; if (next == null) break; if (next.Target == null) pos.Next = next.Next; //safe, only 1 thread doing this else pos = next; } } /// /// true if a table maintenance session is scheduled or ongoing. /// static int _TableMaintenanceIsPending = 0; #if !SILVERLIGHT /// /// Small timer that will /// check every 1/10 second if second level GC count increased. /// static Timer _Timer = new Timer(CheckGCCount, null, 100, 100); static int _Level2GCCount; #else /// /// Small timer that will start a garbage sweep every 10 seconds. /// In growing hashtables the garbage will get swept regularly but they /// can not shrink without a sweep. /// static Timer _Timer = new Timer(CheckGCCount, null, 10000, 10000); #endif /// /// Checks if a garbage collection has indeed taken place and schedules /// a table maintenance session. /// /// static internal void CheckGCCount(object dummy) { #if !SILVERLIGHT if (_Level2GCCount != GC.CollectionCount(2) && Interlocked.CompareExchange(ref _TableMaintenanceIsPending, 1, 0) == 0) { _Level2GCCount = GC.CollectionCount(2); #else if (Interlocked.CompareExchange(ref _TableMaintenanceIsPending, 1, 0) == 0) { #endif RemoveVoidTables(); var thread = new System.Threading.Thread( new ThreadStart( delegate { try { var pos = _TableList; while (pos != null) { var target = (IMaintainable)pos.Target; if (target != null) target.DoMaintenance(); pos = pos.Next; } } finally { _TableMaintenanceIsPending = 0; } } ) ); #if !SILVERLIGHT thread.Priority = ThreadPriority.Highest; #endif thread.Start(); } } #endregion #region maintaining Tables list sealed class ListNode { public ListNode(object target) { _Reference = new WeakReference(target); } WeakReference _Reference; public object Target { get { return _Reference.Target; } } public ListNode Next; } /// /// a list of all WeakHashtables /// static ListNode _TableList; /// /// this is to be called from the constructor or initializer of a ConcurrentWeakHashtable instance /// internal static void Register(IMaintainable table) { var node = new ListNode(table); //Next may not be assigned correct value yet when garbage sweep starts //but this is not a very big deal. node.Next = _TableList; node.Next = Interlocked.Exchange(ref _TableList, node); } #endregion } }