<div dir="ltr">The documentation for threadWaitRead states:<div><p style="margin:0.8em 0px;padding:0px;color:rgb(0,0,0);font-family:sans-serif;font-size:13px;line-height:18.200000762939453px">    Block the current thread until data is available to read on the given file descriptor (GHC only).</p>
<p style="margin:0.8em 0px;padding:0px;color:rgb(0,0,0);font-family:sans-serif;font-size:13px;line-height:18.200000762939453px">    This will throw an <code style="margin:0px;padding:0px;line-height:16.1200008392334px">IOError</code> if the file descriptor was closed while this thread was blocked. To safely close a file descriptor that has been used with <code style="margin:0px;padding:0px;line-height:16.1200008392334px"><a href="http://hackage.haskell.org/package/base-4.7.0.1/docs/GHC-Conc-IO.html#v:threadWaitRead" style="margin:0px;padding:0px;text-decoration:none;color:rgb(171,105,84)">threadWaitRead</a></code>, use <code style="margin:0px;padding:0px;line-height:16.1200008392334px"><a href="http://hackage.haskell.org/package/base-4.7.0.1/docs/GHC-Conc-IO.html#v:closeFdWith" style="margin:0px;padding:0px;text-decoration:none;color:rgb(171,105,84)">closeFdWith</a></code>.</p>
<p style="margin:0.8em 0px;padding:0px;color:rgb(0,0,0);font-family:sans-serif;font-size:13px;line-height:18.200000762939453px">So what should happen in your situation is that, when a separate thread closes the fd, threadWaitRead will throw an exception promptly, not continue to wait on a now-closed fd.</p>
<p style="margin:0.8em 0px;padding:0px;color:rgb(0,0,0);font-family:sans-serif;font-size:13px;line-height:18.200000762939453px">Note the docs for closeFdWith:</p><p style="margin:0.8em 0px;padding:0px;color:rgb(0,0,0);font-family:sans-serif;font-size:13px;line-height:18.200000762939453px">
<span style="font-size:13px">    Close a file descriptor in a concurrency-safe way (GHC only). If you are using </span><code style="margin:0px;padding:0px;line-height:16.1200008392334px;font-size:13px"><a href="http://hackage.haskell.org/package/base-4.7.0.1/docs/GHC-Conc-IO.html#v:threadWaitRead" style="margin:0px;padding:0px;text-decoration:none;color:rgb(171,105,84)">threadWaitRead</a></code><span style="font-size:13px"> or </span><code style="margin:0px;padding:0px;line-height:16.1200008392334px;font-size:13px"><a href="http://hackage.haskell.org/package/base-4.7.0.1/docs/GHC-Conc-IO.html#v:threadWaitWrite" style="margin:0px;padding:0px;text-decoration:none;color:rgb(171,105,84)">threadWaitWrite</a></code><span style="font-size:13px"> to perform blocking I/O, you </span><em style="margin:0px;padding:0px;font-size:13px">must</em><span style="font-size:13px"> use this function to close file descriptors, or blocked threads may not be woken.</span></p>
<p style="margin:0.8em 0px;padding:0px;color:rgb(0,0,0);font-family:sans-serif;font-size:13px;line-height:18.200000762939453px"><span style="font-size:13px">So I think the short answer is, if you're using fd's and threadWaitRead, and you always use closeFdWith, you should be protected against operating on a re-used fd index, even with wrapping your fd's in a Maybe.</span></p>
<p style="margin:0.8em 0px;padding:0px;color:rgb(0,0,0);font-family:sans-serif;font-size:13px;line-height:18.200000762939453px">I seem to recall that there are some other issues that arise if you attempt to mix these low-level fd calls with higher-level fd operations, but that was two IO managers ago so those concerns may be out of date.</p>
<p style="margin:0.8em 0px;padding:0px;color:rgb(0,0,0);font-family:sans-serif;font-size:13px;line-height:18.200000762939453px">John L.</p></div></div><div class="gmail_extra"><br><br><div class="gmail_quote">On Mon, Sep 1, 2014 at 4:17 PM, Leon Smith <span dir="ltr"><<a href="mailto:leon.p.smith@gmail.com" target="_blank">leon.p.smith@gmail.com</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr">I was very tangentially involved with a use-after-close IO descriptor fault recently,  and I came to realize that descriptor indexes are typically allocated by the smallest available index,   Previously,  I had erroneously believed that the indexes were sequentially allocated by an ever-increasing counter,  until wrap-around occurs.<div>

