diff options
author | Miguel Costa <miguel.costa@qt.io> | 2021-05-12 13:26:18 +0200 |
---|---|---|
committer | Miguel Costa <miguel.costa@qt.io> | 2021-06-02 09:01:35 +0000 |
commit | 25422f0fde85c050d448c3ba0080522bf2c84140 (patch) | |
tree | 6f40be5f223a0a6a35bf4bc9d7b5da68b3826709 /src | |
parent | dd68189d9ef0167c84d68b01d47d7f82c9d4240c (diff) |
Refactor Concurrent class
The following changes were made to the Concurrent class:
* Class visibility is now public.
* Sub-class type parameter allows dedicated static critical section
for each sub-class instead of sharing the same critical section across
all sub-classes.
* All functions now have a static version, which uses the static
critical section for synchronization.
* New function ThreadSafeInit allows thread-safe lazy initialization of
ref type properties.
Change-Id: I7a16e8d1be2944ee3d63b8922c8ae4e5c57ae9e5
Reviewed-by: Oliver Wolff <oliver.wolff@qt.io>
Diffstat (limited to 'src')
-rw-r--r-- | src/qtvstools/Common/Concurrent.cs | 114 |
1 files changed, 91 insertions, 23 deletions
diff --git a/src/qtvstools/Common/Concurrent.cs b/src/qtvstools/Common/Concurrent.cs index f35559fa..9c395af4 100644 --- a/src/qtvstools/Common/Concurrent.cs +++ b/src/qtvstools/Common/Concurrent.cs @@ -37,58 +37,117 @@ namespace QtVsTools /// </summary> /// [DataContract] - abstract class Concurrent + public abstract class Concurrent<TSubClass> + where TSubClass : Concurrent<TSubClass> { - private readonly static object criticalSectionGlobal = new object(); - private object criticalSection = null; - - protected object CriticalSection - { - get - { - if (criticalSection == null) { // prevent global lock at every call - lock (criticalSectionGlobal) { - if (criticalSection == null) // prevent race conditions - criticalSection = new object(); - } + 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<T>(Func<T> getValue, Action init) + where T : class + { + return StaticThreadSafeInit(getValue, init, this); + } + + protected static T StaticThreadSafeInit<T>( + Func<T> getValue, + Action init, + Concurrent<TSubClass> _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 criticalSection; + return value; } } protected void EnterCriticalSection() { - Monitor.Enter(CriticalSection); + StaticEnterCriticalSection(this); + } + + protected static void StaticEnterCriticalSection(Concurrent<TSubClass> _this = null) + { + Monitor.Enter(_this?.CriticalSection ?? StaticCriticalSection); } protected void LeaveCriticalSection() { - if (Monitor.IsEntered(CriticalSection)) - Monitor.Exit(CriticalSection); + StaticLeaveCriticalSection(this); + } + + protected static void StaticLeaveCriticalSection(Concurrent<TSubClass> _this = null) + { + if (Monitor.IsEntered(_this?.CriticalSection ?? StaticCriticalSection)) + Monitor.Exit(_this?.CriticalSection ?? StaticCriticalSection); } protected void AbortCriticalSection() { - while (Monitor.IsEntered(CriticalSection)) - Monitor.Exit(CriticalSection); + StaticAbortCriticalSection(this); + } + + protected static void StaticAbortCriticalSection(Concurrent<TSubClass> _this = null) + { + while (Monitor.IsEntered(_this?.CriticalSection ?? StaticCriticalSection)) + Monitor.Exit(_this?.CriticalSection ?? StaticCriticalSection); } protected void ThreadSafe(Action action) { - lock (CriticalSection) + StaticThreadSafe(action, this); + } + + protected static void StaticThreadSafe(Action action, Concurrent<TSubClass> _this = null) + { + lock (_this?.CriticalSection ?? StaticCriticalSection) { action(); + } } protected T ThreadSafe<T>(Func<T> func) { - lock (CriticalSection) + return StaticThreadSafe(func, this); + } + + protected static T StaticThreadSafe<T>(Func<T> func, Concurrent<TSubClass> _this = null) + { + lock (_this?.CriticalSection ?? StaticCriticalSection) { return func(); + } + } + + protected bool Atomic(Func<bool> test, Action action) + { + return StaticAtomic(test, action, _this: this); + } + + protected bool Atomic(Func<bool> test, Action action, Action actionElse) + { + return StaticAtomic(test, action, actionElse, this); } - protected bool Atomic(Func<bool> test, Action action, Action actionElse = null) + protected static bool StaticAtomic( + Func<bool> test, + Action action, + Action actionElse = null, + Concurrent<TSubClass> _this = null) { bool success = false; - lock (CriticalSection) { + lock (_this?.CriticalSection ?? StaticCriticalSection) { success = test(); if (success) action(); @@ -100,6 +159,15 @@ namespace QtVsTools } /// <summary> + /// Base class of objects requiring thread-safety features + /// Sub-classes will share the same static critical section + /// </summary> + /// + public class Concurrent : Concurrent<Concurrent> + { + } + + /// <summary> /// 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 |