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