> There's still some discussion to be had on what putMVar should
> do when presented with a full MVar.  The options are
>
> 1. throw an exception (as now)
> 2. block until the MVar is empty
> 3. succeed, replacing the current value in the MVar
> 4. succeed, adding the new value to a buffer in the MVar
>
> (1) is easy to implement.  (2) is more convenient occasionally,
> but can always be implemented with an extra MVar.  (3) is also
> more convenient in certain cases (imagine an MVar that held
> mouse movement events), but again can be implemented with extra
> MVars.  (4) adds some complication to the MVar implementation.

This is one aspect of the Concurrent Haskell design I have never
been happy with - I would much prefer (2) over (1), the original
CH paper also mentioned ordered (4a) and unordered buffering
(4b). Here are some arguments for the discussion:

a. Ease of implementation should never be the first argument;-)

b. In contrast to other things, that can be built on top of the
   current MVars, (1) is not only inconvenient, but unsafe,
   unless (and in a sense even if, see (d)) you always protect
   each use of putMVar with an exception handler (do you?).

c. Exceptions should be reserved for exceptional circumstances.

   Trying to execute putMVar on an MVar that has been filled
   already doesn't look exceptional to me in the presence of
   shared access from other concurrent, unsynchronised processes.

   It takes explicit programming to ensure that this situation
   cannot occur (or is handled correctly), but if this
   programming is needed in each and every case, it should be
   reused by incorporating it into the implementation (or into a
   library - why isn't everybody using CVars, btw?).

   Why would you want to treat as an exception a condition that
   occurs frequently and could be handled safely and implicitly
   by other means, such as (2), or CVars?

d. With (2), scheduling determines which of two conflicting
   writers gets to fill an MVar, and the loser simply blocks
   until the MVar is empty again. With (1), however, scheduling
   determines **whether or not there will be an exception**: if a
   reader is scheduled on a filled MVar before the second writer,
   everything is fine, whereas if the second writer is scheduled
   before any readers, you'll have an exception. Such things are
   fun to debug..

e. With the original CH semantics, exceptions would propagate to
   the top-level, absorbing the whole process network. Combine
   this with (d), and I do not understand why anyone would have
   wanted (1) in the first place (apart from supporting the case
   for exception handling?-).

   Exception handling can help here, but without grouping of
   processes, exception handling can only be global (which would
   mean losing your current process network) or immediate in each
   process that tries putMVar.  In fact, I would guess that in
   almost all cases the exception handling code is right next to
   the putMVar, so why use a non-local language construct
   (exceptions) for dealing with a perfectly localised situation?

   Of course, this assumes that you never forget to handle your
   exceptions..  If you `forgetī your exception handling, you
   will have a system that may or may not die at any time, giving
   bugs that are hard to reproduce (or might even go unnoticed
   for a while..).

f. there are lots of similarities between CH and Petri nets, and
   to get leverage from the popularity of Petri nets and from the
   experience of the Petri net community, it seems advisable not
   to introduce unnecessary differences.  Petri nets tend to use
   (2), or an unlimited capacity variant of (2) (mostly (4b)
   without, sometimes (4a) with ordering of buffer elements).

   Unordered, unlimited capacity buffers are a nice high-level
   abstraction, and limited capacity buffers can easily be
   modelled with pairs of unlimited capacity buffers, but as long
   as the outcome of conflicting putMVars is non-deterministic,
   single-place buffers should be fine for CH, and they are
   easier to implement efficiently;-)

   The point is: with (2), you could take persons used to
   coloured Petri nets (CPNs) and tell them that CH is very
   similar to what they have been using in their projects for
   ages.  There is added flexibility in CH, and added structural
   stability in CPNs - but CH can be embedded in CPNs easily, and
   more interestingly, an implementation of (a variant of) CPNs
   in CH could support a nice graphical frontend for CH..

So I can see lots of reasons against (1) and only one in favour
of (1) - easy implementation.

The main reason why I did not speak up before is that the
original CH paper seemed to suggest that MVars would only be used
at the most basic implementation level, whereas users and
libraries would almost certainly use safe variants (e.g., CVars)
built, in CH, on top of MVars. This would have achieved ease of
implementation while also protecting users from the dangers of
(1). Unfortunately, the present discussion of MVar properties
shows that if a feature is easily accessible, some hackers will
use it!-)

So I would now suggest either to change the implementation from
(1) to (2), or to discourage strongly all external use of MVars
and to shift the present discussion from MVar features to
features of abstractions built on top of them.  In other words,
what abstractions and operations need to be provided so that
noone will need to use MVars directly?

But I admit that I do not have practical experience with CH, so
perhaps I am missing the point, or there are practical reasons
why anyone would desperately want to have a variant of putMVar
that can throw exceptions at the will of the scheduler?

Claus

PS. As for tryTakeMVar or locks on MVars, what is wrong
       with using MMVar a = (MVar (MayBe a)) and a suitable
       access protocol?

       MVar empty    --> MMVar is locked
       MVar Nothing --> MMVar is empty, not locked
       MVar (Just v)  --> MMVar holds value v, not locked

       The only danger would be someone else filling the MVar while
       it is empty, but making MMVar abstract should avoid that, no?



Reply via email to