2015-02-03 20 views
16

Dlaczego c_sleep wraca natychmiast w poniższym kodzie?Dlaczego sen nie działa?

{-# 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 

Uwaga: Właściwie chciałbym używać sleep w kodzie C symulować jakiś skomplikowanych obliczeń w funkcji func i nazywają że funkcji w Haskell, ale to nie działa albo prawdopodobnie z tych samych powodów.

+1

Możliwe, że środowisko wykonawcze GHC wykorzystuje sygnały zakłócające tryb uśpienia. Czy sprawdziłeś kod błędu? Być może powinieneś zawinąć go w pętlę, aby go zrestartować, jeśli zostanie przerwany (w takim przypadku najlepiej jest użyć 'nanosleep' dla wyższej precyzji). – Rufflewind

+1

@Rufflewind: 'sleep' nie zwraca kodu błędu, ale kwota nieużywania w sekundach: /. Nie próbowałem jeszcze 'nanosleep', ale' usleep' też nie działa. – Zeta

+0

Kod błędu jest "zwracany" przez zmienną 'errno'. Wrap '(c_sleep 10)' wewnątrz 'throwErrnoIf (/ = 0)" sleep "', a zobaczysz, że jest on przerywany. – Rufflewind

Odpowiedz

12

GHC's RTS appears to use signals dla jego ownpurposes, co oznacza, że ​​nie potrwa długo, zanim sen zostanie przerwany przez jeden z tych sygnałów. Myślę, że to nie jest błąd, środowisko wykonawcze ma, że ​​tak powiem, come with its own territory. Podejście Haskellego polegałoby na użyciu threadDelay, ale programowi C nie jest łatwo uzyskać do niego dostęp bez jakiejś sztuczki.

Urządzenie proper way ma wielokrotnie wznowić sen pomimo przerw w działaniu innych sygnałów. Polecam używanie nanosleep od sleep ma tylko dokładność sekund i sygnały wydają się występować znacznie częściej niż to.

#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; 
} 
1

Wszystkie prymitywy współbieżności mają zawsze instrukcję windykacyjną, którą mogą zablokować na czas krótszy niż określony - mogą one zwracać fałszywie. Nie ma to nic wspólnego z językiem, jest to natura współbieżności, więc jeśli chcesz poczekać dokładnie określoną ilość czasu, w dowolnym języku musisz zbudować pętlę sprawdzającą zegar po snu.

+0

Chociaż nie jest to prawdą, nie można oczekiwać prawidłowego wywołania funkcji "(u) sleep()", która zwykle działa dobrze w domenie C z jednym wątkiem, aby zostać przerwanym natychmiast po wywołaniu_. Tym bardziej, jeśli nie używano '-threaded' i/lub concurrency. – Zeta

+0

@Zeta Oczekiwanie jest wtedy błędne. Nikt nigdy nie podaje przyczyny fałszywego przebudzenia - jest to powściągliwe; nie mówi "może obudzić * tylko *, ponieważ aplikacja sama się przerwała". Nie uważam, że sensowne jest kodowanie intuicyjnie przeciwko nieokreślonemu zachowaniu - tak jak nie zakładamy, że niezainicjowane 'int' wynosi zero w C. –