<br></div><div>Obviously,  the smallest available index allocation strategy makes use-after-close faults rather more significant,  because in a server application that is constantly closing and allocating descriptors,  it makes it rather more likely that an erroneous operation will actually have a tangible effect,  and not simply return an EINVAL or similar errno.</div>

<div><br></div><div>So in my low-level linux-inotify binding,  I felt it wise to add protection against use-after-close faults.   The approach I'm currently investigating is the obvious one:   to (conceptually) represent the inotify socket by a "MVar (Maybe Fd)" value.</div>

<div><br></div><div>However, if there's no file system events to read from the socket,   we want to call threadWaitRead,  So somewhere there's some code that's essentially this:</div><div><br></div><div>     readMVar inotifyfd >>= maybe (throwIO ...) threadWaitRead</div>

<div><br></div><div>So the problem is,  what happens when the thread executing this snippet yields in between the readMVar and the threadWaitRead?      It would then be possible for another thread (or two) to close the inotify descriptor,   and then allocate another descriptor with the same index.   The first thread would then end up blocking until the new descriptor becomes readable,   after which the thread would re-read the MVar and throw an exception that the descriptor has been closed.</div>

<div><br></div><div>This is a _relatively_ benign effect,  but still,  it does prevent the waiting thread from throwing an exception at the earliest possible moment,   and there are several particularly unlucky scenarios I can think of where such a thread could be waiting on the wrong descriptor for a very long time.  Not to mention that this is a whole family of race conditions,  which I haven't really explored the other possible manifestations of which might not be as benign.</div>

<div><br></div><div>Somebody did also point me to the new threadWaitReadSTM primitives,  but this doesn't cover this use case although the original proposal (properly implemented,  of course) would:</div><div><br></div>

<div><a href="http://www.haskell.org/ghc/docs/7.8.3/html/libraries/base-4.7.0.1/Control-Concurrent.html" target="_blank">http://www.haskell.org/ghc/docs/7.8.3/html/libraries/base-4.7.0.1/Control-Concurrent.html</a><br></div>
<div><a href="https://ghc.haskell.org/trac/ghc/ticket/7216" target="_blank">https://ghc.haskell.org/trac/ghc/ticket/7216</a><br>
</div><div><br></div><div><br></div><div>In any case,  my linux-inotify binding is available on github;   the current main branch is unreleased and also has code to attempt to make reading from a descriptor a thread-safe operation.    However I'm not too enthusiastic about this unreleased code at the moment, and am considering throwing it out.   However,  I'd still very much like to add protection against use-after-close faults,  and I'd certainly prefer being able to concurrently call both close and read on the descriptor with complete safety.</div>

<div><br></div><div><a href="https://github.com/lpsmith/linux-inotify" target="_blank">https://github.com/lpsmith/linux-inotify</a></div><div><br></div><div>Best,<br>Leon</div><div><br></div><div><br></div></div>
<br>_______________________________________________<br>
Haskell-Cafe mailing list<br>
<a href="mailto:Haskell-Cafe@haskell.org">Haskell-Cafe@haskell.org</a><br>
<a href="http://www.haskell.org/mailman/listinfo/haskell-cafe" target="_blank">http://www.haskell.org/mailman/listinfo/haskell-cafe</a><br>
<br></blockquote></div><br></div>