Oz's declarative concurrency is worth taking a look at. In Oz, a thread implicitly goes into sleep or wake up on data dependency. for example,
declare
FOO BAR % FOO and BAR are not bound to any value yet.
in
thread
{print FOO} % the thread blocks here until FOO is bound to a value.
BAR = 456 % Binds BAR which wake up any thread waiting for it.
end
thread
FOO = 132 % Binds FOO which wake up any thread waiting for it.
{print BAR} % the thread blocks here until BAR is bound to a value.
end
This sounds like an easy recipe for race conditions. Consider:
declare
FOO BAR % FOO and BAR are not bound to any value yet.
in
thread
{print FOO} % the thread blocks here until FOO is bound to a value.
BAR = 456 % Binds BAR which wake up any thread waiting for it.
end
thread
{print BAR} % the thread blocks here until BAR is bound to a value.
FOO = 132 % Binds FOO which wake up any thread waiting for it.
end
I swapped the instructions in the second thread. Does Oz detect that, or otherwise protect programmer from such mistakes?
If that's the complete program or alternatively if it's the entire scope for the FOO and BAR variables such that the language guarantees no other thread (or process or anything else) may mutate or even access FOO or BAR, then the implementation should throw an error saying as much, the moment the scope is left (e.g. by reaching end-of-input).
However, if one is still free to do the moral equivalent of BAR = 1 or FOO = argc in a third thread, or even at compile/link time, then why should it a mistake to have these two threads blocking on such a (possible) event?