/* * Process Hacker - * rundown protection * * 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.Threading; namespace ProcessHacker.Common.Threading { /// /// Provides methods for managing object/resource destruction. /// public struct RundownProtection { private const int RundownActive = 0x1; private const int RundownCountShift = 1; private const int RundownCountIncrement = 0x2; private int _value; private FastEvent _wakeEvent; /// /// Initializes a rundown protection structure. /// /// The initial usage count. public RundownProtection(int value) { _value = RundownCountIncrement * value; _wakeEvent = new FastEvent(false); } /// /// Gets the current usage count. /// public int Count { get { return _value >> RundownCountShift; } } /// /// Gets whether the rundown has been initiated. /// public bool Rundown { get { return (_value & RundownActive) != 0; } } /// /// Attempts to acquire rundown protection. /// /// Whether rundown protection was acquired. public bool Acquire() { int value; while (true) { value = _value; // Has the rundown already started? if ((value & RundownActive) != 0) return false; // Attempt to increment the usage count. if (Interlocked.CompareExchange( ref _value, value + RundownCountIncrement, value ) == value) return true; } } /// /// Attempts to acquire rundown protection. /// /// The usage count to add. /// Whether rundown protection was acquired. public bool Acquire(int count) { int value; while (true) { value = _value; // Has the rundown already started? if ((value & RundownActive) != 0) return false; // Attempt to increase the usage count. if (Interlocked.CompareExchange( ref _value, value + RundownCountIncrement * count, value ) == value) return true; } } /// /// Releases rundown protection. /// public void Release() { int value; while (true) { value = _value; // Has the rundown already started? if ((value & RundownActive) != 0) { int newValue; newValue = Interlocked.Add(ref _value, -RundownCountIncrement); // Are we the last out? Set the event if that's the case. if (newValue == RundownActive) { _wakeEvent.Set(); } return; } else { if (Interlocked.CompareExchange( ref _value, value - RundownCountIncrement, value ) == value) return; } } } /// /// Releases rundown protection. /// /// The usage count to subtract. public void Release(int count) { int value; while (true) { value = _value; // Has the rundown already started? if ((value & RundownActive) != 0) { int newValue; newValue = Interlocked.Add(ref _value, -RundownCountIncrement * count); // Are we the last out? Set the event if that's the case. if (newValue == RundownActive) { _wakeEvent.Set(); } return; } else { if (Interlocked.CompareExchange( ref _value, value - RundownCountIncrement * count, value ) == value) return; } } } /// /// Waits for all references to be released while disallowing /// attempts to acquire rundown protection. /// public void Wait() { this.Wait(Timeout.Infinite); } /// /// Waits for all references to be released while disallowing /// attempts to acquire rundown protection. /// /// The timeout, in milliseconds. /// Whether all references were released. public bool Wait(int millisecondsTimeout) { int value; // Fast path. Just in case there are no users, we can go ahead // and set the active flag and exit. Or if someone has already // initiated the rundown, exit as well. value = Interlocked.CompareExchange(ref _value, RundownActive, 0); if (value == 0 || value == RundownActive) return true; // Set the rundown active flag. do { value = _value; } while (Interlocked.CompareExchange( ref _value, value | RundownActive, value ) != value); // Wait for the event, but only if we had users. if ((value & ~RundownActive) != 0) return _wakeEvent.Wait(millisecondsTimeout); else return true; } } }