Re: putMVar on full MVar
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
--- 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
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
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
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
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
"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