WeakEventTable.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Base / MS / Internal / WeakEventTable.cs / 3 / WeakEventTable.cs

                            //---------------------------------------------------------------------------- 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// Description: Storage for the "weak event listener" pattern. 
//              See WeakEventManager.cs for an overview. 
//
//--------------------------------------------------------------------------- 

using System;
using System.Diagnostics;           // Debug
using System.Collections;           // Hashtable 
using System.Collections.Specialized; // HybridDictionary
using System.Runtime.CompilerServices;  // RuntimeHelpers 
using System.Security;              // [SecurityCritical,SecurityTreatAsSafe] 
using System.Threading;             // [ThreadStatic]
using System.Windows;               // WeakEventManager 
using System.Windows.Threading;     // DispatcherObject
using MS.Utility;                   // FrugalList

namespace MS.Internal 
{
    ///  
    /// This class manages the correspondence between event types and 
    /// event managers, in support of the "weak event listener" pattern.
    /// It also stores data on behalf of the managers;  a manager can store 
    /// data of its own choosing, indexed by the pair (manager, source).
    /// 
    internal class WeakEventTable : DispatcherObject
    { 
        #region Constructors
 
        // 
        //  Constructors
        // 

        /// 
        /// Create a new instance of WeakEventTable.
        ///  
        /// 
        ///     Critical: This code calls into Link demanded methods to attach handlers 
        ///     TreatAsSafe: This code does not take any parameter or return state. 
        ///     It simply attaches private call back.
        ///  
        [SecurityCritical,SecurityTreatAsSafe]
        private WeakEventTable()
        {
            // Set up the final cleanup 
            Dispatcher.ShutdownFinished += new EventHandler(OnDispatcherShutDown);
 
            // also cleanup during AppDomain shutdown events (bug 121070) 
            AppDomain.CurrentDomain.ProcessExit += new EventHandler(OnDispatcherShutDown);
            AppDomain.CurrentDomain.DomainUnload += new EventHandler(OnDispatcherShutDown); 

            _lookupKeyDisposable = new LookupKeyDisposable(this);
        }
 
        #endregion Constructors
 
        #region Internal Properties 

        // 
        //  Internal Properties
        //

        ///  
        /// Return the WeakEventTable for the current thread
        ///  
        internal static WeakEventTable CurrentWeakEventTable 
        {
            get 
            {
                // _currentTable is [ThreadStatic], so there's one per thread
                if (_currentTable == null)
                { 
                    _currentTable = new WeakEventTable();
                } 
 
                return _currentTable;
            } 
        }

        /// 
        /// Take a read-lock on the table, and return the IDisposable. 
        /// Queries to the table should occur within a
        /// "using (Table.ReadLock) { ... }" clause, except for queries 
        /// that are already within a write lock. 
        /// 
        internal IDisposable ReadLock 
        {
            get { return _lock.ReadLock; }
        }
 
        /// 
        /// Take a write-lock on the table, and return the IDisposable. 
        /// All modifications to the table should occur within a 
        /// "using (Table.WriteLock) { ... }" clause.
        ///  
        internal IDisposable WriteLock
        {
            get { return _lock.WriteLock; }
        } 

        ///  
        /// Get or set the manager instance for the given type. 
        /// 
        internal WeakEventManager this[Type managerType] 
        {
            get { return (WeakEventManager)_managerTable[managerType]; }
            set { _managerTable[managerType] = value; }
        } 

        ///  
        /// Get or set the data stored by the given manager for the given source. 
        /// 
        internal object this[WeakEventManager manager, object source] 
        {
            get
            {
                object result; 
                using (SetLookupKey(manager, source))
                { 
                    result = _dataTable[_lookupKey]; 
                }
                return result; 
            }

            set
            { 
                EventKey key = new EventKey(manager, source);
                _dataTable[key] = value; 
            } 
        }
 
        /// 
        /// Indicates whether cleanup is enabled.
        /// 
        ///  
        /// Normally cleanup is always enabled, but a perf test environment might
        /// want to disable cleanup so that it doesn't interfere with the real 
        /// perf measurements. 
        /// 
        internal bool IsCleanupEnabled 
        {
            get { return _cleanupEnabled; }
            set { _cleanupEnabled = value; }
        } 

        #endregion Internal Properties 
 
        #region Internal Methods
 
        //
        //  Internal Methods
        //
 
        /// 
        /// Remove the data for the given manager and source. 
        ///  
        internal void Remove(WeakEventManager manager, object source)
        { 
            using (SetLookupKey(manager, source))
            {
                _dataTable.Remove(_lookupKey);
            } 
        }
 
        ///  
        /// Schedule a cleanup pass.  This can be called from any thread.
        ///  
        internal void ScheduleCleanup()
        {
            // only the first request after a previous cleanup should schedule real work
            if (Interlocked.Increment(ref _cleanupRequests) == 1) 
            {
                Dispatcher.BeginInvoke(DispatcherPriority.ContextIdle, new DispatcherOperationCallback(CleanupOperation), null); 
            } 
        }
 
        /// 
        /// Perform a cleanup pass.
        /// 
        internal static bool Cleanup() 
        {
            return CurrentWeakEventTable.Purge(false); 
        } 

        #endregion Internal Methods 

