/**************************************************************************** ** ** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt VS Tools. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ using System; using System.Runtime.Serialization; using System.Threading; namespace QtVsTools { /// /// Base class of objects requiring thread-safety features /// /// [DataContract] public abstract class Concurrent where TSubClass : Concurrent { private static readonly object _StaticCriticalSection = new object(); protected static object StaticCriticalSection => _StaticCriticalSection; private object _CriticalSection = null; protected object CriticalSection => StaticThreadSafeInit(() => _CriticalSection, () => _CriticalSection = new object()); protected T ThreadSafeInit(Func getValue, Action init) where T : class { return StaticThreadSafeInit(getValue, init, this); } protected static T StaticThreadSafeInit( Func getValue, Action init, Concurrent _this = null) where T : class { // prevent global lock at every call T value = getValue(); if (value != null) return value; lock (_this?.CriticalSection ?? StaticCriticalSection) { // prevent race conditions value = getValue(); if (value == null) { init(); value = getValue(); } return value; } } protected void EnterCriticalSection() { StaticEnterCriticalSection(this); } protected static void StaticEnterCriticalSection(Concurrent _this = null) { Monitor.Enter(_this?.CriticalSection ?? StaticCriticalSection); } protected void LeaveCriticalSection() { StaticLeaveCriticalSection(this); } protected static void StaticLeaveCriticalSection(Concurrent _this = null) { if (Monitor.IsEntered(_this?.CriticalSection ?? StaticCriticalSection)) Monitor.Exit(_this?.CriticalSection ?? StaticCriticalSection); } protected void AbortCriticalSection() { StaticAbortCriticalSection(this); } protected static void StaticAbortCriticalSection(Concurrent _this = null) { while (Monitor.IsEntered(_this?.CriticalSection ?? StaticCriticalSection)) Monitor.Exit(_this?.CriticalSection ?? StaticCriticalSection); } protected void ThreadSafe(Action action) { StaticThreadSafe(action, this); } protected static void StaticThreadSafe(Action action, Concurrent _this = null) { lock (_this?.CriticalSection ?? StaticCriticalSection) { action(); } } protected T ThreadSafe(Func func) { return StaticThreadSafe(func, this); } protected static T StaticThreadSafe(Func func, Concurrent _this = null) { lock (_this?.CriticalSection ?? StaticCriticalSection) { return func(); } } protected bool Atomic(Func test, Action action) { return StaticAtomic(test, action, _this: this); } protected bool Atomic(Func test, Action action, Action actionElse) { return StaticAtomic(test, action, actionElse, this); } protected static bool StaticAtomic( Func test, Action action, Action actionElse = null, Concurrent _this = null) { bool success = false; lock (_this?.CriticalSection ?? StaticCriticalSection) { success = test(); if (success) action(); else if (actionElse != null) actionElse(); } return success; } } /// /// Base class of objects requiring thread-safety features /// Sub-classes will share the same static critical section /// /// [DataContract] public class Concurrent : Concurrent { } /// /// Allows exclusive access to a wrapped variable. Reading access is always allowed. Concurrent /// write requests are protected by mutex: only the first requesting thread will be granted /// access; all other requests will be blocked until the value is reset (i.e. thread with /// write access sets the variable's default value). /// /// Type of wrapped variable /// class Exclusive : Concurrent { private T value; public void Set(T newValue) { EnterCriticalSection(); if (IsNull(value) && !IsNull(newValue)) { value = newValue; } else if (!IsNull(value) && !IsNull(newValue)) { value = newValue; LeaveCriticalSection(); } else if (!IsNull(value) && IsNull(newValue)) { value = default(T); LeaveCriticalSection(); LeaveCriticalSection(); } else { LeaveCriticalSection(); } } bool IsNull(T value) { if (typeof(T).IsValueType) return value.Equals(default(T)); else return value == null; } public void Release() { Set(default(T)); } public static implicit operator T(Exclusive _this) { return _this.value; } } }