Two Wrongs

(don't make a right)

Using withPtr From inline-c in Haskell

by ~kqr

A friend was creating an experiment in Haskell where he wanted to use the POSIX fork and execve calls, through System.Posix.Process. The problem he encountered was that he could not find a way to wait for the forked process to complete running, which caused some odd behaviour with stdin sometimes being captured by one process, and sometimes by the other.

Now, of course, the getProcessStatus function in the same module calls waitpid and thus waits for the child process – but if we ignore this, we have encountered an interesting opportunity to flex our muscles with some inline C code.

Inline C

The inline-c library is uh-maz-ing. It lets you write C code in your Haskell source files very seamlessly. The level of integration is above and beyond. The library is recent so the documentation is sparse, and there was one thing in particular that took me a while to figure out, which I'll share with you now.

Embedding regular C code in your Haskell code is very easy. You could imagine doing something like

{-# LANGUAGE TemplateHaskell, QuasiQuotes #-}
module Main where
import qualified Language.C.Inline as C


C.include "<stdio.h>"

main :: IO ()
main = do
     putStrLn "Pick your lucky number"
     number <- readLn
     [C.block| void {
         printf("Lucky number: %i\n", $(int number));
     } |]
     putStrLn "Wasn't that nice?"

After including the proper C header files, we can introduce a C code block with the block quasi-quoter. There are two bits of "new" syntax inside the block, which is

  1. The "return type declaration" – we specify that we expect the block to return "void", which gets translated by the quasi-quoter to IO ().
  2. The variable interpolation. By saying $(int number) we interpolate a variable from the surrounding Haskell environment. This might very well be the best thing about the inline-c library. How cool isn't that!? You can just interpolate your Haskell values into the C code!

(Oh, and by the way, if you try to build your program and you get linker errors (errors from ld) in the spirit of (.text): undefined reference to inline_c, you have forgot to specify a c-sources field in your cabal file. Had me puzzled for a while, because I didn't read the documentation properly …)

waitpid

The POSIX waitpid function is declared as follows:

pid_t waitpid(pid_t pid, int* status, int options)

This looks easy, right? We just use the knowledge we have from the previous example!

{-# LANGUAGE TemplateHaskell, QuasiQuotes #-}
module WaitPid where
import qualified Language.C.Inline as C


C.include "<sys/wait.h>"

waitpid :: ProcessID -> IO ()
waitpid pid =
     [C.block| void {
         waitpid($(int pid), ???, 0);
     } |]

The problem is, as you may have guessed, that waitpid expects a pointer to an integer – and what's worse, waitpid is going to mutate whatever is at the memory location of that pointer! (Technically, we could also pass in a null pointer and waitpid would not touch it, but that's no fun, is it?)

The inline-c library provides a withPtr function, which sounds like it might be just what we need. It has the following type signature (simplified for our case):

withPtr :: (Ptr Int -> IO b) -> IO (Int, b) 

So as its argument, it takes a function of a pointer to an Int, and then returns the result of the IO computation that resulted together with whatever value the pointer now points at. We start by turning our C code block into a function that takes a status pointer.

waitPidStatus :: ProcessID -> Ptr Int -> IO ()
waitPidStatus pid status =
     [C.block| void {
         waitpid($(int pid), $(int* status), 0);
     } |]

The inline-c library knows how to interpolate Ptr Int values into the C code, so we're done with that function.

Now we can call this under withPtr, like so:

waitPid :: ProcessID -> IO (Int, ())
waitPid pid =
     withPtr (\status -> waitPidStatus pid status)

And that's the magic. withPtr will allocate an Int and invent a pointer to it, and then the C code can mutate that Int as much as it wants through the pointer. When the withPtr function is finished, nobody has access to the pointer anymore so whatever it points to will be stable, and that's why it can return it as a regular Int.

We quickly realise as a final refactoring step that compacting the two definitions to one loses us nothing.

waitpid :: ProcessID -> IO (Int, ())
waitpid pid =
     withPtr (\status ->
         [C.block| void {
             waitpid($(int pid), $(int* status), 0);
         } |]
     )

We could tidy up the return type (either by using the withPtr_ function or by unpacking the tuple and just returning the first element of it), but that is simple and beyond what I'm interested in for this article.

This can hopefully be used as a simple example on how withPtr from the fantastic inline-c library can be used for fun and profit.

If you enjoyed this article, you might like others tagged with