summaryrefslogtreecommitdiff
path: root/testsuite/timeout/WinCBindings.hsc
blob: 345cc4e3d7e2a05738162357a3e82c6195b7d096 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
{-# LANGUAGE CPP, ForeignFunctionInterface #-}
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

#include <windows.h>

type LPPROCESS_INFORMATION = Ptr PROCESS_INFORMATION
data PROCESS_INFORMATION = PROCESS_INFORMATION
    { piProcess :: HANDLE
    , piThread :: HANDLE
    , piProcessId :: DWORD
    , piThreadId :: DWORD
    } deriving Show

instance Storable PROCESS_INFORMATION where
    sizeOf = const #size PROCESS_INFORMATION
    alignment = sizeOf
    poke buf pinfo = do
        (#poke PROCESS_INFORMATION, hProcess)    buf (piProcess   pinfo)
        (#poke PROCESS_INFORMATION, hThread)     buf (piThread    pinfo)
        (#poke PROCESS_INFORMATION, dwProcessId) buf (piProcessId pinfo)
        (#poke PROCESS_INFORMATION, dwThreadId)  buf (piThreadId  pinfo)

    peek buf = do
        vhProcess    <- (#peek PROCESS_INFORMATION, hProcess)    buf
        vhThread     <- (#peek PROCESS_INFORMATION, hThread)     buf
        vdwProcessId <- (#peek PROCESS_INFORMATION, dwProcessId) buf
        vdwThreadId  <- (#peek PROCESS_INFORMATION, dwThreadId)  buf
        return $ PROCESS_INFORMATION {
            piProcess   = vhProcess,
            piThread    = vhThread,
            piProcessId = vdwProcessId,
            piThreadId  = vdwThreadId}

type LPSTARTUPINFO = Ptr STARTUPINFO
data STARTUPINFO = STARTUPINFO
    { siCb :: DWORD
    , siDesktop :: LPTSTR
    , siTitle :: LPTSTR
    , siX :: DWORD
    , siY :: DWORD
    , siXSize :: DWORD
    , siYSize :: DWORD
    , siXCountChars :: DWORD
    , siYCountChars :: DWORD
    , siFillAttribute :: DWORD
    , siFlags :: DWORD
    , siShowWindow :: WORD
    , siStdInput :: HANDLE
    , siStdOutput :: HANDLE
    , siStdError :: HANDLE
    } deriving Show

instance Storable STARTUPINFO where
    sizeOf = const #size STARTUPINFO
    alignment = sizeOf
    poke buf si = do
        (#poke STARTUPINFO, cb)              buf (siCb si)
        (#poke STARTUPINFO, lpDesktop)       buf (siDesktop si)
        (#poke STARTUPINFO, lpTitle)         buf (siTitle si)
        (#poke STARTUPINFO, dwX)             buf (siX si)
        (#poke STARTUPINFO, dwY)             buf (siY si)
        (#poke STARTUPINFO, dwXSize)         buf (siXSize si)
        (#poke STARTUPINFO, dwYSize)         buf (siYSize si)
        (#poke STARTUPINFO, dwXCountChars)   buf (siXCountChars si)
        (#poke STARTUPINFO, dwYCountChars)   buf (siYCountChars si)
        (#poke STARTUPINFO, dwFillAttribute) buf (siFillAttribute si)
        (#poke STARTUPINFO, dwFlags)         buf (siFlags si)
        (#poke STARTUPINFO, wShowWindow)     buf (siShowWindow si)
        (#poke STARTUPINFO, hStdInput)       buf (siStdInput si)
        (#poke STARTUPINFO, hStdOutput)      buf (siStdOutput si)
        (#poke STARTUPINFO, hStdError)       buf (siStdError si)

    peek buf = do
        vcb              <- (#peek STARTUPINFO, cb)              buf
        vlpDesktop       <- (#peek STARTUPINFO, lpDesktop)       buf
        vlpTitle         <- (#peek STARTUPINFO, lpTitle)         buf
        vdwX             <- (#peek STARTUPINFO, dwX)             buf
        vdwY             <- (#peek STARTUPINFO, dwY)             buf
        vdwXSize         <- (#peek STARTUPINFO, dwXSize)         buf
        vdwYSize         <- (#peek STARTUPINFO, dwYSize)         buf
        vdwXCountChars   <- (#peek STARTUPINFO, dwXCountChars)   buf
        vdwYCountChars   <- (#peek STARTUPINFO, dwYCountChars)   buf
        vdwFillAttribute <- (#peek STARTUPINFO, dwFillAttribute) buf
        vdwFlags         <- (#peek STARTUPINFO, dwFlags)         buf
        vwShowWindow     <- (#peek STARTUPINFO, wShowWindow)     buf
        vhStdInput       <- (#peek STARTUPINFO, hStdInput)       buf
        vhStdOutput      <- (#peek STARTUPINFO, hStdOutput)      buf
        vhStdError       <- (#peek STARTUPINFO, hStdError)       buf
        return $ STARTUPINFO {
            siCb            =  vcb,
            siDesktop       =  vlpDesktop,
            siTitle         =  vlpTitle,
            siX             =  vdwX,
            siY             =  vdwY,
            siXSize         =  vdwXSize,
            siYSize         =  vdwYSize,
            siXCountChars   =  vdwXCountChars,
            siYCountChars   =  vdwYCountChars,
            siFillAttribute =  vdwFillAttribute,
            siFlags         =  vdwFlags,
            siShowWindow    =  vwShowWindow,
            siStdInput      =  vhStdInput,
            siStdOutput     =  vhStdOutput,
            siStdError      =  vhStdError}

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

cWAIT_OBJECT_0 :: DWORD
cWAIT_OBJECT_0 = #const WAIT_OBJECT_0

cWAIT_TIMEOUT :: DWORD
cWAIT_TIMEOUT = #const WAIT_TIMEOUT

cCREATE_SUSPENDED :: DWORD
cCREATE_SUSPENDED = #const CREATE_SUSPENDED

cHANDLE_FLAG_INHERIT :: DWORD
cHANDLE_FLAG_INHERIT = #const HANDLE_FLAG_INHERIT

foreign import WINDOWS_CCONV unsafe "windows.h GetExitCodeProcess"
    getExitCodeProcess :: HANDLE -> LPDWORD -> IO BOOL

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 WINDOWS_CCONV unsafe "windows.h AssignProcessToJobObject"
    assignProcessToJobObject :: HANDLE -> HANDLE -> IO BOOL

foreign import WINDOWS_CCONV unsafe "windows.h CreateJobObjectW"
    createJobObjectW :: LPSECURITY_ATTRIBUTES -> LPCTSTR -> IO HANDLE

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 ccall 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

foreign import WINDOWS_CCONV unsafe "windows.h SetHandleInformation"
    setHandleInformation :: HANDLE -> DWORD -> 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 handle to the job is closed. 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
          case res of
            False -> return ()
            True  -> do
                completionCode <- peek p_CompletionCode
                if completionCode == cJOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO
                           then return ()
                   else if completionCode == cJOB_OBJECT_MSG_EXIT_PROCESS
                           then loop -- Debug point, do nothing for now
                   else if completionCode == cJOB_OBJECT_MSG_NEW_PROCESS
                           then loop -- Debug point, do nothing for now
                           else loop

    loop -- Kick it all off

    overlapped <- peek p_Overlapped
    code       <- peek $ p_CompletionCode

    return $ if overlapped == nullPtr && code /= cJOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO
                then False -- Timeout occurred. *dark voice* YOU HAVE FAILED THIS TEST!.
                else True
#endif