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