aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMiguel Costa <miguel.costa@qt.io>2021-05-12 13:26:18 +0200
committerMiguel Costa <miguel.costa@qt.io>2021-06-02 09:01:35 +0000
commit25422f0fde85c050d448c3ba0080522bf2c84140 (patch)
tree6f40be5f223a0a6a35bf4bc9d7b5da68b3826709 /src
parentdd68189d9ef0167c84d68b01d47d7f82c9d4240c (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.cs114
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