/*
* Process Hacker -
* provider host 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.Collections.ObjectModel;
using System.Threading;
using ProcessHacker.Common;
using ProcessHacker.Common.Objects;
using ProcessHacker.Common.Threading;
using ProcessHacker.Native.Api;
using ProcessHacker.Native.Objects;
using ProcessHacker.Native.Security;
// This code is partially back-ported from PH 2.x.
namespace ProcessHacker
{
public class ProviderThread : BaseObject, IEnumerable
{
private LinkedListEntry _listHead = new LinkedListEntry();
private int _boostCount = 0;
private int _count;
private Thread _thread;
private ThreadHandle _threadHandle;
private FastEvent _initializedEvent = new FastEvent(false);
private bool _terminating = false;
private int _interval;
private TimerHandle _timerHandle;
public ProviderThread(int interval)
{
LinkedList.InitializeListHead(_listHead);
_timerHandle = TimerHandle.Create(TimerAccess.All, TimerType.SynchronizationTimer);
this.Interval = interval;
_thread = new Thread(new ThreadStart(this.Update), ProcessHacker.Common.Utils.QuarterStackSize);
_thread.IsBackground = true;
_thread.SetApartmentState(ApartmentState.STA);
_thread.Start();
_thread.Priority = ThreadPriority.Lowest;
_initializedEvent.Wait();
}
protected override void DisposeObject(bool disposing)
{
_terminating = true;
_threadHandle.Alert();
_threadHandle.Wait();
_threadHandle.Dispose();
_threadHandle = null;
_thread = null;
_timerHandle.Dispose();
}
public int Count
{
get { return _count; }
}
public int Interval
{
get { return _interval; }
set
{
if (_interval != value)
{
_interval = value;
_timerHandle.Set(_interval * Win32.TimeMsTo100Ns, _interval);
}
}
}
public void Add(IProvider provider)
{
provider.Owner = this;
provider.Unregistering = false;
provider.Boosting = false;
lock (_listHead)
{
LinkedList.InsertTailList(_listHead, provider.ListEntry);
_count++;
}
}
public void Boost(IProvider provider)
{
if (provider.Unregistering)
return;
lock (_listHead)
{
LinkedList.RemoveEntryList(provider.ListEntry);
LinkedList.InsertHeadList(_listHead, provider.ListEntry);
provider.Boosting = true;
_boostCount++;
}
// Wake up the thread.
_threadHandle.Alert();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
public IEnumerator GetEnumerator()
{
for (
LinkedListEntry entry = _listHead.Flink;
entry != _listHead;
entry = entry.Flink
)
{
yield return entry.Value;
}
}
public void Remove(IProvider provider)
{
provider.Unregistering = false;
lock (_listHead)
{
LinkedList.RemoveEntryList(provider.ListEntry);
// Fix the boost count.
if (provider.Boosting)
_boostCount--;
_count--;
}
}
private void Update()
{
LinkedListEntry tempListHead = new LinkedListEntry();
LinkedListEntry listEntry;
NtStatus status = NtStatus.Success;
_threadHandle = ThreadHandle.OpenCurrent(
ThreadAccess.Alert | (ThreadAccess)StandardRights.Synchronize
);
_initializedEvent.Set();
while (!_terminating)
{
LinkedList.InitializeListHead(tempListHead);
Monitor.Enter(_listHead);
// Main loop.
while (true)
{
if (status == NtStatus.Alerted)
{
// Check if we have any more providers to boost.
if (_boostCount == 0)
break;
}
listEntry = LinkedList.RemoveHeadList(_listHead);
if (listEntry == _listHead)
break;
// Add the provider to the temp list.
LinkedList.InsertTailList(tempListHead, listEntry);
if (status != NtStatus.Alerted)
{
if (!listEntry.Value.Enabled || listEntry.Value.Unregistering)
continue;
}
else
{
if (listEntry.Value.Unregistering)
{
// Give the unregistering thread a chance to fix
// the boost count.
Monitor.Exit(_listHead);
Monitor.Enter(_listHead);
continue;
}
}
if (status == NtStatus.Alerted)
{
listEntry.Value.Boosting = false;
_boostCount--;
}
Monitor.Exit(_listHead);
try
{
listEntry.Value.Run();
}
finally
{
Monitor.Enter(_listHead);
}
}
// Re-add the items in the temp list to the main list.
while ((listEntry = LinkedList.RemoveHeadList(tempListHead)) != tempListHead)
LinkedList.InsertTailList(_listHead, listEntry);
Monitor.Exit(_listHead);
// Wait for the interval. We may get alerted.
status = _timerHandle.Wait(true);
}
}
}
}