Почему не спит?

Почему c_sleep сразу возвращается в следующий код?

{-# LANGUAGE ForeignFunctionInterface #-}
import Foreign.C.Types
import Data.Time.Clock
import Control.Concurrent

foreign import ccall unsafe "unistd.h sleep" 
    c_sleep :: CUInt -> IO CUInt

main :: IO ()
main = do
    getCurrentTime >>= print . utctDayTime
    c_sleep 10     >>= print                -- this doesn't sleep
    getCurrentTime >>= print . utctDayTime
    threadDelay $ 10 * 1000 * 1000          -- this does sleep
    getCurrentTime >>= print . utctDayTime
$ ghc --make Sleep.hs && ./Sleep
[1 of 1] Compiling Main             ( Sleep.hs, Sleep.o )
Linking Sleep ...
29448.191603s
10
29448.20158s
29458.211402s

$ ghc --version
The Glorious Glasgow Haskell Compilation System, version 7.8.3

$ cabal --version
cabal-install version 1.20.0.3
using version 1.20.0.0 of the Cabal library 

Примечание: На самом деле, я хотел бы использовать sleep в коде C, чтобы имитировать некоторые тяжелые вычисления в функцию func и вызывать эту функцию в Haskell, но это тоже не работает, возможно, по тем же причинам.

Ответы

Ответ 1

GHC RTS, похоже, использует сигналы для своего цели, а это означает, что не скоро будет прерван сон одним из этих сигналов. Я также не думаю, что это ошибка, время выполнения поставляется с собственной территорией, так сказать. Подход Haskellian состоял бы в использовании threadDelay, но для программы C не было легко получить доступ к ней без каких-либо обманов.

правильный путь заключается в повторном возобновлении сна, несмотря на прерывания от других сигналов. Я рекомендую использовать nanosleep, поскольку sleep имеет только точность секунд, и сигналы появляются намного чаще, чем это.

#include <errno.h>
#include <time.h>

/* same as 'sleep' except it doesn't get interrupted by signals */
int keep_sleeping(unsigned long sec) {
    struct timespec rem, req = { (time_t) sec, 0 }; /* warning: may overflow */
    while ((rem.tv_sec || rem.tv_nsec) && nanosleep(&req, &rem)) {
        if (errno != EINTR) /* this check is probably unnecessary */
            return -1;
        req = rem;
    }
    return 0;
}

Ответ 2

Все примитивы concurrency всегда имеют оператор clawback, который они могут блокировать за меньшее время, чем указано - они могут возвращаться ложно. Это не имеет никакого отношения к языку, это характер concurrency, поэтому, если вы хотите подождать точно указанное количество времени, на любом языке вам нужно построить цикл, проверяющий часы после сна.