Debug help - ! in data sharing concurrency
Steven Schveighoffer
schveiguy at gmail.com
Sun Aug 31 01:27:57 UTC 2025
On Saturday, 30 August 2025 at 22:05:49 UTC, Brother Bill wrote:
> A predicate (!*isDone) vs. (*isDone == false) seems to have
> different behavior, where I would expect identical behavior.
> What am I missing?
>
> This program runs forever, even though isDone changes from
> false to true.
> ```d
> import std.stdio;
> import std.concurrency;
> import core.thread;
> import core.time : msecs;
>
> void main()
> {
> shared(bool) isDone = false;
> spawn(&worker, &isDone);
> writeln("main");
>
> Thread.sleep(1.seconds);
>
> // Signalling the worker to terminate:
> isDone = true;
> writeln("main() isDone: ", isDone);
> }
>
> void worker(shared(bool)* isDone)
> {
> writeln("worker() before while, isDone: ", *isDone);
> while (!*isDone)
> {
> Thread.sleep(250.msecs);
> writeln("worker() isDone: ", *isDone);
> }
> }
>
> ```
Thank you for posting the full code. This helps diagnose issues
that generally are confusing because you are looking at the wrong
problem.
What you have done here:
```d
// Signalling the worker to terminate:
isDone = true;
writeln("main() isDone: ", isDone);
// exits main
}
```
is you exited the `main` function. However, where does `isDone`
live? It lives in `main`'s stack frame! When you exit the stack
frame, it becomes unallocated.
This means the next function that gets called, will overtake the
value at the address of `isDone`, and write some other value into
it (obviously 0).
If I add a sleep 1 second after setting isDone to true, the
worker exits. this is because the pause gives the worker enough
time to see the value has changed to `true` before it leaves.
Why would your second iteration make a difference? Purely by
chance! In fact, on my machine, it does not exit in either case.
Welcome to the wonderful world of race conditions and
multithreading!
To properly solve this problem, you can:
a) allocate `isDone` on the heap so it doesn't go away.
b) place `isDone` as a global variable.
c) Do not exit the main thread until the worker thread is
finished.
I recommend c in this case:
```d
import std.stdio;
import std.concurrency;
import core.thread;
import core.time : msecs;
void main()
{
shared(bool) isDone = false;
auto thread = spawnLinked(&worker, &isDone); // note spawnLinked
here
writeln("main");
Thread.sleep(1.seconds);
// Signalling the worker to terminate:
isDone = true;
writeln("main() isDone: ", isDone);
receiveOnly!LinkTerminated(); // wait for worker to exit
}
void worker(shared(bool)* isDone)
{
writeln("worker() before while, isDone: ", *isDone);
while (!*isDone)
{
Thread.sleep(250.msecs);
writeln("worker() isDone: ", *isDone);
}
}
```
More information about the Digitalmars-d-learn
mailing list