How to redirect a handle within a thread (GHC 6.4.1 runtime)?

Is it possible to redirect a Handle (say stdout) somewhere only within a running thread (started with forkIO) not touching the same handle for the main and other threads? I have a lot of code written with putStr(Ln) which was used in a program acting as a filter, i. e. stdout was redirected by the invoking shell. Now I want to run this code within a thread in other program, but output must go to a file (or a pipe, or anywhere else a file descriptor may be opened for). So fdToHandle is not good because I need to modify the `stdout' only for that thread, not to create a new Handle. Rewriting the code is not a convenient way (but will be done if nothing else helps) because then I will need to pass that handle around. Running that code in a separate executable is just a bit more convenient because I need to pass many parameters via command line (a thread inherits values of them), and therefore is also desired to be avioded. Functions in System.Process deal with external executables. Does there exist anything like runInteractiveProcess, but just forking a new process (the whole memory is copied, so everything will be inherited), but executes a particular function within the parent program? I'm afraid that if I just make a foreign call to `fork', the RTS may not understand this correctly. -- Dimitry Golubovsky Anywhere on the Web

Hello Dimitry, Wednesday, January 18, 2006, 9:01:28 PM, you wrote: DG> new Handle. Rewriting the code is not a convenient way (but will be DG> done if nothing else helps) because then I will need to pass that DG> handle around. implicit parameter or global IORef may save father of russian democracy ;) -- Best regards, Bulat mailto:bulatz@HotPOP.com

On Wed, 18 Jan 2006, Dimitry Golubovsky wrote:
Is it possible to redirect a Handle (say stdout) somewhere only within a running thread (started with forkIO) not touching the same handle for the main and other threads?
I think it's fairly simple, if I have understood your requirement. I assume we are talking about UNIX or similar platform. You don't need to really modify stdout at all, it's more effective to accomplish the redirection at a lower lever, with file descriptors.
... fdToHandle is not good because I need to modify the `stdout' only for that thread, not to create a new Handle.
Indeed, so instead of fdToHandle, you want dupTo, which will redirect unit 1 in the process' OS level table. Standard input, output and error are really 0, 1, and 2 at the UNIX system level; stdout etc. are just process level language library constructs that add buffers. Note the flushes in my example. The first may be necessary to avoid copying buffered data into the fork, which would cause it to appear twice. The second is a work-around for a bug that was mentioned here only a day or two ago. Donn Cave, donn@drizzle.com module Main (main) where import System.IO (hFlush, stdout) import System.Posix.IO import System.Posix.Process redirect :: FilePath -> IO () -> IO () redirect file fn = do fd <- openFd file WriteOnly (Just 420) defaultFileFlags x <- dupTo fd 1 closeFd fd fn hFlush stdout main = do hFlush stdout pid <- forkProcess $ redirect "test.out" $ putStrLn "howdy" st <- getProcessStatus True False pid putStrLn ("status " ++ (show st))

On Wednesday 18 January 2006 19:01, Dimitry Golubovsky wrote:
Is it possible to redirect a Handle (say stdout) somewhere only within a running thread (started with forkIO) not touching the same handle for the main and other threads?
I have a lot of code written with putStr(Ln) which was used in a program acting as a filter, i. e. stdout was redirected by the invoking shell. Now I want to run this code within a thread in other program, but output must go to a file (or a pipe, or anywhere else a file descriptor may be opened for). So fdToHandle is not good because I need to modify the `stdout' only for that thread, not to create a new Handle. Rewriting the code is not a convenient way (but will be done if nothing else helps) because then I will need to pass that handle around.
Isn't this _the_ real-world example perfectly matching Robert Dockins' 'threadlocal' proposal? Anyway, I think you could hack your way around the problem: {-# NOINLINE special_tid_var #-} special_tid_var :: MVar ThreadId special_tid_var = unsafePeformIO newEmptyMVar {-# NOINLINE special_stdout_var #-} special_stdout_var :: MVar Handle special_stdout_var = unsafePeformIO newEmptyMVar main = do ... open File ... >>= putMVar special_stdout_var forkIO ... >>= putMVar special_tid_var ... -- replacement for the usual putStrLn putStrLn :: String -> IO () putStrLn str = do mytid <- myThreadId special_tid <- readMVar special_tid_var specia_stdout <- readMVar special_stdout_var if mytid == special_tid then System.IO.hPutStrLn special_stdout str else System.IO.putStrLn str Not tested, but you should get the idea. Caveat: you must change all your import declarations to hide System.IO.putStrLn and instead import the version above. Ben

On Jan 18, 2006, at 2:59 PM, Benjamin Franksen wrote:
On Wednesday 18 January 2006 19:01, Dimitry Golubovsky wrote:
Is it possible to redirect a Handle (say stdout) somewhere only within a running thread (started with forkIO) not touching the same handle for the main and other threads?
I have a lot of code written with putStr(Ln) which was used in a program acting as a filter, i. e. stdout was redirected by the invoking shell. Now I want to run this code within a thread in other program, but output must go to a file (or a pipe, or anywhere else a file descriptor may be opened for). So fdToHandle is not good because I need to modify the `stdout' only for that thread, not to create a new Handle. Rewriting the code is not a convenient way (but will be done if nothing else helps) because then I will need to pass that handle around.
Isn't this _the_ real-world example perfectly matching Robert Dockins' 'threadlocal' proposal?
Yes, actually. This is precisely the use case that got me thinking about threadlocal storage in the first place. I'm working on Shellac (http://www.mail-archive.com/haskell@haskell.org/msg17871.html) and it turns out that redirecting the standard output handle for a specific thread would be really nice. The obvious alternative is to pass in a function of type (String -> IO ()) and pass it around, and make sure to use it instead of putStr and friends. The non-obvious alternative is some unsafePerformIO hackery, as just posted. Either way, its ugly. [snip] Rob Dockins Speak softly and drive a Sherman tank. Laugh hard; it's a long way to the bank. -- TMBG
participants (5)
-
Benjamin Franksen
-
Bulat Ziganshin
-
Dimitry Golubovsky
-
Donn Cave
-
Robert Dockins