AA vs __gshared

IchorDev zxinsworld at gmail.com
Fri Jul 28 08:39:37 UTC 2023


On Thursday, 27 July 2023 at 21:31:02 UTC, Jonathan M Davis wrote:
> What should normally be happening is that you use shared, and 
> then when you've protected the object so that you know that it 
> can only be accessed on the current thread by the section of 
> code that you're in (e.g. by locking a mutex), you temporarily 
> cast away shared to operate on the object via a thread-local 
> reference. Then, before exiting that section of code and 
> removing the protections that are preventing other threads from 
> accessing the object (e.g. by unlocking the mutex), you make 
> sure that you've gotten rid of all of the thread-local 
> references to the object so that only the shared reference 
> exists. That way, you don't accidentally mutate the object 
> while it's not protected from access by other threads.

Doing this doesn't help, unfortunately.
This is an abstract version what the problematic code looks like 
now:
```d
import std.stdio: writeln;
import core.sync.mutex;
import core.thread;
import core.time;
static import core.stdc.stdlib;

struct Pos{
	long x,y;
}

shared Obj obj;

final class Obj{
	CacheData[Pos] cache;
	CacheData* getCache(Pos key){
		if(auto item = key in cache){
			if(++item.useCount >= 8){
				cache.remove(key);
				//I thought this ^ might cause a use-after-free issue, but 
apparently not.
			}
			return item;
		}
		return null;
	}
	struct CacheData{
		double[1000] data;
		uint useCount = 1;
	}
	
	double[1000] doStuff(Pos pos){
		immutable data = (){
			if(auto data = getCache(pos)){
				return *data;
			}else{
				double[1000] data;
				//initialisses it with something, this is arbirray though:
				foreach(ref item; data){
					import std.random;
					item = uniform01();
				}
				cache[pos] = CacheData(data);
				return CacheData(data);
			}
		}();
		
		//do stuff with data
		
		return data.data;
	}	
}

__gshared OtherObj otherObj;

final class OtherObj{
	shared Mutex mutex;
	__gshared ubyte[2^^18] data;
	
	this(long n){
		obj = cast(shared Obj)alloc!Obj(n);
		mutex = new shared Mutex;
	}
	
	void callObj(Pos pos){
		double[1000] data;
		
		{
			auto objRef = cast(Obj)obj;
			data = objRef.doStuff(pos);
		}
		
		//do things with returned value...
	}
}

void thread(){
	bool run = true;
	Pos pos;
	while(run){
		otherObj.mutex.lock();
		foreach(i; 0..100){
			otherObj.callObj(pos);
			//this is pretty arbirary:
			import std.random;
			if(uniform01() > 0.9){
				auto v = uniform01();
				if(v < 0.25) pos.x--;
				else if(v < 0.5) pos.y++;
				else if(v < 0.75) pos.y--;
				else pos.x++;
			}
		}
		otherObj.mutex.unlock();
		
		Thread.sleep(1.seconds / 20);
	}
}

void main(){
	otherObj = alloc!OtherObj(-7);
	assert(otherObj !is null);
	
	auto otherThread = new Thread(&thread);
	otherThread.start();
	
	bool run = true;
	while(run){
		if(!otherThread.isRunning()) otherThread.join();
		otherObj.mutex.lock();
		foreach(val; otherObj.data){
			//do stuff
		}
		otherObj.mutex.unlock();
		Thread.sleep(1.seconds / 80);
	}
}

T alloc(T, A...)(auto ref A args){
	enum classSize = __traits(classInstanceSize, T);
	void* mem = core.stdc.stdlib.malloc(classSize);
	scope(failure) core.stdc.stdlib.free(mem);
	assert(mem !is null, "Out of memory");
	mem[0..classSize] = __traits(initSymbol, T);
	T inst = cast(T)mem;
	static if(__traits(hasMember, T, "__ctor")){
		
		inst.__ctor(__traits(parameters));
	}
	return inst;
}
```
Issue is, this code I posted actually runs just fine, unlike the 
real code.
My actual code does this HIGHLY SUSPICIOUS thing when printing 
their length each time before using them:
```
766
766
765
766
767
768
768
768
768
768
768
768
768
768
768
768
768
768
768
128000
Error Program exited with code -11
(backtrace:)
#0  0x0000555555670c46 in rt.aaA.Impl.findSlotLookup(ulong, scope 
const(void*), scope const(TypeInfo)) inout ()
#1  0x0000555555661592 in _aaInX ()
```
Therefore I must logically conclude that DRuntime's AAs are 
cursed! Unless this is a well-known issue or something...
Thinking back, I've actually had them cause segfaults in 
non-threaded code, maybe `__gshared` was never the cause at all.


More information about the Digitalmars-d-learn mailing list