Bernard Sufrin
Emeritus: Department of Computer Science
Oxford University
(Revised 2nd April, 2025)
(Version 0.9.0)
microCSO is a new, and drastically simplified, implementation of the channel communication aspects of my Communicating Scala Objects DSL (CSO) used from 2007 to teach Oxford's Concurrent Programming course. The main differences with ThreadCSO arise from there being no need to implement its pedagogic features comprehensively, and are as follows:
-
Processes run in lightweight threads by default when started, and this makes it possible to deploy tens or hundreds of thousands of them in an applicaiton (as does the most recent
ThreadCSO
). They are also (inJava
/Scala
terminology)Runnable
so can also be run by any of thejava.util.Executor
implementations. -
The
PROC
type is now namedproc
as is the simpleproc
factory object. A simpleproc
p
and aUnit
-valued methodm
with the same body behave almost identically when applied. Thus, if defined by:
val p: proc = proc { body }
or
val p: proc = proc (name) { body }
and
def m: () => Unit = { body }
then
p()
andp.run()
andm()
have essentially the same behaviour; except that a thread in whichp
runs will (for the duration of its execution) change its name toname
in order to make post-mortem/post-deadlock investigations straightforward. -
There are fewer varieties of channel, and their construction is done systematically in the single channel factory. Here
alpha
is declared as an unbuffered (synchronized) channel ofInt
andomega
is a buffered channel ofString
with buffer capacity of 25. Neither of these may have more than one distinct reader or writer thread active "simultaneously". After its output side is closed, readers may continue to read from the channel until its buffer is empty.
val alpha = Chan[Int]("alpha", 0)
val omega = Chan[String]("omega", 25)
- The buffered channel
shared
has the same capacity asomega
but may have up to 2 reader threads and 4 writer threads active simultaneously. It expects 4closeOut()
operations, before its output side is considered closed, and 2closeIn()
operations before its input side is considered closed. After its output side is closed, readers may continue to read from the channel until the buffer is empty.
val shared = Chan.Shared(readers = 2, writers = 4)[String]("Shared", 25)
-
Unlike CSO/ThreadCSO both input ports and output ports are nonvariant. This leads to a more straightforward implementation of guarded events and alternation constructs.
-
Alternation constructs like
alt
andserve
are implemented by fast and efficient polling -- invoked only when necessary. The inter-poll period default can be overridden for each alternation instance. -
The notation for alternation constructs has been dramatically simplified, so that runtime "compilation" is no longer necessary. In particular, the
after
, andorelse
notations have been replaced. -
Control constructs
withPorts
andWithPorts
that respect the network termination convention are provided for clarity and conciseness. -
In contrast to CSO/ThreadCSO there is very little inbuilt support for run-time inspection of the state of a concurrent program.
I will (eventually) revise CSO/ThreadCSO so that process and channel communication features will be consistent with the way they are provided here.
See also github.com/sufrin/ThreadCSO