Difference between revisions of "Talk:SantaClausProblem"

From HaskellWiki
Jump to navigation Jump to search
(Possible infinite re-execution + no definition for check)
m ((Why does wiki not expand tildes to my real name?))
Line 107: Line 107:
 
[[User:Brianh|Brian Hulley]] 22:06, 4 January 2007 (UTC) Page 5: Trivial typo "be execute simultaneously" should be "execute simultaneously" or "be executed simultaneously". I spent ages trying to understand why the 'brutal lock' method wouldn't ensure isolation then I realised it was that I'd just assumed all other threads would be stopped dead in their tracks while the atomic block was running whereas if I'd read the text more carefully I'd have seen that the lock indeed only applies to actions wrapped in <hask>atomically</hask>. I think if you said something like "if a thread is accessing an IORef while holding the <hask>atomically</hask> lock there is nothing to stop a *different* thread from accessing the same IORef directly at the same time" it would help emphasise the fact that other threads can still be running even though only one thread can be in an atomic block at any given time.
 
[[User:Brianh|Brian Hulley]] 22:06, 4 January 2007 (UTC) Page 5: Trivial typo "be execute simultaneously" should be "execute simultaneously" or "be executed simultaneously". I spent ages trying to understand why the 'brutal lock' method wouldn't ensure isolation then I realised it was that I'd just assumed all other threads would be stopped dead in their tracks while the atomic block was running whereas if I'd read the text more carefully I'd have seen that the lock indeed only applies to actions wrapped in <hask>atomically</hask>. I think if you said something like "if a thread is accessing an IORef while holding the <hask>atomically</hask> lock there is nothing to stop a *different* thread from accessing the same IORef directly at the same time" it would help emphasise the fact that other threads can still be running even though only one thread can be in an atomic block at any given time.
   
[[User:Brianh|Brianh]] 09:49, 5 January 2007 (UTC)
+
[[User:Brianh|Brian Hulley]] 09:49, 5 January 2007 (UTC)
 
* Page 6: re-execution: this looks like you could end up in an infinite loop since if thread A needs to do something complicated in an atomic block while thread B just sits in a tight loop continuously incrementing the same TVar (used by A) then A would probably always have an invalid view of memory by the time it was ready to validate. Does the implementation address this problem?
 
* Page 6: re-execution: this looks like you could end up in an infinite loop since if thread A needs to do something complicated in an atomic block while thread B just sits in a tight loop continuously incrementing the same TVar (used by A) then A would probably always have an invalid view of memory by the time it was ready to validate. Does the implementation address this problem?
 
* Page 10: No definition for <hask>check</hask>. Section 2.4 only defines <hask>retry</hask> so perhaps section 3.2 should include:
 
* Page 10: No definition for <hask>check</hask>. Section 2.4 only defines <hask>retry</hask> so perhaps section 3.2 should include:

Revision as of 09:52, 5 January 2007

Beautiful concurrency

I am writing a chapter for a book called "Beautiful code", edited by Greg Wilson. My draft chapter is about Software Transactional Memory in Haskell.

I would welcome any comments or questions you have on the paper, or constructive suggestions for improving it; the more concrete the better.

The book is aimed at a general audience of programmers, not Haskell geeks, so I have tried to explain everything necessary as I go along. So if you are not a Haskell expert, your input would be particularly valuable to me.

You can email me directly (simonpj@microsoft.com), or add Wiki talk notes below.

If you give your real name somewhere in your text (or email it to me), I'll add you to the acknowledgements at the end of the chapter.


Simonpj 14:26, 22 December 2006 (UTC) To add a note, begin with four tilde signs ~~~~; the Wiki will fill in your user name and date.


ArthurVanLeeuwen 16:25, 22 December 2006 (UTC) (SLPJ: done) There's a couple of typos in the paper as it is now. More importantly, the footnote on page 2 has (hGetLine h "hello") where it should state (hPutStr h "hello").

