/* * Process Hacker - * waiter thread * * Copyright (C) 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.Text; using System.Threading; using ProcessHacker.Native.Api; using ProcessHacker.Native.Objects; using ProcessHacker.Native.Security; namespace ProcessHacker.Native.Threading { public delegate void ObjectSignaledDelegate(ISynchronizable obj); public class WaiterThread : IDisposable { private enum WaiterThreadMessageType { AddObject, RemoveObject } private class WaiterThreadMessage { private WaiterThreadMessageType _type; private object _param; public WaiterThreadMessage(WaiterThreadMessageType type) : this(type, null) { } public WaiterThreadMessage(WaiterThreadMessageType type, object param) { _type = type; _param = param; } public WaiterThreadMessageType Type { get { return _type; } } public object Param { get { return _param; } } } /// /// Raised when an object is signaled. /// public event ObjectSignaledDelegate ObjectSignaled; private bool _disposed = false; private bool _terminating = false; private object _disposeLock = new object(); private Thread _waiterThread; private List _waitObjects = new List(); private Queue _waitMessageQueue = new Queue(); private Event _waitMessageEvent = new Event(true, false); /// /// Creates a waiter thread. /// public WaiterThread() { _waiterThread = new Thread(this.WaiterThreadStart); _waiterThread.IsBackground = true; _waiterThread.SetApartmentState(ApartmentState.STA); _waiterThread.Start(); } ~WaiterThread() { this.Dispose(false); } /// /// Releases resources used by the waiter thread. /// public void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool disposing) { if (disposing) Monitor.Enter(_disposeLock); try { if (!_disposed) { // Tell the waiter thread to terminate. _terminating = true; _waitMessageEvent.Set(); _disposed = true; } } finally { Monitor.Exit(_disposeLock); } } /// /// Adds an object for the waiter thread to wait on. /// /// The object to wait for. public void Add(ISynchronizable obj) { lock (_waitMessageQueue) { if (_waitObjects.Count >= Win32.MaximumWaitObjects - 1) throw new TooManyWaitObjectsException(); this.SendWaiterThreadMessage(new WaiterThreadMessage(WaiterThreadMessageType.AddObject, obj)); } } private void OnObjectSignaled(ISynchronizable obj) { if (ObjectSignaled != null) ObjectSignaled(obj); } /// /// Removes an object the waiter thread is waiting on. /// /// An object which is currently being waited on. public void Remove(ISynchronizable obj) { this.SendWaiterThreadMessage(new WaiterThreadMessage(WaiterThreadMessageType.RemoveObject, obj)); } private void SendWaiterThreadMessage(WaiterThreadMessage message) { lock (_waitMessageQueue) { _waitMessageQueue.Enqueue(message); _waitMessageEvent.Set(); } } private void WaiterThreadStart() { ISynchronizable[] waitObjects; while (!_terminating) { waitObjects = new ISynchronizable[_waitObjects.Count + 1]; waitObjects[0] = _waitMessageEvent.Handle; Array.Copy(_waitObjects.ToArray(), 0, waitObjects, 1, _waitObjects.Count); NtStatus waitStatus = NativeHandle.WaitAny(waitObjects); if (waitStatus == NtStatus.Wait0) { // We have a message in the message queue (or we are terminating). WaiterThreadMessage message; // Lock and retrieve the message. lock (_waitMessageQueue) { if (_waitMessageQueue.Count > 0) message = _waitMessageQueue.Dequeue(); else continue; } switch (message.Type) { case WaiterThreadMessageType.AddObject: { _waitObjects.Add((ISynchronizable)message.Param); } break; case WaiterThreadMessageType.RemoveObject: { _waitObjects.Remove((ISynchronizable)message.Param); } break; } } else if (waitStatus > NtStatus.Wait0 && waitStatus <= NtStatus.Wait63) { // One of the objects was signaled. ISynchronizable signaledObject = waitObjects[(int)(waitStatus - NtStatus.Wait0)]; // Remove the object now that it is signaled. _waitObjects.Remove(signaledObject); // Call the object-signaled event. OnObjectSignaled(signaledObject); } } } } public class TooManyWaitObjectsException : Exception { public override string Message { get { return "An attempt was made to add too many objects to the wait list."; } } } }