
On Thu, Aug 02, 2007 at 06:26:36PM -0700, Tim Chevalier wrote:
On 8/2/07, Tim Chevalier
wrote: I'm forwarding this to ghc-users since nobody answered on haskell-cafe, and I'm still curious... (The original message is at http://www.nabble.com/Lazy-in-either-argument--t4133855.html)
Replying to myself again, I figured out later that what's really going on in this example is that when you write: f = f (with no type signature) it desugars to: f = \@a -> let f' = f' in f' whereas if you write: f :: Bool f = f the code is left more or less as is (if you're compiling with -Onot). Then the let-body in the unannotated version gets compiled to a loop that doesn't allocate, whereas (and I don't quite follow why) the definition in the annotated version gets compiled to a loop that allocates a new closure on each iteration. Thus, in the example with two threads, the second version will be pre-empted to run the other thread that does terminate, whereas the first version will never be pre-empted.
This makes sense at least if you consider the desugaring of polymorphic definitions separately from the semantics of threads... it's only when they interact that something strange happens. Maybe it's an extreme corner case, but it still seems weird to me that adding or removing a type signature should change the semantics of code like this. It seems to me like the problem is that that the transformation from (f = f) to (f = let f' = f' in f') isn't valid in the presence of threads, since in a multithreaded situation the two are observably different... but is that a bug in the desugarer, or a bug in GHC's concurrency semantics?
If anything I think it's a bug in the code generator/the runtime. We shouldn't be generating uninterruptable loops! Option 1: Don't generate non-allocating loops. In all functions classed as loop-breaker by the occurAnal, generate heap checks even if there are no bytes involved. Pro: Very simple Con: Neil won't be too happy ;) Adds heap checks to pure-arithmetic loops. Option 2: Preempt non-allocating loops. Store somewhere the addresses of a sufficient subset of safe points. Note that the addresses of all info tables is a sufficient subset. On timer interrupt, single step the code until a safepoint is reached. Pro: low overhead and will provide most of the infrastructure I'll need if I ever get around to trying to implement hardware heap checks in GHC. Con: More complicated, especially if single-stepping is so high overhead we wind up needing to implement some other scheme, like linking the bochs core. Stefan