How to synchronously shutdown the event manager loop

Hello, In my (still unreleased) usb-1.0 (https://github.com/basvandijk/usb) library I use the GHC event manager for managing events from the underlying `libusb` C library. To work with the library a user has to initialize it using: newCtx ∷ IO Ctx The `Ctx` then allows the user to see the USB devices attached to the system using: getDevices ∷ Ctx → IO [Device]
From thereon a user can open devices using:
openDevice ∷ Device → IO DeviceHandle and perform IO with them: readBulk ∷ DeviceHandle → EndpointAddress → Size → Timeout → IO (B.ByteString, Status) Internally the synchronous `readBulk` is implemented using asynchronous IO: first a request to read bytes from a USB device is asynchronously submitted. Then it waits on a lock (`takeMVar lock`). When the bytes are read an event is fired on a certain file descriptor. The GHC event manager then wakes up and calls a callback which will release the lock (`putMVar lock ()`). I choose to have one event loop per session (`Ctx`) with the usb library. This means that I make a new `EventManager` in `newCtx`: evtMgr ← EventManager.new register the `libusb` file descriptors with the event manager and fork a thread which will `loop` the `EventManager`: tid <- forkIOWithUnmask $ \unmask → unmask $ EventManager.loop evtMgr The underlying `libusb` C library needs to be deinitialized when you're done with it. However for safety and ease of use a user of my Haskell library doesn't need to explicitly deinitialize the library. Instead this happens automatically when the garbage collector collects an unreferenced `Ctx` by registering (using `Foreign.Concurrent.newForeignPtr`) the following finalizer with the `Ctx` foreign pointer: -- Stop the event handling loop by killing its thread: killThread tid ... -- Finally deinitialize libusb: c'libusb_exit ctxPtr As you see I also kill the thread which is running the event manager loop. However I think this is not the right way to do it because when I use the library I see the following message being continually printed after the `Ctx` is finalized: ghc: ioManagerWakeup: write: Bad file descriptor Note that when I remove the `killThread tid` the messages disappear. It seems that killing the event manager loop is not allowed. Is there another way of shutting down the event manager loop? I know I can shut it down by using the `shutdown` function. However this function is asynchronous. This won't work because I need to make sure the event loop is shutdown before I exit the `libusb` library (because that will close the file descriptors that the event loop is working with). I see the event manager provides the `finished` function which will poll the manager to see if it stopped looping. This function is not exported however. Is it possible to export this function? It would be even nicer to have a synchronous shutdown function so that I don't need to setup a loop that waits till the manager has been shut down. Regards, Bas

On Tue, Aug 30, 2011 at 6:49 AM, Bas van Dijk
As you see I also kill the thread which is running the event manager loop. However I think this is not the right way to do it because when I use the library I see the following message being continually printed after the `Ctx` is finalized:
ghc: ioManagerWakeup: write: Bad file descriptor
I'm afraid we don't provide a way to shut down an event manager synchronously at the moment. You'll have to submit a patch if you want to add that capability. Perhaps you could add a way to specify an "onShutdown :: IO ()" hook that would be called by the event manager thread once it shuts down.

On 30 August 2011 17:39, Bryan O'Sullivan
On Tue, Aug 30, 2011 at 6:49 AM, Bas van Dijk
wrote: As you see I also kill the thread which is running the event manager loop. However I think this is not the right way to do it because when I use the library I see the following message being continually printed after the `Ctx` is finalized:
ghc: ioManagerWakeup: write: Bad file descriptor
I'm afraid we don't provide a way to shut down an event manager synchronously at the moment. You'll have to submit a patch if you want to add that capability.
I see what I can do. I'm first going to export the 'finished' function from GHC.Event and use that to wait till the loop finishes and see if that solves my problem. BTW to reproduce the problem (if you're interested) you can use the following program: (make sure to use the latest usb sources from git) import System.USB (newCtx) import Control.Concurrent (threadDelay) import System.Mem (performGC) main :: IO () main = do _ <- newCtx threadDelay 2000000 performGC threadDelay 2000000 running this yields: example: ioManagerWakeup: write: Bad file descriptor example: ioManagerDie: write: Bad file descriptor
Perhaps you could add a way to specify an "onShutdown :: IO ()" hook that would be called by the event manager thread once it shuts down.
That's a possibility. BTW is there a reason 'shutdown' needs to be asynchronous or was it just easier to implement it this way? Otherwise I think a synchronous 'shutdown' is easier to work with. Regards, Bas

On 31 August 2011 00:15, Bas van Dijk
I see what I can do. I'm first going to export the 'finished' function from GHC.Event and use that to wait till the loop finishes and see if that solves my problem.
Waiting till the loop finishes doesn't solve the problem. Here's an isolated program with the same problem that doesn't use the usb library: import Control.Concurrent import GHC.Event main = do em <- new forkIO $ loop em threadDelay 2000000 shutdown em threadDelay 2000000 When executed it prints: example: ioManagerWakeup: write: Bad file descriptor example: ioManagerDie: write: Bad file descriptor So it seems like a bug in GHC. I will create a ticket in the morning. Regards, Bas

On 31 August 2011 01:11, Bas van Dijk
So it seems like a bug in GHC. I will create a ticket in the morning.
Ticket created: http://hackage.haskell.org/trac/ghc/ticket/5443
participants (2)
-
Bas van Dijk
-
Bryan O'Sullivan