The documentation for getErrno reads:

Get the current value of errno in the current thread.

It is unclear to me whether this means the current OS thread. In particular, does the (threaded) runtime fetch and stash away errno whenever a Haskell thread is migrated from one OS thread to another?

This question seems related, but it is unclear to me whether what is said there pertains to OS or Haskell threads.

有帮助吗?

解决方案

For recent versions of GHC at least, the runtime saves errno in the Thread Storage Object (TSO) and takes care of managing it when it migrates a runtime thread to another OS thread. That means it should be safe to rely on errno in both unbound (forkIO) and bound (forkOS) threads. Here's a test for Linux:

err.hs

import Control.Concurrent
import Control.Exception
import Foreign.C

foreign import ccall safe get_ostid :: IO CUInt
foreign import ccall safe sleep :: CUInt -> IO CUInt
foreign import ccall unsafe get_errno :: IO CInt
foreign import ccall unsafe set_errno :: CInt -> IO ()

forkIO' f = do
  done <- newEmptyMVar
  forkIO (f `finally` putMVar done ())
  return (takeMVar done)

prefix = do
  id <- get_ostid
  return $ show id ++ ": "

main = do
  wait <- forkIO' $ do -- spawn a lightweight thread
    errno <- get_errno
    (putStr =<< prefix) >> putStrLn ("outer errno is " ++ show errno)
    (putStr =<< prefix) >> putStrLn "Setting outer errno to 3"
    set_errno 3
    wait' <- forkIO' $ do -- spawn another lightweight thread
      errno <- get_errno
      (putStr =<< prefix) >> putStrLn ("inner errno is " ++ show errno)
      (putStr =<< prefix) >> putStrLn "Setting inner errno to 2"
      set_errno 2
      sleep 2 -- force this lightweight thread to tie up the OS thread
      errno <- get_errno
      (putStr =<< prefix) >> putStrLn ("inner errno is " ++ show errno)
    threadDelay 1000000 -- wait a second
    -- By now, we should be in another OS thread.
    errno <- get_errno
    (putStr =<< prefix) >> putStrLn ("outer errno is " ++ show errno)
    wait'
  wait

err.c

#include <errno.h>
#include <sys/syscall.h>
#include <unistd.h>

int get_errno(void) { return errno; }
void set_errno(int e) { errno = e; }

unsigned get_ostid(void) {
  return syscall(SYS_gettid);
}

Compile with:

ghc -o err -threaded err.hs err.c

And the result should look something like:

12282: outer errno is 0
12282: Setting outer errno to 3
12282: inner errno is 0
12282: Setting inner errno to 2
12283: outer errno is 3
12282: inner errno is 2

The OS thread IDs are printed at the beginning of each line. Note that the errno of 3 was migrated to the second OS thread (12283).

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top