/*
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
}
}