Neil Mitchell 16:28, 22 December 2006 (UTC) (SLPJ: done) Sect 1, Para 2. If we want to write parallel program[s] - missing s.


Steven807 17:14, 22 December 2006 (UTC) (SLPJ: there is in fact, in 2.4; but it needs a backward ref.) There is no definition or description of check

Tibbe 18:33, 22 December 2006 (UTC); (SLPJ: done)The footnote on page 2 now has a incorrectly capitalized T in hPutSTr.

Fanf 18:51, 22 December 2006 (UTC) (SLPJ: done)page 1 para 2 "as well shall see" should be "as we shall see"

Fanf 18:51, 22 December 2006 (UTC) (SLPJ: done)page 3 "a bit like *t in C" should be "a bit like t* in C" since t is a type

Garious 18:56, 22 December 2006 (UTC) (SLPJ: done) page 10 "at which point An elf" should be "at which point an elf"

Garious 18:58, 22 December 2006 (UTC) (SLPJ: done) page 10 "Here, then is a possible" should be "Here then, is a possible"

Garious 19:16, 22 December 2006 (UTC) (SLPJ: done) page 11 "Again, we define Group is declared" whaaa? maybe: "Group is declared as a data type with constructor MkGroup. MkGroup is passed the Group's capacity, and a TVar containing the number of current members and the Group's two Gates."

Garious 19:16, 22 December 2006 (UTC) (SLPJ: done) page 11 "Creating a new Group is simply..." Is a process of three somewhat-abstract steps simple enough to say 'simply'? Instead, maybe show the code and let the reader think, "Hey, that's simple!"

Rycee 20:46, 22 December 2006 (UTC) (SLPJ: done) Page 4. You probably want to change "... the action a does ..." to "... the action act does ..."

Rycee 20:46, 22 December 2006 (UTC) (SLPJ: done) Page 8. Typographic nitpick: The space after "i.e." looks wide, perhaps you forgot to write "i.e.\ " to force a regular (non sentence ending) space in LaTeX?

MichalPalka 22:32, 22 December 2006 (UTC) (SLPJ: hard to konw what is "familiar" to a random reader. For a reader unfamiliar with SQL and triggers, referring to that might just make it more confusing. What do others think?) You could add a reference to SQL and triggers. They are similar in that it is programming with transations and seeing familiar names will make applied programmers feel more comfortable.

DavidHouse 22:55, 22 December 2006 (UTC) (SLPJ: done) Page 3, "That is, return v is a function that, when performed, does no side effects and returns v." Your use of 'that is' implies that you could deduce the fact that it does no side effects from the type signature just given, which isn't true. It's an auxiliary property of 'return'. Maybe just miss out the 'that is'.

DavidHouse 23:04, 22 December 2006 (UTC)(SLPJ: done) Bottom of Page 4, "Then 'atomically act' grabs the lock, performs the action 'at',". Missing 'c' out of 'at'.

DavidHouse 23:23, 22 December 2006 (UTC) (SLPJ: done) Page 5, "Then 'withdraw' is an STM action that adds amount to the balance in the account." 1) For consistency with the rest of the paper, it should be "Then 'withdraw account ammount'..." 2) withdraw subtracts, not adds, to the amount in the account.

DavidHouse 23:40, 22 December 2006 (UTC) (SLPJ: done at defn of hEchoLine) Page 9, you probably want to mention that ++ is string concatenation.

DavidHouse 23:45, 22 December 2006 (UTC) (SLPJ: done)Page 10, I would expect to see the type signatures for the Gate interface rewritten alongside their definitions.

DavidHouse 23:47, 22 December 2006 (UTC) (SLPJ: difficut to do this crisply, but I take the point) Page 9/10, algebraic datatypes are a little weird for the non-initiated, especially with constructors that don't appear to do anything ("Where do you define MkGate?" etc.). You might want to liken them to C's structs, and explain the constructors as tags?

DavidHouse 23:57, 22 December 2006 (UTC) (SLPJ: done) Page 13, I don't think you sufficiently explain 'sequence'. You may wish to add a sentence along the lines of "'sequence' is a function that takes a list of IO actions and returns the action that, when executed, runs each of the actions you passed it in turn."

