mozart / mozart2

Mozart Programming System v2
http://mozart.github.io/
BSD 2-Clause "Simplified" License
557 stars 95 forks source link

System.printInfo thread safe? / Implementing mutexes for N threads in Oz? #38

Closed mcandre closed 11 years ago

mcandre commented 11 years ago

I'm writing a collection of sample programs for printing "Hello World" out of order using multithreads. When I do this in oz/parhello, not all of the characters are printed.

Snippet:

thread
  {Delay ({OS.rand} mod 2)}
  {System.printInfo ""#[C]}
  nil
end

Trace:

$ make
./parhello
l W

First, I thought this was an issue of the main thread completing before waiting for the children to finish. I read up in the docs how to make it dependent on the other threads completing, by using variable binding dependencies (yay dataflow programming!). But it still wasn't printing all the characters.

Then I tried delaying main thread completion by waiting more than enough time for the threads to complete. Still wasn't printing all the characters.

Then I thought, maybe the characters are overwriting a buffer. So I inserted {Open.flush Open.stdout} after each printInfo statement. Still wasn't printing all the characters.

Now I'm starting to think that System.printInfo isn't thread-safe. At this point, I'm not sure how to wrap the critical section in a mutex. The docs show examples for two threads using hard-coded data dependencies, but I'm not sure how to apply this to N threads without hard-coding N variables. Any ideas?

sjrd commented 11 years ago

System.printInfo is thread-safe. It is a builtin, hence preemption cannot happen during its execution, which makes it thread-safe.

Then I tried delaying main thread completion by waiting more than enough time for the threads to complete. Still wasn't printing all the characters.

Not quite. With {Wait Children}, you wait for the list Children to be determined, i.e., for the top-level cons pair to be constructed (which is immediately). You need to wait for all the elements in that list to be determined. Hence {ForAll Children Wait}.

By the way, with Mozart 2 it is better not to call {OS.srand 0} at all. The PRNG used by {OS.rand} is automatically seeded with a cryptographically secure random seed when the VM start.

mcandre commented 11 years ago

Excellent, works for me!

... Is there a way to wait on threaded procedures to complete? In my case, there's nothing to be determined except a dummy nil for each thread.

sjrd commented 11 years ago

Nope, it's the only way. Synchronization between threads, of any kind, is always achieved through dataflow variable. However in Oz we traditionally use the value unit for this usage (a "flag" raised by one thread to say "I'm done doing something").