From 2905deda99ef26c4fa20adea7cdeac80a0e36c66 Mon Sep 17 00:00:00 2001 From: Simon Marlow Date: Fri, 1 Dec 2006 15:20:42 +0000 Subject: [PATCH] Add support for the IO manager thread on Windows Fixes #637. The test program in that report now works for me with -threaded, but it doesn't work without -threaded (I don't know if that's new behaviour or not, though). --- GHC/Conc.lhs | 256 ++++++++++++++++++++++++++++++++++++++-------------- cbits/Win32Utils.c | 121 +++++++++++++++++++++++++ cbits/runProcess.c | 100 -------------------- include/HsBase.h | 6 ++ 4 files changed, 315 insertions(+), 168 deletions(-) create mode 100644 cbits/Win32Utils.c diff --git a/GHC/Conc.lhs b/GHC/Conc.lhs index c506ba4..461a3bf 100644 --- a/GHC/Conc.lhs +++ b/GHC/Conc.lhs @@ -79,13 +79,13 @@ module GHC.Conc , asyncWriteBA -- :: Int -> Int -> Int -> Int -> MutableByteArray# RealWorld -> IO (Int, Int) #endif -#ifndef mingw32_HOST_OS , ensureIOManagerIsRunning -#endif ) where import System.Posix.Types +#ifndef mingw32_HOST_OS import System.Posix.Internals +#endif import Foreign import Foreign.C @@ -99,7 +99,9 @@ import GHC.Base import GHC.IOBase import GHC.Num ( Num(..) ) import GHC.Real ( fromIntegral, quot ) +#ifndef mingw32_HOST_OS import GHC.Base ( Int(..) ) +#endif import GHC.Exception ( catchException, Exception(..), AsyncException(..) ) import GHC.Pack ( packCString# ) import GHC.Ptr ( Ptr(..), plusPtr, FunPtr(..) ) @@ -614,11 +616,7 @@ threadWaitWrite fd -- threadDelay :: Int -> IO () threadDelay time -#ifndef mingw32_HOST_OS | threaded = waitForDelayEvent time -#else - | threaded = c_Sleep (fromIntegral (time `quot` 1000)) -#endif | otherwise = IO $ \s -> case fromIntegral time of { I# time# -> case delay# time# s of { s -> (# s, () #) @@ -626,20 +624,42 @@ threadDelay time registerDelay :: Int -> IO (TVar Bool) registerDelay usecs -#ifndef mingw32_HOST_OS | threaded = waitForDelayEventSTM usecs | otherwise = error "registerDelay: requires -threaded" -#else - = error "registerDelay: not currently supported on Windows" -#endif - --- On Windows, we just make a safe call to 'Sleep' to implement threadDelay. -#ifdef mingw32_HOST_OS -foreign import stdcall safe "Sleep" c_Sleep :: CInt -> IO () -#endif foreign import ccall unsafe "rtsSupportsBoundThreads" threaded :: Bool +waitForDelayEvent :: Int -> IO () +waitForDelayEvent usecs = do + m <- newEmptyMVar + now <- getTicksOfDay + let target = now + usecs `quot` tick_usecs + atomicModifyIORef pendingDelays (\xs -> (Delay target m : xs, ())) + prodServiceThread + takeMVar m + +-- Delays for use in STM +waitForDelayEventSTM :: Int -> IO (TVar Bool) +waitForDelayEventSTM usecs = do + t <- atomically $ newTVar False + now <- getTicksOfDay + let target = now + usecs `quot` tick_usecs + atomicModifyIORef pendingDelays (\xs -> (DelaySTM target t : xs, ())) + prodServiceThread + return t + +calculateTarget :: Int -> IO Int +calculateTarget usecs = do + now <- getTicksOfDay + let -- Convert usecs to ticks, rounding up as we must wait /at least/ + -- as long as we are told + usecs' = (usecs + tick_usecs - 1) `quot` tick_usecs + target = now + 1 -- getTicksOfDay will have rounded down, but + -- again we need to wait for /at least/ as long + -- as we are told, so add 1 to it + + usecs' + return target + -- ---------------------------------------------------------------------------- -- Threaded RTS implementation of threadWaitRead, threadWaitWrite, threadDelay @@ -673,16 +693,18 @@ foreign import ccall unsafe "rtsSupportsBoundThreads" threaded :: Bool -- hope we don't need to do any blocking IO between fork & exec. #ifndef mingw32_HOST_OS - data IOReq = Read {-# UNPACK #-} !Fd {-# UNPACK #-} !(MVar ()) | Write {-# UNPACK #-} !Fd {-# UNPACK #-} !(MVar ()) +#endif data DelayReq = Delay {-# UNPACK #-} !Int {-# UNPACK #-} !(MVar ()) | DelaySTM {-# UNPACK #-} !Int {-# UNPACK #-} !(TVar Bool) +#ifndef mingw32_HOST_OS pendingEvents :: IORef [IOReq] +#endif pendingDelays :: IORef [DelayReq] -- could use a strict list or array here {-# NOINLINE pendingEvents #-} @@ -700,6 +722,146 @@ ensureIOManagerIsRunning | threaded = seq pendingEvents $ return () | otherwise = return () +insertDelay :: DelayReq -> [DelayReq] -> [DelayReq] +insertDelay d [] = [d] +insertDelay d1 ds@(d2 : rest) + | delayTime d1 <= delayTime d2 = d1 : ds + | otherwise = d2 : insertDelay d1 rest + +delayTime (Delay t _) = t +delayTime (DelaySTM t _) = t + +type Ticks = Int +tick_freq = 50 :: Ticks -- accuracy of threadDelay (ticks per sec) +tick_usecs = 1000000 `quot` tick_freq :: Int +tick_msecs = 1000 `quot` tick_freq :: Int + +-- XXX: move into GHC.IOBase from Data.IORef? +atomicModifyIORef :: IORef a -> (a -> (a,b)) -> IO b +atomicModifyIORef (IORef (STRef r#)) f = IO $ \s -> atomicModifyMutVar# r# f s + +foreign import ccall unsafe "getTicksOfDay" + getTicksOfDay :: IO Ticks + +#ifdef mingw32_HOST_OS +-- ---------------------------------------------------------------------------- +-- Windows IO manager thread + +startIOManagerThread :: IO () +startIOManagerThread = do + wakeup <- c_getIOManagerEvent + forkIO $ service_loop wakeup [] + return () + +service_loop :: HANDLE -- read end of pipe + -> [DelayReq] -- current delay requests + -> IO () + +service_loop wakeup old_delays = do + -- pick up new delay requests + new_delays <- atomicModifyIORef pendingDelays (\a -> ([],a)) + let delays = foldr insertDelay old_delays new_delays + + now <- getTicksOfDay + (delays', timeout) <- getDelay now delays + + r <- c_WaitForSingleObject wakeup timeout + case r of + 0xffffffff -> do c_maperrno; throwErrno "service_loop" + 0 -> do + r <- c_readIOManagerEvent + exit <- + case r of + _ | r == io_MANAGER_WAKEUP -> return False + _ | r == io_MANAGER_DIE -> return True + 0 -> return False -- spurious wakeup + r -> do start_console_handler (r `shiftR` 1); return False + if exit + then return () + else service_cont wakeup delays' + + _other -> service_cont wakeup delays' -- probably timeout + +service_cont wakeup delays = do + takeMVar prodding + putMVar prodding False + service_loop wakeup delays + +-- must agree with rts/win32/ThrIOManager.c +io_MANAGER_WAKEUP = 0xffffffff :: Word32 +io_MANAGER_DIE = 0xfffffffe :: Word32 + +start_console_handler :: Word32 -> IO () +start_console_handler r = do + stableptr <- peek console_handler + forkIO $ do io <- deRefStablePtr stableptr; io (fromIntegral r) + return () + +foreign import ccall "&console_handler" + console_handler :: Ptr (StablePtr (CInt -> IO ())) + +stick :: IORef HANDLE +{-# NOINLINE stick #-} +stick = unsafePerformIO (newIORef nullPtr) + +prodding :: MVar Bool +{-# NOINLINE prodding #-} +prodding = unsafePerformIO (newMVar False) + +prodServiceThread :: IO () +prodServiceThread = do + b <- takeMVar prodding + if (not b) + then do hdl <- readIORef stick + c_sendIOManagerEvent io_MANAGER_WAKEUP + else return () + putMVar prodding True + +-- Walk the queue of pending delays, waking up any that have passed +-- and return the smallest delay to wait for. The queue of pending +-- delays is kept ordered. +getDelay :: Ticks -> [DelayReq] -> IO ([DelayReq], DWORD) +getDelay now [] = return ([], iNFINITE) +getDelay now all@(d : rest) + = case d of + Delay time m | now >= time -> do + putMVar m () + getDelay now rest + DelaySTM time t | now >= time -> do + atomically $ writeTVar t True + getDelay now rest + _otherwise -> + return (all, (fromIntegral (delayTime d - now) * + fromIntegral tick_msecs)) + -- delay is in millisecs for WaitForSingleObject + +-- ToDo: this just duplicates part of System.Win32.Types, which isn't +-- available yet. We should move some Win32 functionality down here, +-- maybe as part of the grand reorganisation of the base package... +type HANDLE = Ptr () +type DWORD = Word32 + +iNFINITE = 0xFFFFFFFF :: DWORD -- urgh + +foreign import ccall unsafe "getIOManagerEvent" -- in the RTS (ThrIOManager.c) + c_getIOManagerEvent :: IO HANDLE + +foreign import ccall unsafe "readIOManagerEvent" -- in the RTS (ThrIOManager.c) + c_readIOManagerEvent :: IO Word32 + +foreign import ccall unsafe "sendIOManagerEvent" -- in the RTS (ThrIOManager.c) + c_sendIOManagerEvent :: Word32 -> IO () + +foreign import ccall unsafe "maperrno" -- in runProcess.c + c_maperrno :: IO () + +foreign import stdcall "WaitForSingleObject" + c_WaitForSingleObject :: HANDLE -> DWORD -> IO DWORD + +#else +-- ---------------------------------------------------------------------------- +-- Unix IO manager thread, using select() + startIOManagerThread :: IO () startIOManagerThread = do allocaArray 2 $ \fds -> do @@ -793,13 +955,13 @@ service_loop wakeup readfds writefds ptimeval old_reqs old_delays = do service_loop wakeup readfds writefds ptimeval reqs' delays' +io_MANAGER_WAKEUP = 0xff :: CChar +io_MANAGER_DIE = 0xfe :: CChar + stick :: IORef Fd {-# NOINLINE stick #-} stick = unsafePerformIO (newIORef 0) -io_MANAGER_WAKEUP = 0xff :: CChar -io_MANAGER_DIE = 0xfe :: CChar - prodding :: MVar Bool {-# NOINLINE prodding #-} prodding = unsafePerformIO (newMVar False) @@ -864,42 +1026,9 @@ waitForWriteEvent fd = do prodServiceThread takeMVar m --- XXX: move into GHC.IOBase from Data.IORef? -atomicModifyIORef :: IORef a -> (a -> (a,b)) -> IO b -atomicModifyIORef (IORef (STRef r#)) f = IO $ \s -> atomicModifyMutVar# r# f s - -- ----------------------------------------------------------------------------- -- Delays -waitForDelayEvent :: Int -> IO () -waitForDelayEvent usecs = do - m <- newEmptyMVar - target <- calculateTarget usecs - atomicModifyIORef pendingDelays (\xs -> (Delay target m : xs, ())) - prodServiceThread - takeMVar m - --- Delays for use in STM -waitForDelayEventSTM :: Int -> IO (TVar Bool) -waitForDelayEventSTM usecs = do - t <- atomically $ newTVar False - target <- calculateTarget usecs - atomicModifyIORef pendingDelays (\xs -> (DelaySTM target t : xs, ())) - prodServiceThread - return t - -calculateTarget :: Int -> IO Int -calculateTarget usecs = do - now <- getTicksOfDay - let -- Convert usecs to ticks, rounding up as we must wait /at least/ - -- as long as we are told - usecs' = (usecs + tick_usecs - 1) `quot` tick_usecs - target = now + 1 -- getTicksOfDay will have rounded down, but - -- again we need to wait for /at least/ as long - -- as we are told, so add 1 to it - + usecs' - return target - -- Walk the queue of pending delays, waking up any that have passed -- and return the smallest delay to wait for. The queue of pending -- delays is kept ordered. @@ -917,30 +1046,20 @@ getDelay now ptimeval all@(d : rest) setTimevalTicks ptimeval (delayTime d - now) return (all,ptimeval) -insertDelay :: DelayReq -> [DelayReq] -> [DelayReq] -insertDelay d [] = [d] -insertDelay d1 ds@(d2 : rest) - | delayTime d1 <= delayTime d2 = d1 : ds - | otherwise = d2 : insertDelay d1 rest - -delayTime (Delay t _) = t -delayTime (DelaySTM t _) = t - -type Ticks = Int -tick_freq = 50 :: Ticks -- accuracy of threadDelay (ticks per sec) -tick_usecs = 1000000 `quot` tick_freq :: Int - newtype CTimeVal = CTimeVal () foreign import ccall unsafe "sizeofTimeVal" sizeofTimeVal :: Int -foreign import ccall unsafe "getTicksOfDay" - getTicksOfDay :: IO Ticks - foreign import ccall unsafe "setTimevalTicks" setTimevalTicks :: Ptr CTimeVal -> Ticks -> IO () +{- + On Win32 we're going to have a single Pipe, and a + waitForSingleObject with the delay time. For signals, we send a + byte down the pipe just like on Unix. +-} + -- ---------------------------------------------------------------------------- -- select() interface @@ -971,4 +1090,5 @@ foreign import ccall unsafe "sizeof_fd_set" sizeofFdSet :: Int #endif + \end{code} diff --git a/cbits/Win32Utils.c b/cbits/Win32Utils.c new file mode 100644 index 0000000..dca270a --- /dev/null +++ b/cbits/Win32Utils.c @@ -0,0 +1,121 @@ +/* ---------------------------------------------------------------------------- + (c) The University of Glasgow 2006 + + Useful Win32 bits + ------------------------------------------------------------------------- */ + +#include "HsBase.h" + +/* This is the error table that defines the mapping between OS error + codes and errno values */ + +struct errentry { + unsigned long oscode; /* OS return value */ + int errnocode; /* System V error code */ +}; + +static struct errentry errtable[] = { + { ERROR_INVALID_FUNCTION, EINVAL }, /* 1 */ + { ERROR_FILE_NOT_FOUND, ENOENT }, /* 2 */ + { ERROR_PATH_NOT_FOUND, ENOENT }, /* 3 */ + { ERROR_TOO_MANY_OPEN_FILES, EMFILE }, /* 4 */ + { ERROR_ACCESS_DENIED, EACCES }, /* 5 */ + { ERROR_INVALID_HANDLE, EBADF }, /* 6 */ + { ERROR_ARENA_TRASHED, ENOMEM }, /* 7 */ + { ERROR_NOT_ENOUGH_MEMORY, ENOMEM }, /* 8 */ + { ERROR_INVALID_BLOCK, ENOMEM }, /* 9 */ + { ERROR_BAD_ENVIRONMENT, E2BIG }, /* 10 */ + { ERROR_BAD_FORMAT, ENOEXEC }, /* 11 */ + { ERROR_INVALID_ACCESS, EINVAL }, /* 12 */ + { ERROR_INVALID_DATA, EINVAL }, /* 13 */ + { ERROR_INVALID_DRIVE, ENOENT }, /* 15 */ + { ERROR_CURRENT_DIRECTORY, EACCES }, /* 16 */ + { ERROR_NOT_SAME_DEVICE, EXDEV }, /* 17 */ + { ERROR_NO_MORE_FILES, ENOENT }, /* 18 */ + { ERROR_LOCK_VIOLATION, EACCES }, /* 33 */ + { ERROR_BAD_NETPATH, ENOENT }, /* 53 */ + { ERROR_NETWORK_ACCESS_DENIED, EACCES }, /* 65 */ + { ERROR_BAD_NET_NAME, ENOENT }, /* 67 */ + { ERROR_FILE_EXISTS, EEXIST }, /* 80 */ + { ERROR_CANNOT_MAKE, EACCES }, /* 82 */ + { ERROR_FAIL_I24, EACCES }, /* 83 */ + { ERROR_INVALID_PARAMETER, EINVAL }, /* 87 */ + { ERROR_NO_PROC_SLOTS, EAGAIN }, /* 89 */ + { ERROR_DRIVE_LOCKED, EACCES }, /* 108 */ + { ERROR_BROKEN_PIPE, EPIPE }, /* 109 */ + { ERROR_DISK_FULL, ENOSPC }, /* 112 */ + { ERROR_INVALID_TARGET_HANDLE, EBADF }, /* 114 */ + { ERROR_INVALID_HANDLE, EINVAL }, /* 124 */ + { ERROR_WAIT_NO_CHILDREN, ECHILD }, /* 128 */ + { ERROR_CHILD_NOT_COMPLETE, ECHILD }, /* 129 */ + { ERROR_DIRECT_ACCESS_HANDLE, EBADF }, /* 130 */ + { ERROR_NEGATIVE_SEEK, EINVAL }, /* 131 */ + { ERROR_SEEK_ON_DEVICE, EACCES }, /* 132 */ + { ERROR_DIR_NOT_EMPTY, ENOTEMPTY }, /* 145 */ + { ERROR_NOT_LOCKED, EACCES }, /* 158 */ + { ERROR_BAD_PATHNAME, ENOENT }, /* 161 */ + { ERROR_MAX_THRDS_REACHED, EAGAIN }, /* 164 */ + { ERROR_LOCK_FAILED, EACCES }, /* 167 */ + { ERROR_ALREADY_EXISTS, EEXIST }, /* 183 */ + { ERROR_FILENAME_EXCED_RANGE, ENOENT }, /* 206 */ + { ERROR_NESTING_NOT_ALLOWED, EAGAIN }, /* 215 */ + { ERROR_NOT_ENOUGH_QUOTA, ENOMEM } /* 1816 */ +}; + +/* size of the table */ +#define ERRTABLESIZE (sizeof(errtable)/sizeof(errtable[0])) + +/* The following two constants must be the minimum and maximum + values in the (contiguous) range of Exec Failure errors. */ +#define MIN_EXEC_ERROR ERROR_INVALID_STARTING_CODESEG +#define MAX_EXEC_ERROR ERROR_INFLOOP_IN_RELOC_CHAIN + +/* These are the low and high value in the range of errors that are + access violations */ +#define MIN_EACCES_RANGE ERROR_WRITE_PROTECT +#define MAX_EACCES_RANGE ERROR_SHARING_BUFFER_EXCEEDED + +void maperrno (void) +{ + int i; + DWORD dwErrorCode; + + dwErrorCode = GetLastError(); + + /* check the table for the OS error code */ + for (i = 0; i < ERRTABLESIZE; ++i) + { + if (dwErrorCode == errtable[i].oscode) + { + errno = errtable[i].errnocode; + return; + } + } + + /* The error code wasn't in the table. We check for a range of */ + /* EACCES errors or exec failure errors (ENOEXEC). Otherwise */ + /* EINVAL is returned. */ + + if (dwErrorCode >= MIN_EACCES_RANGE && dwErrorCode <= MAX_EACCES_RANGE) + errno = EACCES; + else + if (dwErrorCode >= MIN_EXEC_ERROR && dwErrorCode <= MAX_EXEC_ERROR) + errno = ENOEXEC; + else + errno = EINVAL; +} + +#define TICKS_PER_SECOND 50 +// must match GHC.Conc.tick_freq + +HsInt getTicksOfDay(void) +{ + HsInt64 t; + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + t = ((HsInt64)ft.dwHighDateTime << 32) | ft.dwLowDateTime; + t = (t * TICKS_PER_SECOND) / 10000000LL; + /* FILETIMES are in units of 100ns */ + return (HsInt)t; +} + diff --git a/cbits/runProcess.c b/cbits/runProcess.c index 39597b7..93aa8c4 100644 --- a/cbits/runProcess.c +++ b/cbits/runProcess.c @@ -233,105 +233,6 @@ int waitForProcess (ProcHandle handle) /* -------------------- WINDOWS VERSION --------------------- */ -/* This is the error table that defines the mapping between OS error - codes and errno values */ - -struct errentry { - unsigned long oscode; /* OS return value */ - int errnocode; /* System V error code */ -}; - -static struct errentry errtable[] = { - { ERROR_INVALID_FUNCTION, EINVAL }, /* 1 */ - { ERROR_FILE_NOT_FOUND, ENOENT }, /* 2 */ - { ERROR_PATH_NOT_FOUND, ENOENT }, /* 3 */ - { ERROR_TOO_MANY_OPEN_FILES, EMFILE }, /* 4 */ - { ERROR_ACCESS_DENIED, EACCES }, /* 5 */ - { ERROR_INVALID_HANDLE, EBADF }, /* 6 */ - { ERROR_ARENA_TRASHED, ENOMEM }, /* 7 */ - { ERROR_NOT_ENOUGH_MEMORY, ENOMEM }, /* 8 */ - { ERROR_INVALID_BLOCK, ENOMEM }, /* 9 */ - { ERROR_BAD_ENVIRONMENT, E2BIG }, /* 10 */ - { ERROR_BAD_FORMAT, ENOEXEC }, /* 11 */ - { ERROR_INVALID_ACCESS, EINVAL }, /* 12 */ - { ERROR_INVALID_DATA, EINVAL }, /* 13 */ - { ERROR_INVALID_DRIVE, ENOENT }, /* 15 */ - { ERROR_CURRENT_DIRECTORY, EACCES }, /* 16 */ - { ERROR_NOT_SAME_DEVICE, EXDEV }, /* 17 */ - { ERROR_NO_MORE_FILES, ENOENT }, /* 18 */ - { ERROR_LOCK_VIOLATION, EACCES }, /* 33 */ - { ERROR_BAD_NETPATH, ENOENT }, /* 53 */ - { ERROR_NETWORK_ACCESS_DENIED, EACCES }, /* 65 */ - { ERROR_BAD_NET_NAME, ENOENT }, /* 67 */ - { ERROR_FILE_EXISTS, EEXIST }, /* 80 */ - { ERROR_CANNOT_MAKE, EACCES }, /* 82 */ - { ERROR_FAIL_I24, EACCES }, /* 83 */ - { ERROR_INVALID_PARAMETER, EINVAL }, /* 87 */ - { ERROR_NO_PROC_SLOTS, EAGAIN }, /* 89 */ - { ERROR_DRIVE_LOCKED, EACCES }, /* 108 */ - { ERROR_BROKEN_PIPE, EPIPE }, /* 109 */ - { ERROR_DISK_FULL, ENOSPC }, /* 112 */ - { ERROR_INVALID_TARGET_HANDLE, EBADF }, /* 114 */ - { ERROR_INVALID_HANDLE, EINVAL }, /* 124 */ - { ERROR_WAIT_NO_CHILDREN, ECHILD }, /* 128 */ - { ERROR_CHILD_NOT_COMPLETE, ECHILD }, /* 129 */ - { ERROR_DIRECT_ACCESS_HANDLE, EBADF }, /* 130 */ - { ERROR_NEGATIVE_SEEK, EINVAL }, /* 131 */ - { ERROR_SEEK_ON_DEVICE, EACCES }, /* 132 */ - { ERROR_DIR_NOT_EMPTY, ENOTEMPTY }, /* 145 */ - { ERROR_NOT_LOCKED, EACCES }, /* 158 */ - { ERROR_BAD_PATHNAME, ENOENT }, /* 161 */ - { ERROR_MAX_THRDS_REACHED, EAGAIN }, /* 164 */ - { ERROR_LOCK_FAILED, EACCES }, /* 167 */ - { ERROR_ALREADY_EXISTS, EEXIST }, /* 183 */ - { ERROR_FILENAME_EXCED_RANGE, ENOENT }, /* 206 */ - { ERROR_NESTING_NOT_ALLOWED, EAGAIN }, /* 215 */ - { ERROR_NOT_ENOUGH_QUOTA, ENOMEM } /* 1816 */ -}; - -/* size of the table */ -#define ERRTABLESIZE (sizeof(errtable)/sizeof(errtable[0])) - -/* The following two constants must be the minimum and maximum - values in the (contiguous) range of Exec Failure errors. */ -#define MIN_EXEC_ERROR ERROR_INVALID_STARTING_CODESEG -#define MAX_EXEC_ERROR ERROR_INFLOOP_IN_RELOC_CHAIN - -/* These are the low and high value in the range of errors that are - access violations */ -#define MIN_EACCES_RANGE ERROR_WRITE_PROTECT -#define MAX_EACCES_RANGE ERROR_SHARING_BUFFER_EXCEEDED - -static void maperrno (void) -{ - int i; - DWORD dwErrorCode; - - dwErrorCode = GetLastError(); - - /* check the table for the OS error code */ - for (i = 0; i < ERRTABLESIZE; ++i) - { - if (dwErrorCode == errtable[i].oscode) - { - errno = errtable[i].errnocode; - return; - } - } - - /* The error code wasn't in the table. We check for a range of */ - /* EACCES errors or exec failure errors (ENOEXEC). Otherwise */ - /* EINVAL is returned. */ - - if (dwErrorCode >= MIN_EACCES_RANGE && dwErrorCode <= MAX_EACCES_RANGE) - errno = EACCES; - else - if (dwErrorCode >= MIN_EXEC_ERROR && dwErrorCode <= MAX_EXEC_ERROR) - errno = ENOEXEC; - else - errno = EINVAL; -} - /* * Function: mkAnonPipe * @@ -413,7 +314,6 @@ runProcess (char *cmd, char *workingDirectory, void *environment, STARTUPINFO sInfo; PROCESS_INFORMATION pInfo; DWORD flags; - char buffer[256]; ZeroMemory(&sInfo, sizeof(sInfo)); sInfo.cb = sizeof(sInfo); diff --git a/include/HsBase.h b/include/HsBase.h index aa1a7fb..fe8c0cd 100644 --- a/include/HsBase.h +++ b/include/HsBase.h @@ -132,6 +132,12 @@ #include "runProcess.h" #if defined(__MINGW32__) +/* in Win32Utils.c */ +extern void maperrno (void); +extern HsInt getTicksOfDay(void); +#endif + +#if defined(__MINGW32__) #include #include #include "timeUtils.h" -- 1.7.10.4