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.
errno is thread-local; setting it in one thread does not affect its value in any other thread.
Indeed we should only check errno in the case where an error occurred. This is because if no error occurred, then it is still possible that errno will contain a non-zero value (e.g. if an error occurred during the execution of a library call but the error was recovered).
Upon successful completion, read(), pread() and readv() return a non-negative integer indicating the number of bytes actually read. Otherwise, the functions return -1 and set errno to indicate the error.
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
).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With