/* * Process Hacker - * provider base class * * Copyright (C) 2008-2009 wj32 * * This file is part of Process Hacker. * * Process Hacker is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Process Hacker is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Process Hacker. If not, see . */ using System; using System.Collections.Generic; using System.Threading; using ProcessHacker.Common; using ProcessHacker.Common.Objects; namespace ProcessHacker { /// /// Provides services for continuously updating a dictionary. /// public abstract class Provider : BaseObject, IProvider { /// /// A generic delegate which is used when updating the dictionary. /// public delegate void ProviderUpdateOnce(); /// /// Represents a handler called when a dictionary item is added. /// /// The added item. public delegate void ProviderDictionaryAdded(TValue item); /// /// Represents a handler called when a dictionary item is modified. /// /// The modified item. public delegate void ProviderDictionaryModified(TValue oldItem, TValue newItem); /// /// Represents a handler called when a dictionary item is removed. /// /// The removed item. public delegate void ProviderDictionaryRemoved(TValue item); /// /// Represents a handler called when an error occurs while updating. /// /// The raised exception. public delegate void ProviderError(Exception ex); /// /// Occurs when the provider needs to update the dictionary (after waiting the duration of the interval). /// protected event ProviderUpdateOnce ProviderUpdate; public new event Action Disposed; public event ProviderUpdateOnce BeforeUpdate; /// /// Occurs when the provider has been updated. /// public event ProviderUpdateOnce Updated; /// /// Occurs when the provider adds an item to the dictionary. /// public event ProviderDictionaryAdded DictionaryAdded; /// /// Occurs when the provider modifies an item in the dictionary. /// public event ProviderDictionaryModified DictionaryModified; /// /// Occurs when the provider removes an item from the dictionary. /// public event ProviderDictionaryRemoved DictionaryRemoved; /// /// Occurs when an exception is raised while updating. /// public event ProviderError Error; private string _name = string.Empty; private Thread _thread; private IDictionary _dictionary; private object _busyLock = new object(); private bool _disposing = false; private bool _busy = false; private bool _createThread = true; private bool _enabled = false; private int _runCount = 0; private int _interval; /// /// Creates a new instance of the Provider class. /// public Provider() : this(new Dictionary()) { } /// /// Creates a new instance of the Provider class, specifying a /// custom equality comparer. /// public Provider(IEqualityComparer comparer) : this(new Dictionary(comparer)) { } /// /// Creates a new instance of the Provider class, specifying a /// custom instance. /// public Provider(IDictionary dictionary) { if (dictionary == null) throw new ArgumentNullException("dictionary"); _dictionary = dictionary; } protected override void DisposeObject(bool disposing) { Logging.Log(Logging.Importance.Information, "Provider (" + this.Name + "): disposing (" + disposing.ToString() + ")"); _disposing = true; if (disposing) Monitor.Enter(_busyLock); //if (_thread != null) //{ // _thread.Abort(); // _thread = null; //} if (this.Disposed != null) { try { this.Disposed(this); } catch (Exception ex) { Logging.Log(ex); } } if (disposing) Monitor.Exit(_busyLock); Logging.Log(Logging.Importance.Information, "Provider (" + this.Name + "): finished disposing (" + disposing.ToString() + ")"); } public string Name { get { return _name; } protected set { _name = value; if (_name == null) _name = string.Empty; } } /// /// Determines whether the provider is currently updating. /// public bool Busy { get { return _busy; } } /// /// If enabled, the provider manages a background thread for the updater. /// public bool CreateThread { get { return _createThread; } set { _createThread = value; } } /// /// Determines whether the provider should update. /// public bool Enabled { get { return _enabled; } set { _enabled = value; if (_enabled && _createThread && _thread == null) { _thread = new Thread(new ThreadStart(Update)); _thread.IsBackground = true; _thread.SetApartmentState(ApartmentState.STA); _thread.Start(); _thread.Priority = ThreadPriority.Lowest; } } } /// /// Gets the number of times this provider has updated. /// public int RunCount { get { return _runCount; } } /// /// Gets or sets the interval to wait between each update. /// public int Interval { get { return _interval; } set { _interval = value; } } /// /// Gets the dictionary. /// public IDictionary Dictionary { get { return _dictionary; } protected set { _dictionary = value; } } /// /// Updates the provider if it is enabled. /// private void Update() { while (true) { if (_enabled && !_disposing) { this.RunOnce(); } Thread.Sleep(_interval); } } /// /// Updates the provider. If it is already updating, this function waits until it finishes. /// public void RunOnce() { lock (_busyLock) { // Bail out if we are disposing if (_disposing) { Logging.Log(Logging.Importance.Warning, "Provider (" + _name + "): RunOnce: currently disposing"); return; } _busy = true; if (ProviderUpdate != null) { try { if (BeforeUpdate != null) BeforeUpdate(); } catch { } try { ProviderUpdate(); _runCount++; } catch (Exception ex) { try { if (Error != null) Error(ex); } catch { } Logging.Log(ex); } try { if (Updated != null) Updated(); } catch { } } _busy = false; } } /// /// Updates the provider in an internal worker thread. /// public void RunOnceAsync() { WorkQueue.GlobalQueueWorkItemTag(new Action(this.RunOnce), "provider-runonceasync"); } /// /// Executes code as soon as no updater is running. /// public void InterlockedExecute(Delegate action, params object[] args) { this.InterlockedExecute(action, -1, args); } /// /// Executes code as soon as no updater is running. /// public void InterlockedExecute(Delegate action, int timeout, params object[] args) { lock (_busyLock) action.DynamicInvoke(args); } /// /// Waits for the current update process to finish. If an update process is not currently /// running, this function returns immediately. /// public void Wait() { this.Wait(-1); } /// /// Waits for the current update process to finish. If an update process is not currently /// running, this function returns immediately. You may specify a timeout for the wait. /// /// The time in milliseconds to wait for the update process to finish. /// Whether the update process was finished before the timeout. public bool Wait(int timeout) { if (Monitor.TryEnter(_busyLock, timeout)) { Monitor.Exit(_busyLock); return true; } return false; } private void CallEvent(Delegate e, params object[] args) { if (e != null) { try { e.DynamicInvoke(args); } catch (Exception ex) { Logging.Log(ex); } } } protected void OnDictionaryAdded(TValue item) { this.CallEvent(this.DictionaryAdded, item); } protected void OnDictionaryModified(TValue oldItem, TValue newItem) { this.CallEvent(this.DictionaryModified, oldItem, newItem); } protected void OnDictionaryRemoved(TValue item) { this.CallEvent(this.DictionaryRemoved, item); } } }