diff --git a/trunk/ProcessHacker.Native/Api/NativeDefinitions.cs b/trunk/ProcessHacker.Native/Api/NativeDefinitions.cs index 8c1ba9b64..5f2dbff91 100644 --- a/trunk/ProcessHacker.Native/Api/NativeDefinitions.cs +++ b/trunk/ProcessHacker.Native/Api/NativeDefinitions.cs @@ -39,6 +39,7 @@ namespace ProcessHacker.Native.Api public const int GdiHandleBufferSize = 34; #endif public const int MaximumSupportedExtension = 512; + public const int MaximumWaitObjects = 64; public const int SecurityDescriptorMinLength = 20; public const int SecurityDescriptorRevision = 1; public static readonly int SecurityMaxSidSize = diff --git a/trunk/ProcessHacker.Native/ProcessHacker.Native.csproj b/trunk/ProcessHacker.Native/ProcessHacker.Native.csproj index 058deeb7a..1c5cb68e9 100644 --- a/trunk/ProcessHacker.Native/ProcessHacker.Native.csproj +++ b/trunk/ProcessHacker.Native/ProcessHacker.Native.csproj @@ -142,6 +142,7 @@ + Form diff --git a/trunk/ProcessHacker.Native/Security/ObjectSecurityInformation.cs b/trunk/ProcessHacker.Native/Security/ObjectSecurityInformation.cs index 23fcc99a7..bb11c3f9e 100644 --- a/trunk/ProcessHacker.Native/Security/ObjectSecurityInformation.cs +++ b/trunk/ProcessHacker.Native/Security/ObjectSecurityInformation.cs @@ -74,6 +74,7 @@ namespace ProcessHacker.Native.Security private void ClearPool() { _pool.ForEach((alloc) => alloc.Dispose()); + _pool.Clear(); } #region ISecurityInformation Members diff --git a/trunk/ProcessHacker.Native/Threading/WaiterThread.cs b/trunk/ProcessHacker.Native/Threading/WaiterThread.cs new file mode 100644 index 000000000..0b86efa8b --- /dev/null +++ b/trunk/ProcessHacker.Native/Threading/WaiterThread.cs @@ -0,0 +1,226 @@ +/* + * 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."; + } + } + } +}