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

Tim t.oliver at windowslive.com
Wed Jan 13 02:15:49 UTC 2021


On Tuesday, 12 January 2021 at 01:49:11 UTC, tsbockman wrote:
> The compiler and the physical CPU are both allowed to change 
> the order in which instructions are executed to something 
> different from what your code specifies, as long as the 
> visible, "official" results and effects of the chosen order of 
> execution are the same as those of your specified code, FROM 
> THE PERSPECTIVE OF THE EXECUTING THREAD.
>
> This is allowed so that the compiler can optimize to minimize 
> negative "unofficial" effects such as the passage of time and 
> memory consumption.
>
> However, this re-ordering IS permitted to freely alter the 
> behavior of your code from the perspective of OTHER threads. A 
> likely cause of your bug is that the write to db by the 
> constructor's thread is being committed to memory after the 
> read of db by the MessageService thread.
>
> In order to RELIABLY fix this kind of problem, you must 
> correctly use the only commands which the compiler and CPU are 
> NOT allowed to reorder with respect to other threads, namely 
> atomic operations, memory barriers and synchronization 
> primitives. A wide selection of these tools may be found in 
> these D runtime library modules:
>
>     core.sync: 
> http://dpldocs.info/experimental-docs/core.sync.html
>     core.atomic: 
> http://dpldocs.info/experimental-docs/core.atomic.html
>     core.thread: 
> http://dpldocs.info/experimental-docs/core.thread.html
>
> (I recommend using Adam D. Ruppe's unofficial but superior 
> rendering of the D runtime documentation at dpldocs.info rather 
> than the official dlang.org rendering, as I found some 
> necessary pieces of the documentation are just mysteriously 
> missing from the offical version.)
>
> Be warned that most models of multi-threaded programming are 
> difficult to implement correctly, as opposed to ALMOST 
> correctly with subtle heisen-bugs. You should either stick to 
> one of the known simple models like immutable message passing 
> with GC, or do some studying before writing too much code.
>
> Here are some resources which I have found very helpful in 
> learning to understand this topic, and to avoid its pitfalls:
>
>     Short educational game: https://deadlockempire.github.io/
>     Tech talk by C++ expert Herb Sutter (D's core.atomic uses 
> the C++ memory model):
>         
> https://channel9.msdn.com/Shows/Going+Deep/Cpp-and-Beyond-2012-Herb-Sutter-atomic-Weapons-1-of-2
>         
> https://channel9.msdn.com/Shows/Going+Deep/Cpp-and-Beyond-2012-Herb-Sutter-atomic-Weapons-2-of-2
>
> If you want to seriously dig into this, I suggest reviewing 
> some or all of the content at the links above. If you're still 
> confused about how to apply it in D, feel free to come back and 
> ask for examples or code reviews. I'd rather not start with 
> examples, though, because if you don't understand the rules and 
> principles behind them, it's really easy to unknowingly 
> introduce bugs into otherwise correct examples with seemingly 
> innocent changes.

Fantastic response, thank you! I did some more digging and 
properly narrowed down where the issue is and created a test 
script that demonstrates the problem. Let me know what you think 
and if it could still be a similar problem to what you have 
stated above. I'll still read that info you sent to sharpen up on 
these concepts.

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. I don't think that a member variable is going null or 
anything. But a strange problem that I can't seem to debug. The 
output is at the bottom.

----------------------------------------------------------------

import vibe.db.mongo.mongo;
import core.thread;
import std.stdio;

void main(){
     auto callable = new Callable();

     while(true){}
}

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");
     }
}

----------------------------------------------------------------

Output:
     Started
     
{"_id":"5ff6705e21e91678c737533f","state":"running","knowledge":true}
     Finished
     Started


More information about the Digitalmars-d-learn mailing list