www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - aa.keys, synchronized and shared

reply torhu <torhu yahoo.com> writes:
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
next sibling parent reply torhu <torhu yahoo.com> writes:
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
parent cc <cc nevernet.com> writes:
On Friday, 11 November 2022 at 01:09:54 UTC, torhu wrote:
 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...
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; } /* ... */ } ```
Nov 10 2022
prev sibling next sibling parent reply Kagamin <spam here.lot> writes:
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
parent reply torhu <torhu yahoo.com> writes:
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
parent reply Kagamin <spam here.lot> writes:
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
parent torhu <torhu yahoo.com> writes:
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
prev sibling parent Kagamin <spam here.lot> writes:
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