I have a (possibly irrational) bias against mutexes, and attempted to
solve a threading problem with just message passing. The code works,
but only part of the time.
I haven't run your code myself, but I think I see a few things wrong with it.
This code:
(let ((th (thread-start!
(make-thread
(lambda ()
(let lp ()
(let ((p (thread-receive 0 #f)))
(if p
(p 40)
(begin
(thread-yield!)
(lp))))))))))
(+ 2 (call/cc
(lambda (ret)
(thread-send th ret)
(thread-sleep! 4)))))
Occasionally fails by attempting to add 2 to #!void.
Let's call the original thread "thread #1" and the newly created one "thread #2".
Thread #1 will start thread #2, capture its own continuation and send it to thread #2. Then it adds the result of (thread-sleep! 4) [which is #!void] to 2, which cause your error after 4 seconds.
In thread #2, calling (p 40) will make that thread invoke thread #1's continuation, resulting in a final result of 42. Note that thread #2 will not loop if it gets the continuation as a message, because it discards its own continuation when invoking (p 40). It basically becomes thread #1 at that point.
Other notes: you should just wait in thread #2 for a message instead of waiting in a spin lock (ie using 'thread-receive' with a timeout of 0). Also, calling 'thread-yield!' isn't necessary since Gambit's thread system is preemptive.
Finally, for debugging it might help to use Gambit's command-line options so that threads other than the primordial thread will also start a repl on crashes, using for example:
% gsi -:dar
(for more details see Gambit's documentation).
Hope this helps and that I didn't misinterpret the code. Personally, I don't think you will have to use mutexes if you design your program correctly.
Guillaume