» A Little Lesson on Laziness and Unsafety #

Paul R. Brown @ 2008-01-15

I learned a good lesson about Haskell and unsafe IO today.

As I added comment support to perpubplat, I did a little performance testing on comment submission just to make sure that it would behave decently, and I started picking up sporadic segmentation faults under load with no discernible pattern. The faults were occurring as the new comment was serialized to disk, which to the imperative programmer's eye, seems impossible, since the code that does the writing is plain vanilla Haskell code:

write_ :: B.Item -> IO ()
write_ i = do { let f = filename (B.internal_id i)
              ; h <- openFile f WriteMode
              ; hPutStr h $ B.to_string i
              ; hClose h }

On #haskell, dcoutts made the helpful suggestion to check for any foreign code, and thereby hangs a tale. (The GHC documentation makes the same suggestion.) Laziness means that computations may not be performed before their results are needed, so it's not enough to think about what's happening in the write_ function; you also have to think about the computation that created the value that's passed to write_, and on and on. With enough back-tracing, the data passed to write_ had its roots in fields read from an HTTP request handled by FastCGI, and the root cause was buried in the implementation of the function that reads the form fields: a ByteString being built out of a hunk of memory allocated to a FastCGI structure using... unsafeInterleaveIO. The segmentation fault was occurring when the evaluation of the write_ function (on a background thread) was effectively trying to read that hunk of memory that had been freed back when request processing was completed.

Copying the data in the request handling resolved the issue, but the FastCGI library should probably change slightly to insulate its clients from this issue.

 

← 2008-01-11 — Ahhh... That's Better
→ 2008-01-16 — VCs Drive Beaters, Too