digitalmars.D.learn - aa.keys, synchronized and shared
- torhu (57/57) Nov 10 2022 I'm trying to make a more thread-safe wrapper for AA's:
- torhu (5/8) Nov 10 2022 I chose to fix this by just using `synchronized (this)` inside
- cc (91/100) Nov 10 2022 That's about what I ended up with, and just declaring my
- Kagamin (28/28) Nov 11 2022 Try this:
- torhu (13/22) Nov 11 2022 Thanks, that worked! Feels like programming in C, though. If I
- Kagamin (30/30) Nov 11 2022 With allocation:
I'm trying to make a more thread-safe wrapper for AA's: ``` synchronized final class SyncAA(K, V) /// { /// V opIndex(K key) { return data_[key]; } /// V opIndexAssign(V value, K key) { return data_[key] = value; } /// K[] keys() const { return data_.keys; } /// void remove(K key) { data_.remove(key); } /// There is no `in` operator, it would not be thread-safe. V get(K key, lazy V defaultValue=V.init) { auto p = key in data_; return p ? *p : defaultValue; } /// int opApply(scope int delegate(inout ref V) dg) const { int result = 0; foreach (value; data_) { result = dg(value); if (result) break; } return result; } /// int opApply(scope int delegate(K, inout ref V) dg) const { int result = 0; foreach (key, value; data_) { result = dg(key, value); if (result) break; } return result; } private: V[K] data_; } ``` In another file: `__gshared serverListCache = new SyncAA!(string, ServerList);` I'm using `keys` in a regular function, this is the error i get: C:\prog\dmd\windows\bin\..\..\src\druntime\import\object.d(3245,36): Error: cannot implicitly convert expression `aa` of type `shared(const(ServerList[string]))` to `const(shared(ServerList)[string])` src\syncaa.d(17,36): Error: template instance `object.keys!(shared(const(ServerList[string])), shared(const(ServerList)), string)` error instantiating src\serveractions.d(45,33): instantiated from here: `SyncAA!(string, ServerList)` src\serveractions.d(50,17): Error: `shared` `const` method `syncaa.SyncAA!(string, ServerList).SyncAA.keys` is not callable using a non-shared mutable object Error C:\prog\dmd\windows\bin\dmd.exe failed with exit code 1.
Nov 10 2022
On Thursday, 10 November 2022 at 21:55:26 UTC, torhu wrote:I'm trying to make a more thread-safe wrapper for AA's: ``` synchronized final class SyncAA(K, V) ///I chose to fix this by just using `synchronized (this)` inside each method instead, for now. Still interested in cleaner solutions, but I guess synchronized/shared is a bit of a rabbit hole...
Nov 10 2022
On Friday, 11 November 2022 at 01:09:54 UTC, torhu wrote:On Thursday, 10 November 2022 at 21:55:26 UTC, torhu wrote:That's about what I ended up with, and just declaring my references __gshared. I don't know what's going on with shared/synchronized but it sounds like it's unfinished and people can't agree on what they're actually supposed to mean. __gshared, it just works. Previous thread: [synchronized/shared associative array .require error](https://forum.dlang.org/post/hcbrgpmdufjgjtxtuoju forum.dlang.org) Also: [synchronized - shared but actually useful](https://forum.dlang.org/thread/drrlgymevccozrqmsxle forum.dlang.org) ```d class SyncTable(KEY, VAL) { private VAL[KEY] table; auto opIndexAssign(VAL value, KEY key) { synchronized(this) { return table[key] = value; } } int opApply(int delegate(ref KEY, ref VAL) dg) { synchronized(this) { foreach (key, val; table) { if (dg(key, val)) return 1; } return 0; } } auto opBinaryRight(string op)(KEY key) if (op == "in") { synchronized(this) { return key in table; } } auto opDispatch(string s, SA...)(SA sargs) { synchronized(this) { static if (SA.length == 0) { mixin(format("return table.%s;", s)); } else { mixin(format("return table.%s(%s);", s, sargs.stringof[6 .. $-1])); // tuple(_param_0) } } } } ``` With synchronized on the class, I had to do something like: ```d synchronized class SyncTable(KEY, VAL) { // Anything that mutates must reassign unshared back to table! auto opIndexAssign(VAL value, KEY key) { auto unshared = cast(T) table; unshared[key] = value; table = cast(shared) unshared; return value; } auto require(KEY key) { auto unshared = cast(T) table; auto r = unshared.require(key); table = cast(shared) unshared; return r; } /* ... */ } ``` and it just doesn't feel right. For mutexes without synchronized, there's also: ```d struct Lock { private shared Mutex _mtx; this(shared Mutex mtx) { _mtx = mtx; _mtx.lock(); } this(this) disable; ~this() { if (_mtx) _mtx.unlock(); _mtx = null; } } class SyncTable(KEY, VAL) { private VAL[KEY] table; shared Mutex mtx; this() { mtx = new shared Mutex; } auto opIndexAssign(VAL value, KEY key) { auto lock = Lock(mtx); return table[key] = value; } /* ... */ } ```I'm trying to make a more thread-safe wrapper for AA's: ``` synchronized final class SyncAA(K, V) ///I chose to fix this by just using `synchronized (this)` inside each method instead, for now. Still interested in cleaner solutions, but I guess synchronized/shared is a bit of a rabbit hole...
Nov 10 2022
Try this: ``` synchronized final class SyncAA(K, V) { V opIndex(K key) { return sharedTable[key]; } V opIndexAssign(V value, K key) { return sharedTable[key]=value; } const(K[]) keys() const { return unsharedTable.keys; } void remove(K key) { sharedTable.remove(key); } V get(K key, lazy V defaultValue=V.init) { auto p = key in sharedTable; return p ? *p : defaultValue; } private: V[K] sharedTable; ref inout(V[K]) unsharedTable() inout { return *cast(inout(V[K])*)&sharedTable; } } void f(shared SyncAA!(string,string) a) { a.keys(); a["12"]="34"; a.remove("12"); } ```
Nov 11 2022
On Friday, 11 November 2022 at 14:19:31 UTC, Kagamin wrote:Try this: ```private: V[K] sharedTable; ref inout(V[K]) unsharedTable() inout { return *cast(inout(V[K])*)&sharedTable; } ```Thanks, that worked! Feels like programming in C, though. If I could figure out how to initialize the AA explicitly, I could also remove the ref here. If I just remove the ref, the AA is always null. If I try to initialize it in the constructor, I get this: src\syncaa.d(11,5): Error: `_d_monitorenter` cannot be interpreted at compile time, because it has no available source code No idea why, it seems to happen if I try to use the AA in the constructor at all. Even when I just do `data_.remove(K.init);` I also tried DMD 2.101.0-rc.1, using the new `new V[K]` syntax, same error there.
Nov 11 2022
This works for me: ``` synchronized final class SyncAA(K, V) { this(K key, V val) { sharedTable[key]=val; } V opIndex(K key) { return sharedTable[key]; } V opIndexAssign(V value, K key) { return sharedTable[key]=value; } const(K[]) keys() const { return unsharedTable.keys; } void remove(K key) { sharedTable.remove(key); } V get(K key, lazy V defaultValue=V.init) { auto p = key in sharedTable; return p ? *p : defaultValue; } private: V[K] sharedTable; inout(V[K]) unsharedTable() inout { return cast(inout(V[K]))sharedTable; } } shared SyncAA!(string,string) saa; void f() { saa=new shared SyncAA!(string,string)("1","2"); saa.keys(); saa["12"]="34"; saa.remove("12"); } ```
Nov 13 2022
On Monday, 14 November 2022 at 07:57:16 UTC, Kagamin wrote:This works for me: ``` shared SyncAA!(string,string) saa; void f() { saa=new shared SyncAA!(string,string)("1","2"); saa.keys(); saa["12"]="34"; saa.remove("12"); } ```The strange error message I got was because I initialized the variable at module level, that doesn't work when you have a constructor. It worked when I moved it into a module constructor.
Nov 14 2022
With allocation: ``` synchronized final class SyncAA(K, V) { V opIndex(K key) { return sharedTable[key]; } V opIndexAssign(V value, K key) { return sharedTable[key]=value; } const(K[]) keys() const { return unsharedTable.keys; } void remove(K key) { sharedTable.remove(key); } V get(K key, lazy V defaultValue=V.init) { auto p = key in sharedTable; return p ? *p : defaultValue; } private: V[K] sharedTable; ref inout(V[K]) unsharedTable() inout { return *cast(inout(V[K])*)&sharedTable; } } shared SyncAA!(string,string) saa; void f() { saa=new shared SyncAA!(string,string); saa.keys(); saa["12"]="34"; saa.remove("12"); } ```
Nov 11 2022