Getting the file descriptor of a handle, without closing it

Hello! A few months ago, I started a discussion about how to extract the file descriptor of a handle, without the side effect of closing the handle. Bas van Dijk kindly provided the following function: unsafeWithHandleFd :: Handle -> (Fd -> IO a) -> IO a (The action in the second argument is applied to the file descriptor of the handle in the first argument.) Now I'm trying to use it, but it appears to have a bug. This program shows it: snip---------------------------------------------------------------------- import IO import GHC.IO.Handle.Types -- haType, haDevice import GHC.IO.Handle.Internals -- withHandle', do_operation import System.Posix.Types -- Fd import System.IO.Error -- ioeSetErrorString import Data.Typeable -- cast import GHC.IO.Exception -- IllegalOperation import GHC.IO.FD hiding (stdin, stdout, stderr) -- fdFD import Foreign.C -- CInt import Control.Concurrent.MVar (MVar) main = do -- Works okay unsafeWithHandleFd stdin $ \fd -> putStrLn ("stdin: fd = " ++ show fd) -- Blocks unsafeWithHandleFd stdout $ \fd -> putStrLn ("stdout: fd = " ++ show fd) -- By Bas van Dijk unsafeWithHandleFd :: Handle -> (Fd -> IO a) -> IO a unsafeWithHandleFd h@(FileHandle _ m) f = unsafeWithHandleFd' h m f unsafeWithHandleFd h@(DuplexHandle _ _ w) f = unsafeWithHandleFd' h w f unsafeWithHandleFd' :: Handle -> MVar Handle__ -> (Fd -> IO a) -> IO a unsafeWithHandleFd' h m f = withHandle' "unsafeWithHandleFd" h m $ \h_@Handle__{haDevice} -> case cast haDevice of Nothing -> ioError (System.IO.Error.ioeSetErrorString (System.IO.Error.mkIOError IllegalOperation "unsafeWithHandleFd" (Just h) Nothing) "handle is not a file descriptor") Just fd -> do x <- f (Fd (GHC.IO.FD.fdFD fd)) return (h_, x) snip---------------------------------------------------------------------- The first call of unsafeWithHandleFd, works as expected. The second one blocks. I need unsafeWithHandleFd, or something similar, in order to port my HsShellScript library (http://volker-wysk.de/hsshellscript/index.html) to the current version of GHC. If someone who understands the internals of the GHC IO libraries, had a hint, or even a fix, I'd be very grateful. Sincerely, Volker Wysk

Hi This is an addition to my previous post. This modified version of main seems to work: main = do fd <- unsafeWithHandleFd stdin return putStrLn ("stdin: fd = " ++ show fd) fd <- unsafeWithHandleFd stdout return putStrLn ("stdout: fd = " ++ show fd) The way I understand it, unsafeWithHandleFd's job is to keep a reference to the hande, so it won't be garbage collected, while the action is still running. Garbage collecting the handle would close it, as well as the underlying file descriptor, while the latter is still in use by the action. This can't happen as long as use of the file descriptor is encapsulated in the action. This encapsulation can be circumvented by returning the file descriptor, and that's what the modified main function above does. This should usually never be done. However, I want to use it with stdin, stdout and stderr, only. These three should never be garbage collected, should they? I think it would be safe to use unsafeWithHandleFd this way. Am I right? unsafeWithHandleFd is still broken (see previous message), but for my purposes it wouldn't necessarily need to be fixed. Happy hacking Volker Wysk

Can you use 'dup' to copy the file descriptor and return that version?
That will keep a reference to the file even if haskell closes the
original descriptor.
John
On Sat, Mar 10, 2012 at 5:31 PM, Volker Wysk
Hi
This is an addition to my previous post.
This modified version of main seems to work:
main = do
fd <- unsafeWithHandleFd stdin return putStrLn ("stdin: fd = " ++ show fd)
fd <- unsafeWithHandleFd stdout return putStrLn ("stdout: fd = " ++ show fd)
The way I understand it, unsafeWithHandleFd's job is to keep a reference to the hande, so it won't be garbage collected, while the action is still running. Garbage collecting the handle would close it, as well as the underlying file descriptor, while the latter is still in use by the action. This can't happen as long as use of the file descriptor is encapsulated in the action.
This encapsulation can be circumvented by returning the file descriptor, and that's what the modified main function above does. This should usually never be done.
However, I want to use it with stdin, stdout and stderr, only. These three should never be garbage collected, should they? I think it would be safe to use unsafeWithHandleFd this way. Am I right?
unsafeWithHandleFd is still broken (see previous message), but for my purposes it wouldn't necessarily need to be fixed.
Happy hacking Volker Wysk
_______________________________________________ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users

On 11/03/2012 01:31, Volker Wysk wrote:
Hi
This is an addition to my previous post.
This modified version of main seems to work:
main = do
fd<- unsafeWithHandleFd stdin return putStrLn ("stdin: fd = " ++ show fd)
fd<- unsafeWithHandleFd stdout return putStrLn ("stdout: fd = " ++ show fd)
The way I understand it, unsafeWithHandleFd's job is to keep a reference to the hande, so it won't be garbage collected, while the action is still running. Garbage collecting the handle would close it, as well as the underlying file descriptor, while the latter is still in use by the action. This can't happen as long as use of the file descriptor is encapsulated in the action.
This encapsulation can be circumvented by returning the file descriptor, and that's what the modified main function above does. This should usually never be done.
Right. The problem with this: -- Blocks unsafeWithHandleFd stdout $ \fd -> putStrLn ("stdout: fd = " ++ show fd) is that unsafeWithHandleFd is holding the lock on stdout, while you try to write to it with putStrLn. The implementation of unsafeWithHandleFd could probably be fixed to avoid this - as you say, all it needs is to hold a reference to the Handle until the function has returned. The usual way to "hold a reference" to something is to use touch#.
However, I want to use it with stdin, stdout and stderr, only.
Is there some reason you can't just use 0, 1, and 2?
These three should never be garbage collected, should they? I think it would be safe to use unsafeWithHandleFd this way. Am I right?
I wouldn't do that, but you're probably right that it is safe right now. (but no guarantees that it will continue to work for ever.) Cheers, Simon

Am Montag 12 März 2012, 12:31:27 schrieb Simon Marlow:
On 11/03/2012 01:31, Volker Wysk wrote:
However, I want to use it with stdin, stdout and stderr, only.
Is there some reason you can't just use 0, 1, and 2?
This is complicated. I want to be able to fork a child action, and communicate with it through stdin/stdout. I also want to be able to replace the child with an external program, and communicate with it through a stdout-stdin pipe. Something like this: subproc (outm "Durch die Röhre" -|- exec "/bin/cat" []) I don't rely on stdin/-out/-err still being open. One might be closed, and the file descriptor might be reallocated (for a subsequently opened file or whatever). I also don't rely on stdin being fd 0, stdout being fd 1, stderr being fd 2. If you really want to know what's going on, here is the documentation: http://volker-wysk.de/hsshellscript/apidoc/HsShellScript.html#v%3Aexecute_fi...
These three should never be garbage collected, should they? I think it would be safe to use unsafeWithHandleFd this way. Am I right?
I wouldn't do that, but you're probably right that it is safe right now. (but no guarantees that it will continue to work for ever.)
So I need a fixed unsafeWithHandleFd, for it to work forever? I guess, I'll leave it as it is, for now. Cheers Volker
participants (3)
-
John Meacham
-
Simon Marlow
-
Volker Wysk