        #region Private Methods

        // 
        //  Private Methods
        // 
 
        // set the lookup key - IDisposable ensures it gets cleared after
        // use, so that it doesn't hold a reference to the source 
        IDisposable SetLookupKey(WeakEventManager manager, object source)
        {
            _lookupKey.Set(manager, source);
            return _lookupKeyDisposable; 
        }
 
        // run a cleanup pass 
        private object CleanupOperation(object arg)
        { 
            // allow new requests, even if cleanup is disabled
            Interlocked.Exchange(ref _cleanupRequests, 0);

            if (IsCleanupEnabled) 
            {
                Purge(false); 
            } 

            return null; 
        }

        // remove dead entries.  When purgeAll is true, remove all entries.
        private bool Purge(bool purgeAll) 
        {
            bool foundDirt = false; 
 
            using (this.WriteLock)
            { 
                // copy the keys into a separate array, so that later on
                // we can change the table while iterating over the keys
                ICollection ic = _dataTable.Keys;
                EventKey[] keys = new EventKey[ic.Count]; 
                ic.CopyTo(keys, 0);
 
                for (int i=keys.Length-1; i>=0; --i) 
                {
                    object source = keys[i].Source; 
                    foundDirt |= keys[i].Manager.PurgeInternal(source, _dataTable[keys[i]], purgeAll);

                    // if source has been GC'd, remove its data
                    if (!purgeAll && source == null) 
                    {
                        _dataTable.Remove(keys[i]); 
                    } 
                }
 
                if (purgeAll)
                {
                    _managerTable.Clear();
                    _dataTable.Clear(); 
                }
            } 
 
            return foundDirt;
        } 


        // do the final cleanup when the Dispatcher is shut down
        private void OnDispatcherShutDown(object sender, EventArgs e) 
        {
            Purge(true); 
 
            // remove the table from thread storage
            _currentTable = null; 
        }

        #endregion Private Methods
 
        #region Private Fields
 
        // 
        //  Private Fields
        // 

        private Hashtable _managerTable = new Hashtable();  // maps manager type -> instance
        private Hashtable _dataTable = new Hashtable();     // maps EventKey -> data
        private EventKey _lookupKey; 
        private LookupKeyDisposable _lookupKeyDisposable;
 
        ReaderWriterLockWrapper     _lock = new ReaderWriterLockWrapper(); 
        private int                 _cleanupRequests;
        private bool                _cleanupEnabled = true; 

        [ThreadStatic]
        private static WeakEventTable   _currentTable;  // one table per thread
 
        #endregion Private Fields
 
        #region Table Keys 

        // the key for the data table:   
        private struct EventKey
        {
            internal EventKey(WeakEventManager manager, object source)
            { 
                _manager = manager;
                _source = new WeakReference(source); 
                _hashcode = unchecked(manager.GetHashCode() + RuntimeHelpers.GetHashCode(source)); 
            }
 
            internal void Set(WeakEventManager manager, object source)
            {
                _manager = manager;
                _source = source; 
                _hashcode = unchecked(manager.GetHashCode() + RuntimeHelpers.GetHashCode(source));
            } 
 
            internal void Clear()
            { 
                _manager = null;
                _source = null;
                _hashcode = 0;
            } 

            internal object Source 
            { 
                get { return ((WeakReference)_source).Target; }
            } 

            internal WeakEventManager Manager
            {
                get { return _manager; } 
            }
 
            public override int GetHashCode() 
            {
#if DEBUG 
                WeakReference wr = _source as WeakReference;
                object source = (wr != null) ? wr.Target : _source;
                if (source != null)
                { 
                    int hashcode = unchecked(_manager.GetHashCode() + RuntimeHelpers.GetHashCode(source));
                    Debug.Assert(hashcode == _hashcode, "hashcodes disagree"); 
                } 
#endif
 
                return _hashcode;
            }

            public override bool Equals(object o) 
            {
                if (o is EventKey) 
                { 
                    WeakReference wr;
                    EventKey ek = (EventKey)o; 

                    if (_manager != ek._manager || _hashcode != ek._hashcode)
                        return false;
 
                    wr = this._source as WeakReference;
                    object s1 = (wr != null) ? wr.Target : this._source; 
                    wr = ek._source as WeakReference; 
                    object s2 = (wr != null) ? wr.Target : ek._source;
 
                    if (s1!=null && s2!=null)
                        return (s1 == s2);
                    else
                        return (_source == ek._source); 
                }
                else 
                { 
                    return false;
                } 
            }

            public static bool operator==(EventKey key1, EventKey key2)
            { 
                return key1.Equals(key2);
            } 
 
            public static bool operator!=(EventKey key1, EventKey key2)
            { 
                return !key1.Equals(key2);
            }

            WeakEventManager _manager; 
            object _source;             // lookup: direct ref;  In table: WeakRef
            int _hashcode;              // cached, in case source is GC'd 
        } 

        private class LookupKeyDisposable : IDisposable 
        {
            internal LookupKeyDisposable(WeakEventTable table)
            {
                _table = table; 
            }
 
            public void Dispose() 
            {
                _table._lookupKey.Clear(); 
            }

            WeakEventTable _table;
        } 

        #endregion Table Keys 
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.


                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK