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

Reply via email to