Hi,

thanks very much for caring for this problem and for your analysis. In the mean time I realized that Mads' WxProcess implementation indeed works well on Windows! My former mail was due to a misinterpretation of the output I observed, so I am currently happy with Mads' solution.

One thing I have to note though: Trying to abort a process started with wxExecProcess using Process.terminateProcess does not work in Mads' solution. The process does in fact run to its end and then terminates - with an ExitFailure status. Therefore I changed the WxProcess code such that waiting for the two MVars stdOutIsFinished and stdErrIsFinished is done *after* waitForProcess and only if not killed (for which I had to introduce an additional IORef parameter to wxExecProcess). This works only, however, if the application is compiled with -threaded.

The attached modified WxProcess.hs is what I currently use and which allows me to execute and abort processes from within my application perfectly.

Best regards,
Bernd

shelarcy schrieb:
Hi,

On Wed, 05 Nov 2008 21:57:07 +0900, Bernd Holzmüller <[EMAIL PROTECTED]> wrote:
  
The attached small example application shows what happens: with
processExecAsync only garbage is shown on both the stdout and the stderr
console window, with processExecAsyncTimed the stdout console works
fine, but the stderr console shows nothing. Main.hs is the wxHaskell GUI
with two buttons, both calling the application compiled from
Testproc.hs, one using processExecAsync, one using the Timed variant.
The attached makefile builds everything.
        

I tested Mads' program on Mac OS X platform.

[1]. processExecAsync example crashes this program.
[2]. processExecAsyncTimed example doesn't work correctory. stdout console works
     fine, but the stderr console *almost* shows nothing. We must care about this
     fact. stderr console *sometimes* works fine.
     (I also saw this fact on Windows. stderr console *sometimes* works fine, too.)
[3]. wxExecProcess example works fine.


  
I am developing a test management GUI where processes are started and
their output (on stdout and stderr) are shown on corresponding console
windows. The process management worked quite OK with version 0.9.4 but
does no longer work in version 0.10.3 (using ghc 6.8.3 on Windows XP).
        

  
I applied your supposal on my Windows machine with the following
results: wxExecProcess now indeed yields output on stderr, but output on
both stdout and stderr are somewhat incomplete (with no change when
setting output buffering in processtest.exe to NoBuffering):

(snip)
    


Do you test program with same version's GHC? GHC's IO functions has some bugs
on Windows platform. So, I want to know that this is wxHaskell's bug or GHC's
bug.

http://hackage.haskell.org/trac/ghc/ticket/806
http://hackage.haskell.org/trac/ghc/ticket/2189
etc...


I tried to fix the problem in this week. But I can't find where causes this
problem by undoing Graphics.UI.WXCore.Process related changes.


Best Regards,

  
display on stdout frame:

Stdout: 100: Info
98: Info
96: Info
94: Info
92: Info
90: Info
88: Info
86: Info
84: Info
82: Info
Stdout:
80: Info
78: Info
...

and on stderr frame:

Stderr: 99: Error
97: Error
95: Error
93: Error
91: Error
89: Error
87: Error
85: ErroStderr: r
83: Error
...

That is, the first 8 bytes (Stdout: / Stderr: ) are most of the time
vanished. Whereas processExecAsyncTimed yields the correct output (of
course only on stdout):

Stdout: 100: Info
Stdout: 98: Info
Stdout: 96: Info
Stdout: 94: Info
...
    



  

-- 
Bernd Holzmüller
Dipl.-Inform.

BU Methods Processes Tools
ICS AG
Sonnenbergstraße 13
D-70184 Stuttgart
Tel.: +49 (0) 711 / 2 10 37 - 41
Fax:  +49 (0) 711 / 2 10 37 - 75
Mobile: +49 (0) 151 / 17449 534
mailto:[EMAIL PROTECTED]
www.ics-ag.de

Informatik Consulting Systems AG
Sitz Stuttgart Handelsregister - Amtsgericht - Stuttgart HRB 18569
Aufsichtsratsvorsitzender: Dr. Jörg Kees
Vorstand: Dr. Jürgen Hämer (Vorsitzender), Franz-Josef Winkel, Cid Kiefer
{-# LANGUAGE PatternSignatures #-}

module WxProcess
    ( wxExecProcess
    , ExitCode(..)
    )
where

import Control.Concurrent -- forkIO, MVars
import System.Exit (ExitCode(..))
import System.Process (runInteractiveCommand, waitForProcess, ProcessHandle)
import System.IO
import Foreign.Marshal.Array
import Foreign.Marshal.Alloc
import Data.Word
import Data.IORef

-- import qualified Graphics.UI.WX as WX
import Graphics.UI.WX
import Graphics.UI.WX (Prop(..), on)

type OnReceive = String -> IO()
wxExecProcess :: Window a -> String -> Int -> IORef Bool -> (ExitCode -> IO()) 
-> OnReceive -> OnReceive
              -> IO ProcessHandle
-- if the user needs to give input to the created process, we could return IO 
(String -> IO StreamStatus) in stead
wxExecProcess parent cmd bufferSize killedByUser onEndProcess onOutput 
onErrOutput =
    do (inh,outh,errh,pid) <- runInteractiveCommand cmd
       mapM_ (\hdl -> hSetBuffering hdl NoBuffering) [inh, outh, errh]

       -- fork off two threads to start consuming the stdout and stderr output
       stdOutMVar       <- newEmptyMVar
       stdErrMVar       <- newEmptyMVar
       stdOutIsFinished <- newEmptyMVar
       stdErrIsFinished <- newEmptyMVar
       processFinished  <- newEmptyMVar

       let consume handle isFinished outputMVar =
               do buf <- mallocArray bufferSize
                  consume' handle isFinished outputMVar buf
                  free buf

           consume' handle isFinished outputMVar buf =
               do outIsEOF <- hIsEOF handle
                  if outIsEOF
                    then putMVar isFinished ()
                    else do hWaitForInput handle 1000   -- 1000 = one second
                            count <- hGetBufNonBlocking handle buf bufferSize
                            (x :: [Word8]) <- peekArray count buf
                            putMVar outputMVar (map (toEnum . fromIntegral) x)
                            consume' handle isFinished outputMVar buf

       forkIO $ consume outh stdOutIsFinished stdOutMVar
       forkIO $ consume errh stdErrIsFinished stdErrMVar

       let handleAnyInput mvar withOutput =
               do val <- tryTakeMVar mvar
                  maybe (return ()) withOutput val

       let handleAllInput = do handleAnyInput stdOutMVar onOutput
                               handleAnyInput stdErrMVar onErrOutput

       checkOutput <- timer parent [ interval := 100 ] -- 10 times a second
       set checkOutput [ on command := do
                           exitCode <- tryTakeMVar processFinished
                           handleAllInput
                           case exitCode of
                             Nothing   -> return ()
                             Just code -> do onEndProcess code
                                             set checkOutput [enabled := False]
                       ]

       forkIO $ do exitCode <- waitForProcess pid -- compile with -threaded to 
allow other threads to be active concurrently!
                   wasKilled <- varGet killedByUser
                   let waitForOutputs = mapM_ takeMVar [stdOutIsFinished, 
stdErrIsFinished]
                       signalFinished = putMVar processFinished exitCode
                   if wasKilled
                      then do signalFinished; waitForOutputs
                      else do waitForOutputs; signalFinished
                   hClose outh
                   hClose errh

       return pid
-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
_______________________________________________
wxhaskell-devel mailing list
wxhaskell-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/wxhaskell-devel

Reply via email to