Re: putMVar on full MVar

2000-04-14 Thread George Russell

Simon Marlow wrote:
 Let me see if I've got the semantics right: takeMVarMulti makes a
 non-deterministic choice between the full MVars to return the value, and if
 there are no full MVars it waits for the first one to become full?
Yes that's precisely right.  putMVarMulti is the converse.  (So it only
writes to one location, which will confuse some people.)
 
 Defining this in Haskell is pretty hard.  I managed to do it for two MVars
 (code at the end of this message).  There might be an easier way to do it
 using special code in the scheduler, but I need to think about that some
 more.
It's _because_ defining it in Haskell is pretty hard that I was thinking maybe
it should be added to the concurrency primitives.  Unless I'm missing something,
with the current concurrency primitives you cannot make a non-deterministic
choice between N MVars with less than N threads, unless you do it by polling
at intervals

Actually even takeMVarMulti and putMVarMulti could be generalised.  A more
general non-deterministic MVar operation function might be

multi :: [Multi a] - IO a

data Multi a =
   forall b . PutMVar (MVar b) b IO a
   forall b . TakeMVar (MVar b) (b - IO a)
This would wait for the first doable PutMVar or TakeMVar and then
execute the corresponding continuation.




RE: putMVar on full MVar

2000-04-14 Thread Simon Marlow

 --- blocking versions
   takeMVar :: MVar a - IO a
   putMVar  :: MVar a - a - IO ()
 
 --- non-blocking versions
   tryTakeMVar :: MVar a - IO (Maybe a)
   tryPutMVar  :: MVar a - a - IO Bool
 
 --- current putMVar:
   putMVarMayFail :: MVar a - a - IO ()
   putMVarMayFail m a
   = b - tryPutMVar m a
 if b then throw PutFullMVar else return ()
/ \
   / \
   not b

Simon




Re: putMVar on full MVar

2000-04-13 Thread George Russell

Claus Reinke wrote:
 Right. I am relieved to read that your application source is not
 full of calls to putMVar, but rather uses safe abstractions built
 on top of MVars (it might be interesting to isolate them out of
 your current application and migrate them to a CH library?).
Not a bad idea.  Public implementations of channels are already available
(I believe there is already code provided with GHC.)  I think Einar Karlsen's
composable events are an even neater idea and I should like to publicise and
extend them some more later on, maybe by writing a paper about them.

I don't understand the "tryButDoNotSuspend" operation.  What does it mean,
"would lead to an immediate suspension of the current thread"?

It would incidentally be fairly trivial to implement tryForAWhileButDoNotBlockForever
using Einar Karlsen's events.  (Except of course that delays are always going to
be approximate; GHC might not be able to run the thread bang on time because it
could be in the middle of some C function.)

I think the rest of Claus's discussion is a very good illustration of what I
said before that you can think of other primitives that could arguably be
provided for MVars and perhaps we should think the whole issue through
carefully.  Another thing which MVars can't do very well at the moment
is multiple access.  Perhaps there should be a function
   takeMVarList :: [MVar a] - IO a
which waits for the first MVar to come along.  (Again, you could easily
implement such a thing using MVars defined in terms of Einar's events.)

Well I have no quarrel with orthogonal extensions to MVars PROVIDED THAT
THEY DO NOT GET SLOWER.  So what do the wizards of GHC say can be done for free?




Re: putMVar on full MVar

2000-04-13 Thread Claus Reinke

  a. Ease of implementation should never be the first argument;-)
 
 In the case of basic functionality like the synchronisation
 provided by MVars, ease of implementation and efficiency go
 often hand in hand.  For such a basic structure, efficiency
 matters a lot.

The same points have been made for languages that omit array
bounds checks, or allow side effects everywhere, or have limited
Ints as defaults instead of variable length Integers. 

In line with other parts of ghc (e.g., unsafePerformIO), I would like 
to get the safe operations per default, with access to the unsafe, 
more efficient operations if I am willing to take on the proof obligations.

In other words, if I can show that my program logic guarantees
safe execution (or if I can change my program to enable such a
proof), then I want to be able to use the more efficient putMVar.

But the default should not assume that such proofs exist or introduce
race conditions I can do without.

Claus

PS. There is some ambivalence in Haskell about out-of-range use 
of partial functions. Some cause static type errors, some abort at
runtime, some throw exceptions, some return Nothing in Maybe..






Re: putMVar on full MVar

2000-04-13 Thread Claus Reinke

 Where I come from, that's called a race condition :-)  We shouldn't change
 the semantics because it's possible to write broken programs using the
 existing semantics.

Where I come from, race conditions are not welcome in programs, only on
race courses:-) The point is that the semantics causes programs to be broken
*by default*, and that they have to be fixed (at least by handling the
exceptions, better by proving some statements about the dynamic behaviour
of a concurrent, dynamically evolving, asynchronous, higher-order, .. CH
program).

That it is quite possible to write programs that are not broken in spite of
the existing semantics doesn't mean that the semantics shouldn't be changed.

 It turns out that in a large number of cases, the existing MVar semantics
 is sufficient (and more efficient than using one of the abstractions).

Interesting. But also more reason to think about the defaults (if they are
not used from within safe abstractions).

Btw, does a distributed implementation of CH exist already?

Cheers,
Claus








Re: putMVar on full MVar

2000-04-12 Thread Marcin 'Qrczak' Kowalczyk

Wed, 12 Apr 2000 15:12:31 +0100, Claus Reinke [EMAIL PROTECTED] pisze:

 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

That it's impossible to implement the equivalent of takeMVar
(block until it is full).

-- 
 __("Marcin Kowalczyk * [EMAIL PROTECTED] http://qrczak.ids.net.pl/
 \__/  GCS/M d- s+:-- a23 C+++$ UL++$ P+++ L++$ E-
  ^^  W++ N+++ o? K? w(---) O? M- V? PS-- PE++ Y? PGP+ t
QRCZAK  5? X- R tv-- b+++ DI D- G+ e h! r--%++ y-





Re: putMVar on full MVar

2000-04-12 Thread Manuel M. T. Chakravarty

"Claus Reinke" [EMAIL PROTECTED] wrote,

  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;-)

In the case of basic functionality like the synchronisation
provided by MVars, ease of implementation and efficiency go
often hand in hand.  For such a basic structure, efficiency
matters a lot.

 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?).

Whenever the program logic (in all possible schedules)
guarantees that an MVar is never written to a second time
before it is read at least once, there is no need for an
exception handler.  Whenever the program logic does not give
that guarantee, you have to either use an exception handler
or establish this guarantee by using a second MVar.

This is like using partially defined functions.  If you
define a function partially, you have to make sure that you
never call it with a value not in its domain; otherwise, you 
get a runtime error, like with MVars.

Manuel