Dalejordan 02:15, 23 December 2006 (UTC) (SLPJ: done) For the sake of us newbs you might mention in Section 2.1 how all these actions ever get performed. Also, in your description of nTimes I think it would be clearer to say it creates a composite action that performs its argument action n times, rather than say it performs it (directly) n times, even though the value of n is not yet known. Another example of the "beauty" of first class actions (and recursion).

Brecknell 03:29, 23 December 2006 (UTC) (SLPJ: done) Page 7: withdraw should subtract, not add amount to the balance (sadly). Also, since this withdraw has different semantics to the version on p5, you may want to consider giving it a different name.

Brecknell 03:29, 23 December 2006 (UTC) (SLPJ: done) Page 8, towards the end of section 2.4: Two instances of "withdraw" should be "withdraw2", one in the function definition and one in the comment.

Brecknell 05:04, 23 December 2006 (UTC) (SLPJ: done) In the problem definition, "Santa repeatedly sleeps until wakened...", but with the definition of "forever" given in the text, and without some sort of asynchronous signalling, it looks to me like Santa will sleep for at least as long as his random number generator tells him to. Perhaps move the sleep out of forever and into elf and reindeer, then Santa will just sleep in awaitGroup.

Mvanier 19:21, 23 December 2006 (UTC) (SLPJ: done) In section 3.3, "sequence" might as well just be "sequence_". It's confusing to return a list of unit values and not do anything with them. In section 5, I think that the problem with composing lock-based concurrent functions could be explained better; the statement "instead we must expose the locking protocol" could be expanded to show why this is the case.

ChrisKuklewicz 22:17, 24 December 2006 (UTC) In learning Haskell STM back in October of 1995 I had written a solution to the same Santa problem. The main suggestion I have is that the elf and reindeer are given two actions (use Gate inGate) and (useGate outGate) and are required to properly bracket their actual action between these two. If the (joinGroup group) call returned a single scoping combinator (bracket_ (useGate inGate) (useGate outGate)) then the concurrency model for the elf and reindeer would be even to get correct (and beautiful?). Furthermore they could be passed (joinGroup group) instead of (group). This prevents the elf and reindeer from calling any other group functions.

Conal 16:39, 25 December 2006 (UTC) (SLPJ: done) Page 11, first full paragraph, "then waits to the TVar to be decremented to zero." Replace "waits to" with "waits for".

Brecknell 03:41, 27 December 2006 (UTC) This was my first exposure to STM, and I found it clear and easy enough to follow. However, I think it is mainly the discussion of bank accounts that conveys the "beauty" of STM, since that's where the important concepts are demonstrated: composability of transactions, implicit locking and deadlock avoidance, etc. In the Santa Claus problem, those concepts are somewhat obscured. I think the Santa Claus problem is still useful for demonstrating how STM can handle a tricky concurrency scenario with relative ease, but it didn't give me the sense of clarity that the discussion of bank accounts did. Maybe the Santa Claus solution needs more focus on the abstractions possible in STM, how they can help modularise concurrent programs, and why STM is better for solving concurrency problems than the concurrency primitives available elsewhere.

Gaal 08:20, 27 December 2006 (UTC) (SLPJ: done) The binding and usage of the reindeer group on page 12 are inconsistent:

 ; rein_gp <- newGroup 9
 ; sequence [ reindeer gp n | n <- [1 .. 9]]
                    -- ^^
 ; forever (santa elf_group rein_group)

Fernando 14:11, 29 December 2006 (UTC) In page 3, in the definition of incRef, the last line should probably read, writeIORef var (val+1), instead of writeIORef (val+1). The latter version won't compile.

PaulRBrown 07:11, 2 January 2007 (UTC) It would be convenient if the download link for the source code were active. (Otherwise, reviewers have to cut and paste the fragments in by hand.)

