@@ -157,11 +157,21 @@ import Foreign.Ptr (Ptr)
157157import Foreign.Storable (peek, peekByteOff, sizeOf)
158158import GHC.IO.Device (IODeviceType(Stream))
159159import GHC.IO.FD (fdFD, mkFD)
160−import GHC.IO.Handle.FD (mkHandleFromFD, handleToFd)
160+import GHC.IO.Handle.FD (mkHandleFromFD)
161161import Streamly (SerialT)
162162import Streamly.Internal.Data.Parser (Parser)
163163import Streamly.Internal.Memory.Array.Types (Array(..))
164164import System.IO (Handle, hClose, IOMode(ReadMode))
165+#if !MIN_VERSION_base(4,10,0)
166+import Control.Concurrent.MVar (readMVar)
167+import Data.Typeable (cast)
168+import GHC.IO.Exception (IOException(..), IOErrorType(..), ioException)
169+import GHC.IO.FD (FD)
170+import GHC.IO.Handle.Types (Handle__(..), Handle(FileHandle, DuplexHandle))
171+#else
172+import GHC.IO.Handle.FD (handleToFd)
173+#endif
174+
165175
166176import qualified Data.IntMap.Lazy as Map
167177import qualified Streamly.Internal.Data.Fold as FL
@@ -543,6 +553,24 @@ foreign import ccall unsafe
543553utf8ToString :: Array Word8 -> String
544554utf8ToString = runIdentity . S.toList . U.decodeUtf8 . A.toStream
545555
556+#if !MIN_VERSION_base(4,10,0)
557+-- | Turn an existing Handle into a file descriptor. This function throws an
558+-- IOError if the Handle does not reference a file descriptor.
559+handleToFd :: Handle -> IO FD
560+handleToFd h = case h of
561+ FileHandle _ mv -> do
562+ Handle__{haDevice = dev} <- readMVar mv
563+ case cast dev of
564+ Just fd -> return fd
565+ Nothing -> throwErr "not a file descriptor"
566+ DuplexHandle{} -> throwErr "not a file handle"
567+
568+ where
569+
570+ throwErr msg = ioException $ IOError (Just h)
571+ InappropriateType "handleToFd" msg Nothing Nothing
572+#endif
573+
546574-- | Given a 'Config' and a @path@ supplied as a UTF-8 encoded byte array, add
547575-- the path to the list of paths being monitored by the given watch 'Handle'.
548576-- Returns a "watch descriptor" which is used as an idenitfier in the events