On Monday, 1 January 2024 at 19:49:28 UTC, Jonathan M Davis wrote:
[...]
Thank you. Yes, `Foo` is a class for the purposes of inheritance
-- I left that out of the example.
So a completely valid solution is to write a struct wrapper
around an AA of the type I need, overload the required operators,
and then just drop-in replace the current AA? All array
operations would then transparently be between lock and unlock
statements.
```d
struct MutexedAA(AA : V[K], V, K)
{
import core.sync.mutex : Mutex;
shared Mutex mutex;
shared AA aa;
void setup() nothrow
{
mutex = new shared Mutex;
mutex.lock_nothrow();
if (K.init !in (cast()aa))
{
(cast()aa)[K.init] = V.init;
(cast()aa).remove(K.init);
}
mutex.unlock_nothrow();
}
auto opIndexAssign(V value, K key)
in (mutex, typeof(this).stringof ~ " has null Mutex")
{
mutex.lock_nothrow();
(cast()aa)[key] = value;
mutex.unlock_nothrow();
return value;
}
auto opIndex(K key)
in (mutex, typeof(this).stringof ~ " has null Mutex")
{
mutex.lock_nothrow();
auto value = (cast()aa)[key];
mutex.unlock_nothrow();
return value;
}
auto opBinaryRight(string op : "in")(K key)
in (mutex, typeof(this).stringof ~ " has null Mutex")
{
mutex.lock_nothrow();
auto value = key in cast()aa;
mutex.unlock_nothrow();
return value;
}
auto remove(K key)
in (mutex, typeof(this).stringof ~ " has null Mutex")
{
mutex.lock_nothrow();
auto value = (cast()aa).remove(key);
mutex.unlock_nothrow();
return value;
}
auto opEquals()(auto ref typeof(this) other)
in (mutex, typeof(this).stringof ~ " has null Mutex")
{
mutex.lock_nothrow();
auto isEqual = (cast()aa == cast()(other.aa));
mutex.unlock_nothrow();
return isEqual;
}
auto opEquals()(auto ref AA other)
in (mutex, typeof(this).stringof ~ " has null Mutex")
{
mutex.lock_nothrow();
auto isEqual = (cast()aa == other);
mutex.unlock_nothrow();
return isEqual;
}
}
```
(https://gist.github.com/zorael/433c50f238b21b9bb68d076d8a495045)
I tried this and it seems to work. Is it glaringly incorrect
somehow, or am I free to roll with this?
You mention passing a `shared Foo*`. In the gist I pass the
instance of the `MutexedAA!(string[int])` to the worker thread
*by value* instead of as something `shared`, since I couldn't get
operator overloading to work when `shared`. (Calling
`sharedAA.opIndexAssign("hello", 42)` worked, but `sharedAA[42] =
"hello"` wouldn't compile.)
I guess this can break synchronisation between the two if I
replace the `Mutex` in either thread. Are there any other obvious
caveats?