Wednesday, February 25, 2015

Making withSocketsDo unnecessary

Summary: Currently you have to call withSocketsDo before using the Haskell network library. In the next version you won't have to.

The Haskell network library has always had a weird and unpleasant invariant. Under Windows, you must call withSocketsDo before calling any other functions. If you forget, the error message isn't particularly illuminating (e.g. getAddrInfo, does not exist, error 10093). Calling withSocketsDo isn't harmful under Linux, but equally isn't necessary, and thus easy to accidentally omit. The network library has recently merged some patches so that in future versions there is no requirement to call withSocketsDo, even on Windows.

Existing versions of network

The reason for requiring withSocketsDo is so that the network library can initialise the Windows Winsock library. The code for withSocketsDo was approximately:

withSocketsDo :: IO a -> IO a
#if WINDOWS
withSocketsDo act = do
    initWinsock
    act `finally` termWinsock
#else
withSocketsDo act = act
#endif

Where initWinsock and termWinsock were C functions. Both checked a mutable variable so they only initialised/terminated once. The initWinsock function immediately initialised the Winsock library. The termWinsock function did not terminate the library, but merely installed an atexit handler, providing a function that ran when the program shut down which terminated the Winsock library.

As a result, in all existing versions of the network library, it is fine to nest calls to withSocketsDo, call withSocketsDo multiple times, and to perform networking operations after withSocketsDo has returned.

Future versions of network

My approach to removing the requirement to call withSocketsDo was to make it very cheap, then sprinkle it everywhere it might be needed. Making such a function cheap on non-Windows just required an INLINE pragma (although its very likely GHC would have always inlined the function anyway).

For Windows, I changed to:

withSocketsDo act = do evaluate withSocketsInit; act 

{-# NOINLINE withSocketsInit #-}
withSocketsInit = unsafePerformIO $ do
    initWinsock
    termWinsock

Now withSocketsDo is very cheap, with subsequent calls requiring no FFI calls, and thanks to pointer tagging, just a few cheap instructions. When placing additional withSocketsDo calls my strategy was to make sure I called it before constructing a Socket (which many functions take as an argument), and when taking one of the central locks required for the network library. In addition, I identified a few places not otherwise covered.

In newer versions of the network library it is probably never necessary to call withSocketsDo - if you find a place where one is necessary, let me know. However, for compatibility with older versions on Windows, it is good practice to always call withSocketsDo. Libraries making use of the network library should probably call withSocketsDo on their users behalf.

7 comments:

  1. Anonymous8:07 AM

    I'm a little bit surprised by the use of `unsafePerformIO`. According to the [documentation](http://hackage.haskell.org/package/base-4.7.0.2/docs/System-IO-Unsafe.html#v:unsafePerformIO) it's only safe if free of side effects

    > For this to be safe, the IO computation should be free of side effects and independent of its environment.

    or if one uses pragmas and compiler flags:

    > Use `{-# NOINLINE foo #-}` as a pragma on any function `foo` that calls `unsafePerformIO`. If the call is inlined, the I/O may be performed more than once.
    > Use the compiler flag `-fno-cse` to prevent common sub-expression elimination being performed on the module, which might combine two side effects that were meant to be separate. A good example is using multiple global variables (like test in the example below).
    > Make sure that the either you switch off let-floating (`-fno-full-laziness`), or that the call to `unsafePerformIO` cannot float outside a lambda. For example, if you say: f x = unsafePerformIO (newIORef []) you may get only one reference cell shared between all calls to f. Better would be f x = unsafePerformIO (newIORef [x]) because now it can't float outside the lambda.

    I see the pragma, but what about the flags?

    ReplyDelete
  2. Anon: I'm abusing it, but in a fairly robust way. The important thing is I want the value evaluated at least once. More is not a problem, and different evaluations of the same expression are fine too.

    CSE isn't an issue since all values are equivalent. Let floating is also not an issue since only doing it once instead of multiple times would be better. However, in practice, since it's under a top-level binding with NOINLINE, neither of those things are likely to fire anyway.

    ReplyDelete
  3. Anonymous10:23 AM

    Thank you for the explanation.

    ReplyDelete
  4. It might have been better to put the initialization code in a C function with attribute((constructor)) and include that in the library. This wouldn't have any overhead at all, wouldn't require any changes to the Haskell code, and wouldn't require you to find all the places that might need initialization. The GHC linker supports running constructor code, so this would work in GHCi too.

    ReplyDelete
  5. (btw, previous comment left by Simon Marlow)

    ReplyDelete
  6. Unknown: that does sound strictly superior to the approach I took, and quite a bit simpler. I might give it a go at some point.

    ReplyDelete
  7. Unknown: Thanks Simon!

    ReplyDelete