Member variables in method are null when called as delegate from thread

tsbockman thomas.bockman at gmail.com
Wed Jan 13 05:22:16 UTC 2021


On Wednesday, 13 January 2021 at 02:15:49 UTC, Tim wrote:
> Basically, the program calls a function which modifies a 
> document in the database. If it is called form it's own class' 
> constructor, it works fine. If it is called by a thread, it 
> never returns.
> ...
> class Caller : Thread{
>     void delegate() mFunc;
>
>     this(void delegate() func){
>         mFunc = func;
>         super(&loop);
>         start();
>     }
>
>     void loop(){
>         while(true){
>             mFunc();
>         }
>     }
> }
>
> class Callable{
>     MongoClient db;
>     Caller caller;
>
>     this(){
>         db = connectMongoDB("127.0.0.1");
>         foo();
>         caller = new Caller(&foo);
>     }
>
>     ~this(){
> 	db.cleanupConnections();
>     }
>
>     void foo(){
>         writeln("Started");
>         auto result = 
> db.getCollection("test.collection").findAndModify([
> 	    "state": "running"],
> 	    ["$set": ["state": "stopped"]
> 	]);
>         writeln(result);
>         writeln("Finished");
>     }
> }

Note that if you are trying to debug a crash or hang of a program 
by printing messages to the console, you need to flush stdout 
multiple times in the vicinity of the problem, otherwise stdio's 
buffering may make it appear as though the program crashed or 
hung significantly earlier than it really did. (This is not a 
merely theoretical problem; I trip over it frequently myself.)

Anyway, I think your real problem is that MongoClient is not 
thread-safe. From the official vibe.d documentation 
(https://vibed.org/api/vibe.db.mongo.mongo/connectMongoDB):

> Note that the returned MongoClient uses a 
> vibe.core.connectionpool.ConnectionPool internally to create 
> and reuse connections as necessary. Thus, the MongoClient 
> instance can - and should - be shared among all fibers in a 
> thread by storing in in a thread local variable.

Note the "in a thread" part; you may only use a connection from 
the same thread that opened it.

(Why? I'm not familiar with vibe.d's API or code base, so I don't 
really know. But, I'd guess that the connection reuse mechanism 
mentioned in the docs requires some of the information that you 
might expect to be stored in the MongoClient instance itself to 
instead end up in thread-local storage (whether native or 
emulated). Or, there may simply be a manual "same thread" check 
built into the DB operations to prevent data races, and the error 
message isn't reaching you for some reason. `assert` messages 
don't print in release mode, and I've found the gdb debugger for 
D quite unreliable when trying to inspect multi-threaded code.)

Try moving the calls to `connectMongoDB` and `cleanupConnections` 
into the same thread as `foo`. (I can't think of a good reason to 
be doing these in the original thread, other than convenience.) 
If you want to loop in multiple threads simultaneously, just open 
a separate connection per thread, like the vibe.d docs suggest.


More information about the Digitalmars-d-learn mailing list