Princeton University
Computer Science Dept.

Computer Science 510
Programming Languages

Andrew W. Appel

Fall 2000


Directory
General Information | Schedule and Readings | Assignments | Announcements

A Concurrent ML example

This program implements the concurrent-read-exclusive-write (CRXW) lock covered in lecture. To run this program, make a directory containing the two file Run "sml" version 110.0.7 (i.e., /usr/local/sml/bin/sml), type CM.make(); at the first prompt, then Test.go(); at the next prompt.

It will run indefinitely, though you may stop it with an interrupt (control-C).

The sml file contains two separate implementations of the CRXW lock; one in which the locking operations are functions and one in which they are events. The first one is a bit simpler to understand; the second one gives the client a bit more choice about blocking.

Notes on CML programming:

  1. Some CML Documentation is available on line.

  2. Any creation of a channel, sync on an event, etc. must occur within a call to RunCML.doit(). Thus, for example, in structure Test the code
        local val randCh : int chan = channel()
              fun loop s = (send(randCh, s mod 10);
                            loop((s+37) mod 61))
              val _ = spawnc loop 0
          in  fun random() = recv randCh
         end
    
    is not at top level in the structure, but is put inside the main function. Reppy's book does not really explain this, you can see the CML FAQ.

  3. The ordinary TextIO library won't work for CML, since two different threads should not access the same queue without synchronization. Therefore CML provides its own TextIO library with a similar interface, and you can just use print(s) to print a string s. However, because of a bug in the compilation manager, it is helpful to have the line structure TextIO=TextIO in the beginning of any sml file that mentions print.

  4. When you call RunCML.doit it will not return as long as any thread can execute. When all the threads have exited or deadlocked, then RunCML.doit will return. This is different from the behavior of java, which stops as soon as the original thread returns, no matter how many other threads it may have spawned that are still running.

    Therefore, the last line of Test.main, which reads

    app (sync o joinEvt) threads
    
    is not really necessary. But I left it in to illustrate the management of thread id's.

  5. This program uses withNack as explained in a separate note.

  6. If an exception gets raised inside a thread, and the thread doesn't catch it, the default behavior of CML is to silently terminate the thread. This is not always what you want, especially while debugging programs. If you include the line
    structure TraceCML = TraceCML
    
    in your program, then CML will report uncaught exceptions in threads. If you want, you can read about other debugging features of the TraceCML system.