PaulRBrown 07:11, 2 January 2007 (UTC) I had trouble with the motivation for the Santa Claus example, and I ended up resorting to scratch paper to sketch out the problem and the model. That section would benefit from some additional introduction, perhaps a diagram. (Something schematic with "(n of m) || (p of q)" would be nice.) You could also explain it in terms of concurrency APIs that people are familiar with, e.g., count-down semaphores in Java's concurrency package. In fact, the additional development of the counting barrier prior to the Santa Claus problem would help overall clarity.

DavidHouse 10:30, 2 January 2007 (UTC) I actually had trouble with this one too. I think even the addition of a sentence like the following would help a great deal: "We want to model this setup in Haskell, where 'sleeping' is blocking and 'waking up' is doing something."

EricWilligers 12:43, 4 January 2007 (UTC) Page 4: As page 14 currently contains a leap in difficulty with the definition of choose, perhaps foldr1 could be introduced before the last paragraph of 2.1, with an example call. This would also be an opportunity to introduce [ ] in a type signature - it does appear with regards to "sequence" but is easy to miss as the return value isn't discussed. A simple example would avoid (op) notation and would make no sense for empty lists. e.g. max_n values = foldr1 max_2 values

EricWilligers 12:43, 4 January 2007 (UTC) Page 10: Missing closing ) at the end of useGate

EricWilligers 12:43, 4 January 2007 (UTC) Page 12: elf_group => elf_gp, rein_group => rein_gp

EricWilligers 12:43, 4 January 2007 (UTC) Page 13: elf-gp 1 => elf_gp 1

EricWilligers 12:43, 4 January 2007 (UTC) Page 14 and page 8 claim all the code is presented. To meet this claim:- Perhaps the following can appear on p9, just before "Since IO actions are first-class, ..." deliverToys :: String -> IO () deliverToys s = putStr (s ++ " delivering toys\n") Perhaps the following can appear on p13, just before "Working inside-out, ..." reindeer :: Group -> Int -> IO () reindeer gp id = forkIO (forever (reindeer1 gp id)) Alternately, perhaps deliverToys and reindeer could appear in section 3.5 so they don't affect the flow in 3.1 and 3.3.

Brian Hulley 22:06, 4 January 2007 (UTC) Page 5: Trivial typo "be execute simultaneously" should be "execute simultaneously" or "be executed simultaneously". I spent ages trying to understand why the 'brutal lock' method wouldn't ensure isolation then I realised it was that I'd just assumed all other threads would be stopped dead in their tracks while the atomic block was running whereas if I'd read the text more carefully I'd have seen that the lock indeed only applies to actions wrapped in atomically. I think if you said something like "if a thread is accessing an IORef while holding the atomically lock there is nothing to stop a *different* thread from accessing the same IORef directly at the same time" it would help emphasise the fact that other threads can still be running even though only one thread can be in an atomic block at any given time.

Brian Hulley 09:49, 5 January 2007 (UTC)

  • Page 6: re-execution: this looks like you could end up in an infinite loop since if thread A needs to do something complicated in an atomic block while thread B just sits in a tight loop continuously incrementing the same TVar (used by A) then A would probably always have an invalid view of memory by the time it was ready to validate. Does the implementation address this problem?
  • Page 10: No definition for check. Section 2.4 only defines retry so perhaps section 3.2 should include:
    check c = if not c then retry() else return ()

(to avoid introducing yet another Haskell function unless)

  • Page 12: Use of sequence and list comprehensions seems more complicated than something like:
   mapM_ (elf elf_gp) [1..10]
  • Page 16: Section 5 on locks. It seems to me that STM solves these problems by just conceptually using a single lock (the 'brute force' lock on atomically) but a clever thing about STM is that it gets the simplicity of just having a single global lock without the performance penalty due to the use of logging (ie the global lock is only held for the duration of the validate and commit instead of for the whole duration of the atomic block).

These are just minor points though - I thought it was a really good intro to STM - I've never used/looked at STM before now and yet after reading just this one chapter I feel I've got enough understanding to start using it - Thanks.