Dear Kyle,
I of course see your point with implementing a general semaphore with
asynchronous message passing
instead of synchronous - this surely is a classical example. But this
objection misses the point:
My cited case was the most easy example as sort of representative for
more complicated situations,
just to explain the matter. So - sorry - but I do absolutely NOT agree
with your first sentence.
On the contrary, there are lots of examples, where things are
extremely more elegant with
guarded selective waiting than just with selective waiting.
Here another one, which, I think, cannot be replaced as simply as the
semaphore example
by using asynchronous message passing: The first readers writers
problem
(Original reference: Courtois, P. J., Heymans, F., Parnas, D. L.:
Concurrent Control with `"Readers'' and `"Writers''. Commun. ACM 14
(1971) p.667-668;
and furthermore: any book on concurrent programming)
Here the simple "naive" solution with four channels
rE, rL for readers entry/leave and wE, wL for writers entry/leave:
var nR, nW int // number of active readers, writers
for {
if nW == 0 {
if nR == 0 {
select {
case <-rE:
nR++
case <-wE:
nW++
}
} else { // nR > 0
select {
case <-rE:
nR++
case <-rL:
nR--
}
}
} else { // nW == 1
<-wL
nW--
}
}
Definitely much better structured (less cases and a
clear 1:1-mapping between entry-conditions and things to "do")
with selective waiting:
select {
nW == 0 case <-rE:
nR++
nL > 0 case <-rL
nR--
nR == 0 && nW == 0 case <-wE:
nW = 1
nW == 1 case <-wL:
nW = 0
}
You certainly can imagine more complicated examples, where things fall
much more apart;
there are even cases, where an understandable solution with guarded
selective waiting is quite easy,
but without guards more or less at least difficult, if not to say
horrible!
(Start off with the second readers writers problem, generalize to
singe lane bridge problem,
make it more complicated by adding bounds, etc., etc.)