```
import std.concurrency;
import std.stdio;
// version = working;
void child()
{
int i;
while (true)
{
i = receiveOnly!int();
ownerTid().send(i);
ownerTid().send(i);
writeln("2 messages sent");
}
}
void main()
{
Tid child = spawnLinked(&child);
while (true)
{
int i;
child.send(i);
receive( // A
(int x)
{
version (working) {}
else
{
writeln("blocking until 2nd message is received");
receiveOnly!int(); // B
writeln("2nd message was received");
}
}
);
version (working)
{
writeln("blocking until 2nd message is received");
receiveOnly!int(); // C
writeln("2nd message was received");
}
writeln("one loop");
}
}
```
In `version = working`, the program works as expected printing:
`2 messages sent
blocking until 2nd message is received
2nd message was received
one loop
2 messages sent
blocking until 2nd message is received
2nd message was received
one loop`
... and so on
With `version = working` commented out, instead of having the two receives in series (A + C), receive (B) is used in the message handler of receive (A) (i.e receive (B) is nested). Running the program on a multi-core CPU prints:
`2 messages sent
blocking until 2nd message is received`
and it hangs without reaching `2nd message was received`.
When running the same program using `taskset 0x01 ./test` (which limits the CPUs that the program can run on to 1) the behavior becomes similar to the working version for a while but it still hangs eventually.
So I experimented with removing the message from the list before the handler is called, but I always get a LinkTerminated Exception which comes from the child being terminated.
The child is in a while(true) loop, so it shouldn't get terminated, but for some reason now its module deconstructor is now immediately called :/
diff --git a/std/concurrency.d b/std/concurrency.d
index 0e1b505bc..1c426bcc0 100644
--- a/std/concurrency.d
+++ b/std/concurrency.d
@@ -1974,8 +1980,9 @@ private
enum timedWait = false;
}
- bool onStandardMsg(ref Message msg)
+ bool onStandardMsg(ref Message msg, void delegate() beforeSuccess = (){})
{
foreach (i, t; Ops)
{
alias Args = Parameters!(t);
@@ -1985,10 +1992,13 @@ private
{
static if (is(ReturnType!(t) == bool))
{
- return msg.map(op);
+ beforeSuccess();
+ auto b = msg.map(op);
+ return b;
}
else
{
+ beforeSuccess();
msg.map(op);
return true;
}
@@ -2043,6 +2053,8 @@ private
{
for (auto range = list[]; !range.empty;)
{
+ import std.stdio;
+ writefln("range: %s", range.front);
// Only the message handler will throw, so if this occurs
// we can be certain that the message was handled.
scope (failure)
@@ -2071,11 +2083,9 @@ private
}
else
{
- if (onStandardMsg(range.front))
- {
- list.removeAt(range);
+ if (onStandardMsg(range.front, (){ list.removeAt(range); }))
return true;
- }
+
range.popFront();
continue;
}
```
Comment #3 by robert.schadek — 2024-12-01T16:23:29Z