On Tue, Sep 2, 2014 at 12:26 PM, Leon Smith <leon.p.smith@gmail.com> wrote:
On Tue, Sep 2, 2014 at 1:31 PM, Felipe Lessa <felipe.lessa@gmail.com> wrote:
I don't see how one could allow concurrent readers and "closers" without
leaving this small opening.  The best workaround I can think of is to
create a blocking close operation that waits for readers using a semaphore. 

Well yes,  I can't think of a simple lock-based solution to this problem,  because you don't want to hold any kind of lock while you are blocked on the file descriptor.

If you wanted to go this route, you could use an MVar (Maybe (Int,Fd)), where the Int is a count of interested threads.  Instead of using readMVar before threadWaitRead, you would use modifyMVar to atomically increment the counter and retrieve the fd.  Then, after threadWaitRead returns, decrement the counter.  You'd need to make sure that you never close an fd when the counter is greater than 0.  This would work better with a TMVar, because then the close operation could block until the counter has changed.
 
    It would be solvable though if we had a thread-safe

threadWaitReadMVar :: MVar Fd -> IO ()

You should be able to implement this relatively easily if you dig deeper into the IO manager itself,  but we already have threadWaitReadSTM.    (For different reasons, which doesn't cover this use case.)   How many variations on this theme are we going to need.

We could implement threadWaitReadMVar if we had a non-blocking way of registering interest in a file descriptor,  and then later actually blocking on it.   So let's say something like

registerWaitRead :: Fd -> IO (IO ())

threadWaitReadMVar fd = join $ withMVar fd registerWaitRead

Which,  ignoring asynchronous exceptions for the moment,  should be adequate for the task.    I suppose that means you could instead do

threadWaitReadMVar fd = (atomically . fst) =<< withMVar fd threadWaitReadSTM

Which seems like an odd use of STM,  but that also does seem like a solution.   So I guess the earlier part of this email (as well as eariler emails)  is in fact wrong,  that threadWaitReadSTM does cover this use case.     And STM might also offer a nicer way of making multiple reads from the inotify binding thread-safe as well.

This should work even in the presence of async exceptions, barring bugs in withMVar and threadWaitReadSTM.  You can implement your registerWaitRead using forkIO and MVars, but I think the only reason to do so would be for compatibility with older ghcs.  It's probably more sensible to just copy the definition of threadWaitReadSTM in that case, unless you want to target pre-STM compiler versions.

John