summaryrefslogtreecommitdiff
path: root/testsuite/timeout/WinCBindings.hsc
diff options
context:
space:
mode:
Diffstat (limited to 'testsuite/timeout/WinCBindings.hsc')
-rw-r--r--testsuite/timeout/WinCBindings.hsc258
1 files changed, 252 insertions, 6 deletions
diff --git a/testsuite/timeout/WinCBindings.hsc b/testsuite/timeout/WinCBindings.hsc
index 51764dc5df..87e4341c17 100644
--- a/testsuite/timeout/WinCBindings.hsc
+++ b/testsuite/timeout/WinCBindings.hsc
@@ -3,7 +3,16 @@ module WinCBindings where
#if defined(mingw32_HOST_OS)
+##if defined(i386_HOST_ARCH)
+## define WINDOWS_CCONV stdcall
+##elif defined(x86_64_HOST_ARCH)
+## define WINDOWS_CCONV ccall
+##else
+## error Unknown mingw32 arch
+##endif
+
import Foreign
+import Foreign.C.Types
import System.Win32.File
import System.Win32.Types
@@ -109,9 +118,169 @@ instance Storable STARTUPINFO where
siStdOutput = vhStdOutput,
siStdError = vhStdError}
-foreign import stdcall unsafe "windows.h WaitForSingleObject"
+data JOBOBJECT_EXTENDED_LIMIT_INFORMATION = JOBOBJECT_EXTENDED_LIMIT_INFORMATION
+ { jeliBasicLimitInformation :: JOBOBJECT_BASIC_LIMIT_INFORMATION
+ , jeliIoInfo :: IO_COUNTERS
+ , jeliProcessMemoryLimit :: SIZE_T
+ , jeliJobMemoryLimit :: SIZE_T
+ , jeliPeakProcessMemoryUsed :: SIZE_T
+ , jeliPeakJobMemoryUsed :: SIZE_T
+ } deriving Show
+
+instance Storable JOBOBJECT_EXTENDED_LIMIT_INFORMATION where
+ sizeOf = const #size JOBOBJECT_EXTENDED_LIMIT_INFORMATION
+ alignment = const #alignment JOBOBJECT_EXTENDED_LIMIT_INFORMATION
+ poke buf jeli = do
+ (#poke JOBOBJECT_EXTENDED_LIMIT_INFORMATION, BasicLimitInformation) buf (jeliBasicLimitInformation jeli)
+ (#poke JOBOBJECT_EXTENDED_LIMIT_INFORMATION, IoInfo) buf (jeliIoInfo jeli)
+ (#poke JOBOBJECT_EXTENDED_LIMIT_INFORMATION, ProcessMemoryLimit) buf (jeliProcessMemoryLimit jeli)
+ (#poke JOBOBJECT_EXTENDED_LIMIT_INFORMATION, JobMemoryLimit) buf (jeliJobMemoryLimit jeli)
+ (#poke JOBOBJECT_EXTENDED_LIMIT_INFORMATION, PeakProcessMemoryUsed) buf (jeliPeakProcessMemoryUsed jeli)
+ (#poke JOBOBJECT_EXTENDED_LIMIT_INFORMATION, PeakJobMemoryUsed) buf (jeliPeakJobMemoryUsed jeli)
+ peek buf = do
+ vBasicLimitInformation <- (#peek JOBOBJECT_EXTENDED_LIMIT_INFORMATION, BasicLimitInformation) buf
+ vIoInfo <- (#peek JOBOBJECT_EXTENDED_LIMIT_INFORMATION, IoInfo) buf
+ vProcessMemoryLimit <- (#peek JOBOBJECT_EXTENDED_LIMIT_INFORMATION, ProcessMemoryLimit) buf
+ vJobMemoryLimit <- (#peek JOBOBJECT_EXTENDED_LIMIT_INFORMATION, JobMemoryLimit) buf
+ vPeakProcessMemoryUsed <- (#peek JOBOBJECT_EXTENDED_LIMIT_INFORMATION, PeakProcessMemoryUsed) buf
+ vPeakJobMemoryUsed <- (#peek JOBOBJECT_EXTENDED_LIMIT_INFORMATION, PeakJobMemoryUsed) buf
+ return $ JOBOBJECT_EXTENDED_LIMIT_INFORMATION {
+ jeliBasicLimitInformation = vBasicLimitInformation,
+ jeliIoInfo = vIoInfo,
+ jeliProcessMemoryLimit = vProcessMemoryLimit,
+ jeliJobMemoryLimit = vJobMemoryLimit,
+ jeliPeakProcessMemoryUsed = vPeakProcessMemoryUsed,
+ jeliPeakJobMemoryUsed = vPeakJobMemoryUsed}
+
+type ULONGLONG = #type ULONGLONG
+
+data IO_COUNTERS = IO_COUNTERS
+ { icReadOperationCount :: ULONGLONG
+ , icWriteOperationCount :: ULONGLONG
+ , icOtherOperationCount :: ULONGLONG
+ , icReadTransferCount :: ULONGLONG
+ , icWriteTransferCount :: ULONGLONG
+ , icOtherTransferCount :: ULONGLONG
+ } deriving Show
+
+instance Storable IO_COUNTERS where
+ sizeOf = const #size IO_COUNTERS
+ alignment = const #alignment IO_COUNTERS
+ poke buf ic = do
+ (#poke IO_COUNTERS, ReadOperationCount) buf (icReadOperationCount ic)
+ (#poke IO_COUNTERS, WriteOperationCount) buf (icWriteOperationCount ic)
+ (#poke IO_COUNTERS, OtherOperationCount) buf (icOtherOperationCount ic)
+ (#poke IO_COUNTERS, ReadTransferCount) buf (icReadTransferCount ic)
+ (#poke IO_COUNTERS, WriteTransferCount) buf (icWriteTransferCount ic)
+ (#poke IO_COUNTERS, OtherTransferCount) buf (icOtherTransferCount ic)
+ peek buf = do
+ vReadOperationCount <- (#peek IO_COUNTERS, ReadOperationCount) buf
+ vWriteOperationCount <- (#peek IO_COUNTERS, WriteOperationCount) buf
+ vOtherOperationCount <- (#peek IO_COUNTERS, OtherOperationCount) buf
+ vReadTransferCount <- (#peek IO_COUNTERS, ReadTransferCount) buf
+ vWriteTransferCount <- (#peek IO_COUNTERS, WriteTransferCount) buf
+ vOtherTransferCount <- (#peek IO_COUNTERS, OtherTransferCount) buf
+ return $ IO_COUNTERS {
+ icReadOperationCount = vReadOperationCount,
+ icWriteOperationCount = vWriteOperationCount,
+ icOtherOperationCount = vOtherOperationCount,
+ icReadTransferCount = vReadTransferCount,
+ icWriteTransferCount = vWriteTransferCount,
+ icOtherTransferCount = vOtherTransferCount}
+
+data JOBOBJECT_BASIC_LIMIT_INFORMATION = JOBOBJECT_BASIC_LIMIT_INFORMATION
+ { jbliPerProcessUserTimeLimit :: LARGE_INTEGER
+ , jbliPerJobUserTimeLimit :: LARGE_INTEGER
+ , jbliLimitFlags :: DWORD
+ , jbliMinimumWorkingSetSize :: SIZE_T
+ , jbliMaximumWorkingSetSize :: SIZE_T
+ , jbliActiveProcessLimit :: DWORD
+ , jbliAffinity :: ULONG_PTR
+ , jbliPriorityClass :: DWORD
+ , jbliSchedulingClass :: DWORD
+ } deriving Show
+
+instance Storable JOBOBJECT_BASIC_LIMIT_INFORMATION where
+ sizeOf = const #size JOBOBJECT_BASIC_LIMIT_INFORMATION
+ alignment = const #alignment JOBOBJECT_BASIC_LIMIT_INFORMATION
+ poke buf jbli = do
+ (#poke JOBOBJECT_BASIC_LIMIT_INFORMATION, PerProcessUserTimeLimit) buf (jbliPerProcessUserTimeLimit jbli)
+ (#poke JOBOBJECT_BASIC_LIMIT_INFORMATION, PerJobUserTimeLimit) buf (jbliPerJobUserTimeLimit jbli)
+ (#poke JOBOBJECT_BASIC_LIMIT_INFORMATION, LimitFlags) buf (jbliLimitFlags jbli)
+ (#poke JOBOBJECT_BASIC_LIMIT_INFORMATION, MinimumWorkingSetSize) buf (jbliMinimumWorkingSetSize jbli)
+ (#poke JOBOBJECT_BASIC_LIMIT_INFORMATION, MaximumWorkingSetSize) buf (jbliMaximumWorkingSetSize jbli)
+ (#poke JOBOBJECT_BASIC_LIMIT_INFORMATION, ActiveProcessLimit) buf (jbliActiveProcessLimit jbli)
+ (#poke JOBOBJECT_BASIC_LIMIT_INFORMATION, Affinity) buf (jbliAffinity jbli)
+ (#poke JOBOBJECT_BASIC_LIMIT_INFORMATION, PriorityClass) buf (jbliPriorityClass jbli)
+ (#poke JOBOBJECT_BASIC_LIMIT_INFORMATION, SchedulingClass) buf (jbliSchedulingClass jbli)
+ peek buf = do
+ vPerProcessUserTimeLimit <- (#peek JOBOBJECT_BASIC_LIMIT_INFORMATION, PerProcessUserTimeLimit) buf
+ vPerJobUserTimeLimit <- (#peek JOBOBJECT_BASIC_LIMIT_INFORMATION, PerJobUserTimeLimit) buf
+ vLimitFlags <- (#peek JOBOBJECT_BASIC_LIMIT_INFORMATION, LimitFlags) buf
+ vMinimumWorkingSetSize <- (#peek JOBOBJECT_BASIC_LIMIT_INFORMATION, MinimumWorkingSetSize) buf
+ vMaximumWorkingSetSize <- (#peek JOBOBJECT_BASIC_LIMIT_INFORMATION, MaximumWorkingSetSize) buf
+ vActiveProcessLimit <- (#peek JOBOBJECT_BASIC_LIMIT_INFORMATION, ActiveProcessLimit) buf
+ vAffinity <- (#peek JOBOBJECT_BASIC_LIMIT_INFORMATION, Affinity) buf
+ vPriorityClass <- (#peek JOBOBJECT_BASIC_LIMIT_INFORMATION, PriorityClass) buf
+ vSchedulingClass <- (#peek JOBOBJECT_BASIC_LIMIT_INFORMATION, SchedulingClass) buf
+ return $ JOBOBJECT_BASIC_LIMIT_INFORMATION {
+ jbliPerProcessUserTimeLimit = vPerProcessUserTimeLimit,
+ jbliPerJobUserTimeLimit = vPerJobUserTimeLimit,
+ jbliLimitFlags = vLimitFlags,
+ jbliMinimumWorkingSetSize = vMinimumWorkingSetSize,
+ jbliMaximumWorkingSetSize = vMaximumWorkingSetSize,
+ jbliActiveProcessLimit = vActiveProcessLimit,
+ jbliAffinity = vAffinity,
+ jbliPriorityClass = vPriorityClass,
+ jbliSchedulingClass = vSchedulingClass}
+
+data JOBOBJECT_ASSOCIATE_COMPLETION_PORT = JOBOBJECT_ASSOCIATE_COMPLETION_PORT
+ { jacpCompletionKey :: PVOID
+ , jacpCompletionPort :: HANDLE
+ } deriving Show
+
+instance Storable JOBOBJECT_ASSOCIATE_COMPLETION_PORT where
+ sizeOf = const #size JOBOBJECT_ASSOCIATE_COMPLETION_PORT
+ alignment = const #alignment JOBOBJECT_ASSOCIATE_COMPLETION_PORT
+ poke buf jacp = do
+ (#poke JOBOBJECT_ASSOCIATE_COMPLETION_PORT, CompletionKey) buf (jacpCompletionKey jacp)
+ (#poke JOBOBJECT_ASSOCIATE_COMPLETION_PORT, CompletionPort) buf (jacpCompletionPort jacp)
+ peek buf = do
+ vCompletionKey <- (#peek JOBOBJECT_ASSOCIATE_COMPLETION_PORT, CompletionKey) buf
+ vCompletionPort <- (#peek JOBOBJECT_ASSOCIATE_COMPLETION_PORT, CompletionPort) buf
+ return $ JOBOBJECT_ASSOCIATE_COMPLETION_PORT {
+ jacpCompletionKey = vCompletionKey,
+ jacpCompletionPort = vCompletionPort}
+
+
+foreign import WINDOWS_CCONV unsafe "windows.h WaitForSingleObject"
waitForSingleObject :: HANDLE -> DWORD -> IO DWORD
+type JOBOBJECTINFOCLASS = CInt
+
+type PVOID = Ptr ()
+
+type ULONG_PTR = CUIntPtr
+type PULONG_PTR = Ptr ULONG_PTR
+
+jobObjectExtendedLimitInformation :: JOBOBJECTINFOCLASS
+jobObjectExtendedLimitInformation = #const JobObjectExtendedLimitInformation
+
+jobObjectAssociateCompletionPortInformation :: JOBOBJECTINFOCLASS
+jobObjectAssociateCompletionPortInformation = #const JobObjectAssociateCompletionPortInformation
+
+cJOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE :: DWORD
+cJOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = #const JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
+
+cJOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO :: DWORD
+cJOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO = #const JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO
+
+cJOB_OBJECT_MSG_EXIT_PROCESS :: DWORD
+cJOB_OBJECT_MSG_EXIT_PROCESS = #const JOB_OBJECT_MSG_EXIT_PROCESS
+
+cJOB_OBJECT_MSG_NEW_PROCESS :: DWORD
+cJOB_OBJECT_MSG_NEW_PROCESS = #const JOB_OBJECT_MSG_NEW_PROCESS
+
cWAIT_ABANDONED :: DWORD
cWAIT_ABANDONED = #const WAIT_ABANDONED
@@ -121,23 +290,100 @@ cWAIT_OBJECT_0 = #const WAIT_OBJECT_0
cWAIT_TIMEOUT :: DWORD
cWAIT_TIMEOUT = #const WAIT_TIMEOUT
-foreign import stdcall unsafe "windows.h GetExitCodeProcess"
+cCREATE_SUSPENDED :: DWORD
+cCREATE_SUSPENDED = #const CREATE_SUSPENDED
+
+foreign import WINDOWS_CCONV unsafe "windows.h GetExitCodeProcess"
getExitCodeProcess :: HANDLE -> LPDWORD -> IO BOOL
-foreign import stdcall unsafe "windows.h TerminateJobObject"
+foreign import WINDOWS_CCONV unsafe "windows.h CloseHandle"
+ closeHandle :: HANDLE -> IO BOOL
+
+foreign import WINDOWS_CCONV unsafe "windows.h TerminateJobObject"
terminateJobObject :: HANDLE -> UINT -> IO BOOL
-foreign import stdcall unsafe "windows.h AssignProcessToJobObject"
+foreign import WINDOWS_CCONV unsafe "windows.h AssignProcessToJobObject"
assignProcessToJobObject :: HANDLE -> HANDLE -> IO BOOL
-foreign import stdcall unsafe "windows.h CreateJobObjectW"
+foreign import WINDOWS_CCONV unsafe "windows.h CreateJobObjectW"
createJobObjectW :: LPSECURITY_ATTRIBUTES -> LPCTSTR -> IO HANDLE
-foreign import stdcall unsafe "windows.h CreateProcessW"
+foreign import WINDOWS_CCONV unsafe "windows.h CreateProcessW"
createProcessW :: LPCTSTR -> LPTSTR
-> LPSECURITY_ATTRIBUTES -> LPSECURITY_ATTRIBUTES
-> BOOL -> DWORD -> LPVOID -> LPCTSTR -> LPSTARTUPINFO
-> LPPROCESS_INFORMATION -> IO BOOL
+foreign import WINDOWS_CCONV unsafe "string.h" memset :: Ptr a -> CInt -> CSize -> IO (Ptr a)
+
+foreign import WINDOWS_CCONV unsafe "windows.h SetInformationJobObject"
+ setInformationJobObject :: HANDLE -> JOBOBJECTINFOCLASS -> LPVOID -> DWORD -> IO BOOL
+
+foreign import WINDOWS_CCONV unsafe "windows.h CreateIoCompletionPort"
+ createIoCompletionPort :: HANDLE -> HANDLE -> ULONG_PTR -> DWORD -> IO HANDLE
+
+foreign import WINDOWS_CCONV unsafe "windows.h GetQueuedCompletionStatus"
+ getQueuedCompletionStatus :: HANDLE -> LPDWORD -> PULONG_PTR -> Ptr LPOVERLAPPED -> DWORD -> IO BOOL
+
+setJobParameters :: HANDLE -> IO BOOL
+setJobParameters hJob = alloca $ \p_jeli -> do
+ let jeliSize = sizeOf (undefined :: JOBOBJECT_EXTENDED_LIMIT_INFORMATION)
+ _ <- memset p_jeli 0 $ fromIntegral jeliSize
+ -- Configure all child processes associated with the job to terminate when the
+ -- Last process in the job terminates. This prevent half dead processes and that
+ -- hanging ghc-iserv.exe process that happens when you interrupt the testsuite.
+ (#poke JOBOBJECT_EXTENDED_LIMIT_INFORMATION, BasicLimitInformation.LimitFlags)
+ p_jeli cJOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
+ setInformationJobObject hJob jobObjectExtendedLimitInformation
+ p_jeli (fromIntegral jeliSize)
+
+createCompletionPort :: HANDLE -> IO HANDLE
+createCompletionPort hJob = do
+ ioPort <- createIoCompletionPort iNVALID_HANDLE_VALUE nullPtr 0 1
+ if ioPort == nullPtr
+ then do err_code <- getLastError
+ putStrLn $ "CreateIoCompletionPort error: " ++ show err_code
+ return nullPtr
+ else with (JOBOBJECT_ASSOCIATE_COMPLETION_PORT {
+ jacpCompletionKey = hJob,
+ jacpCompletionPort = ioPort}) $ \p_Port -> do
+ res <- setInformationJobObject hJob jobObjectAssociateCompletionPortInformation
+ (castPtr p_Port) (fromIntegral (sizeOf (undefined :: JOBOBJECT_ASSOCIATE_COMPLETION_PORT)))
+ if res
+ then return ioPort
+ else do err_code <- getLastError
+ putStrLn $ "SetInformation, error: " ++ show err_code
+ return nullPtr
+
+waitForJobCompletion :: HANDLE -> HANDLE -> DWORD -> IO BOOL
+waitForJobCompletion hJob ioPort timeout
+ = alloca $ \p_CompletionCode ->
+ alloca $ \p_CompletionKey ->
+ alloca $ \p_Overlapped -> do
+
+ -- getQueuedCompletionStatus is a blocking call,
+ -- it will wake up for each completion event. So if it's
+ -- not the one we want, sleep again.
+ let loop :: IO ()
+ loop = do
+ res <- getQueuedCompletionStatus ioPort p_CompletionCode p_CompletionKey
+ p_Overlapped timeout
+ completionCode <- peek p_CompletionCode
+
+ if completionCode == cJOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO
+ then return ()
+ else if completionCode == cJOB_OBJECT_MSG_EXIT_PROCESS
+ then loop
+ else if completionCode == cJOB_OBJECT_MSG_NEW_PROCESS
+ then loop
+ else loop
+
+ loop
+
+ overlapped <- peek p_Overlapped
+ completionKey <- peek $ castPtr p_CompletionKey
+ return $ if overlapped == nullPtr && completionKey /= hJob
+ then False -- Timeout occurred. *dark voice* YOU HAVE FAILED THIS TEST!.
+ else True
#endif