You must create a type around the file descriptor and a mutex lock. Here's a simple implementation that I think will work for your purposes.
module SharedHandle (SharedHandle, newSharedHandle, withSharedHandle) where import Control.Concurrent.MVar import System.IO data SharedHandle = SharedHandle Handle (MVar ()) newSharedHandle :: IO Handle -> IO SharedHandle newSharedHandle makeHandle = do handle <- makeHandle lock <- newMVar() return $ SharedHandle handle lock withSharedHandle :: SharedHandle -> (Handle -> IO a) -> IO a withSharedHandle (SharedHandle handle lock) operation = do () <- takeMVar lock val <- operation handle putMVar lock () return val
What is being done here, I created a new data type, which is essentially just a file descriptor. The only difference is that it is also equipped with its own individual mutex lock implemented with MVar. I have provided two functions to work on this new type. newSharedHandle performs an operation that would create a normal descriptor and create a shared descriptor with a fresh lock. withSharedHandle performs an operation to work with descriptors, locks a common descriptor, performs an operation, and then unlocks the descriptor. Please note that the constructor or accessors are not provided from the module, so we can be sure that no process forgets to release the lock, and we never get deadlocks on one specific access.
Replacing all file descriptors in your program with this new type may solve your problem.
source share