Asynchronous exceptions in threadWait

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|

Asynchronous exceptions in threadWait

Andrew Martin
The definition of threadWait is:
threadWait :: Event -> Fd -> IO ()
threadWait evt fd = mask_ $ do
  m <- newEmptyMVar
  mgr <- getSystemEventManager_
  reg <- registerFd mgr (\_ e -> putMVar m e) fd evt M.OneShot
  evt' <- takeMVar m `onException` unregisterFd_ mgr reg
  if evt' `eventIs` evtClose
    then ioError $ errnoToIOError "threadWait" eBADF Nothing Nothing
    else return ()
Although the entire function has asynchronous exceptions masked, the call to takeMVar uses onException to deal with the possibility of an exception. According to the docs in Control.Concurrent, takeMVar can throw exceptions. But my understand (which may be wrong) is that the only exception this could throw would be something like BlockedIndefinitelyOnMVar, which I don't believe can happen here.

--
-Andrew Thaddeus Martin

_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: Asynchronous exceptions in threadWait

Michael Walker
> Although the entire function has asynchronous exceptions masked, the call to takeMVar uses onException to deal with the possibility of an exception. According to the docs in Control.Concurrent, takeMVar can throw exceptions. But my understand (which may be wrong) is that the only exception this could throw would be something like BlockedIndefinitelyOnMVar, which I don't believe can happen here.

That's not quite right.  The mask_ function blocks asynchronous
exceptions from being delivered to the thread while it's not
"interruptible".  Most blocking functions, such as takeMVar, make the
thread interruptible while they're blocked.  So any asynchronous
exception (such as ThreadKilled) could be delivered to the thread if
it blocks in the takeMVar.

--
Michael Walker (http://www.barrucadu.co.uk)
_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: Asynchronous exceptions in threadWait

Andrew Martin
Thanks. That makes sense. So, in the code I’m looking at, since takeMVar is the last effectful thing that happens, there is no semantic difference between having mask_ extend all the way down to the bottom of the block (as it currently does) and cutting it off right before the line involving takeMVar?

Sent from my iPhone

On Jan 17, 2019, at 5:23 PM, Michael Walker <[hidden email]> wrote:

>> Although the entire function has asynchronous exceptions masked, the call to takeMVar uses onException to deal with the possibility of an exception. According to the docs in Control.Concurrent, takeMVar can throw exceptions. But my understand (which may be wrong) is that the only exception this could throw would be something like BlockedIndefinitelyOnMVar, which I don't believe can happen here.
>
> That's not quite right.  The mask_ function blocks asynchronous
> exceptions from being delivered to the thread while it's not
> "interruptible".  Most blocking functions, such as takeMVar, make the
> thread interruptible while they're blocked.  So any asynchronous
> exception (such as ThreadKilled) could be delivered to the thread if
> it blocks in the takeMVar.
>
> --
> Michael Walker (http://www.barrucadu.co.uk)
_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: Asynchronous exceptions in threadWait

David Feuer
In reply to this post by Andrew Martin
Michael Walker explained quite well why that's reasonable. What seems much *less* reasonable is that the function masks exceptions right from the start and all the way to the end. I don't see any reason to mask until after calling getSystemEventManager_, or to remain masked after taking the MVar.

On Thu, Jan 17, 2019 at 5:17 PM Andrew Martin <[hidden email]> wrote:
The definition of threadWait is:
threadWait :: Event -> Fd -> IO ()
threadWait evt fd = mask_ $ do
  m <- newEmptyMVar
  mgr <- getSystemEventManager_
  reg <- registerFd mgr (\_ e -> putMVar m e) fd evt M.OneShot
  evt' <- takeMVar m `onException` unregisterFd_ mgr reg
  if evt' `eventIs` evtClose
    then ioError $ errnoToIOError "threadWait" eBADF Nothing Nothing
    else return ()
Although the entire function has asynchronous exceptions masked, the call to takeMVar uses onException to deal with the possibility of an exception. According to the docs in Control.Concurrent, takeMVar can throw exceptions. But my understand (which may be wrong) is that the only exception this could throw would be something like BlockedIndefinitelyOnMVar, which I don't believe can happen here.

--
-Andrew Thaddeus Martin
_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries

_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: Asynchronous exceptions in threadWait

Michael Walker
In reply to this post by Andrew Martin
So, something like this?

    threadWait :: Event -> Fd -> IO ()
    threadWait evt fd = do
      (m, mgr, reg) <- mask_ $ do
        m <- newEmptyMVar
        mgr <- getSystemEventManager_
        reg <- registerFd mgr (\_ e -> putMVar m e) fd evt M.OneShot
        pure (m, mgr, reg)
      evt' <- takeMVar m `onException` unregisterFd_ mgr reg
      if evt' `eventIs` evtClose
        then ioError $ errnoToIOError "threadWait" eBADF Nothing Nothing
        else return ()

I'm not sure this is semantically equivalent.

GHC can pre-empt a thread whenever it allocates memory, and I assume
it can deliver exceptions at that granularity too.  So if
`onException` allocates memory before the exception handler is set up,
then an exception could possibly be delivered in that brief window,
which would result in `unregisterFd_ mgr reg` never being called, and
an exception being raised in the caller of `threadWait`.

On Thu, 17 Jan 2019 at 22:43, Andrew Martin <[hidden email]> wrote:

>
> Thanks. That makes sense. So, in the code I’m looking at, since takeMVar is the last effectful thing that happens, there is no semantic difference between having mask_ extend all the way down to the bottom of the block (as it currently does) and cutting it off right before the line involving takeMVar?
>
> Sent from my iPhone
>
> On Jan 17, 2019, at 5:23 PM, Michael Walker <[hidden email]> wrote:
>
> >> Although the entire function has asynchronous exceptions masked, the call to takeMVar uses onException to deal with the possibility of an exception. According to the docs in Control.Concurrent, takeMVar can throw exceptions. But my understand (which may be wrong) is that the only exception this could throw would be something like BlockedIndefinitelyOnMVar, which I don't believe can happen here.
> >
> > That's not quite right.  The mask_ function blocks asynchronous
> > exceptions from being delivered to the thread while it's not
> > "interruptible".  Most blocking functions, such as takeMVar, make the
> > thread interruptible while they're blocked.  So any asynchronous
> > exception (such as ThreadKilled) could be delivered to the thread if
> > it blocks in the takeMVar.
> >
> > --
> > Michael Walker (http://www.barrucadu.co.uk)



--
Michael Walker (http://www.barrucadu.co.uk)
_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: Asynchronous exceptions in threadWait

David Feuer
No, something like

threadWait :: Event -> Fd -> IO ()
threadWait evt fd = do
  m <- newEmptyMVar
  mgr <- getSystemEventManager_
  evt' <- mask_ $ do
      reg <- registerFd mgr (\_ e -> putMVar m e) fd evt M.OneShot
      takeMVar m `onException` unregisterFd_ mgr reg
  if evt' `eventIs` evtClose
    then ioError $ errnoToIOError "threadWait" eBADF Nothing Nothing
    else return ()

On Thu, Jan 17, 2019 at 5:58 PM Michael Walker <[hidden email]> wrote:
So, something like this?

    threadWait :: Event -> Fd -> IO ()
    threadWait evt fd = do
      (m, mgr, reg) <- mask_ $ do
        m <- newEmptyMVar
        mgr <- getSystemEventManager_
        reg <- registerFd mgr (\_ e -> putMVar m e) fd evt M.OneShot
        pure (m, mgr, reg)
      evt' <- takeMVar m `onException` unregisterFd_ mgr reg
      if evt' `eventIs` evtClose
        then ioError $ errnoToIOError "threadWait" eBADF Nothing Nothing
        else return ()

I'm not sure this is semantically equivalent.

GHC can pre-empt a thread whenever it allocates memory, and I assume
it can deliver exceptions at that granularity too.  So if
`onException` allocates memory before the exception handler is set up,
then an exception could possibly be delivered in that brief window,
which would result in `unregisterFd_ mgr reg` never being called, and
an exception being raised in the caller of `threadWait`.

On Thu, 17 Jan 2019 at 22:43, Andrew Martin <[hidden email]> wrote:
>
> Thanks. That makes sense. So, in the code I’m looking at, since takeMVar is the last effectful thing that happens, there is no semantic difference between having mask_ extend all the way down to the bottom of the block (as it currently does) and cutting it off right before the line involving takeMVar?
>
> Sent from my iPhone
>
> On Jan 17, 2019, at 5:23 PM, Michael Walker <[hidden email]> wrote:
>
> >> Although the entire function has asynchronous exceptions masked, the call to takeMVar uses onException to deal with the possibility of an exception. According to the docs in Control.Concurrent, takeMVar can throw exceptions. But my understand (which may be wrong) is that the only exception this could throw would be something like BlockedIndefinitelyOnMVar, which I don't believe can happen here.
> >
> > That's not quite right.  The mask_ function blocks asynchronous
> > exceptions from being delivered to the thread while it's not
> > "interruptible".  Most blocking functions, such as takeMVar, make the
> > thread interruptible while they're blocked.  So any asynchronous
> > exception (such as ThreadKilled) could be delivered to the thread if
> > it blocks in the takeMVar.
> >
> > --
> > Michael Walker (http://www.barrucadu.co.uk)



--
Michael Walker (http://www.barrucadu.co.uk)
_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries

_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: Asynchronous exceptions in threadWait

Andrew Martin
In reply to this post by Michael Walker
Ah, that makes sense. They certainly differ in the guarantees they provide around when unregisterFd_ is called. Although I think the exception would still be raised in the caller of threadWait regardless of which way it was written.

Sent from my iPhone

> On Jan 17, 2019, at 5:58 PM, Michael Walker <[hidden email]> wrote:
>
> So, something like this?
>
>    threadWait :: Event -> Fd -> IO ()
>    threadWait evt fd = do
>      (m, mgr, reg) <- mask_ $ do
>        m <- newEmptyMVar
>        mgr <- getSystemEventManager_
>        reg <- registerFd mgr (\_ e -> putMVar m e) fd evt M.OneShot
>        pure (m, mgr, reg)
>      evt' <- takeMVar m `onException` unregisterFd_ mgr reg
>      if evt' `eventIs` evtClose
>        then ioError $ errnoToIOError "threadWait" eBADF Nothing Nothing
>        else return ()
>
> I'm not sure this is semantically equivalent.
>
> GHC can pre-empt a thread whenever it allocates memory, and I assume
> it can deliver exceptions at that granularity too.  So if
> `onException` allocates memory before the exception handler is set up,
> then an exception could possibly be delivered in that brief window,
> which would result in `unregisterFd_ mgr reg` never being called, and
> an exception being raised in the caller of `threadWait`.
>
>> On Thu, 17 Jan 2019 at 22:43, Andrew Martin <[hidden email]> wrote:
>>
>> Thanks. That makes sense. So, in the code I’m looking at, since takeMVar is the last effectful thing that happens, there is no semantic difference between having mask_ extend all the way down to the bottom of the block (as it currently does) and cutting it off right before the line involving takeMVar?
>>
>> Sent from my iPhone
>>
>> On Jan 17, 2019, at 5:23 PM, Michael Walker <[hidden email]> wrote:
>>
>>>> Although the entire function has asynchronous exceptions masked, the call to takeMVar uses onException to deal with the possibility of an exception. According to the docs in Control.Concurrent, takeMVar can throw exceptions. But my understand (which may be wrong) is that the only exception this could throw would be something like BlockedIndefinitelyOnMVar, which I don't believe can happen here.
>>>
>>> That's not quite right.  The mask_ function blocks asynchronous
>>> exceptions from being delivered to the thread while it's not
>>> "interruptible".  Most blocking functions, such as takeMVar, make the
>>> thread interruptible while they're blocked.  So any asynchronous
>>> exception (such as ThreadKilled) could be delivered to the thread if
>>> it blocks in the takeMVar.
>>>
>>> --
>>> Michael Walker (http://www.barrucadu.co.uk)
>
>
>
> --
> Michael Walker (http://www.barrucadu.co.uk)
_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: Asynchronous exceptions in threadWait

Haskell - Libraries mailing list
In reply to this post by Andrew Martin

Who are all you people and why am I receiving emails from you? Please stop. Take me off list. Thanks




On Thursday, January 17, 2019 Andrew Martin <[hidden email]> wrote:

Ah, that makes sense. They certainly differ in the guarantees they provide around when unregisterFd_ is called. Although I think the exception would still be raised in the caller of threadWait regardless of which way it was written.

Sent from my iPhone

> On Jan 17, 2019, at 5:58 PM, Michael Walker <[hidden email]> wrote:
>
> So, something like this?
>
>    threadWait :: Event -> Fd -> IO ()
>    threadWait evt fd = do
>      (m, mgr, reg) <- mask_ $ do
>        m <- newEmptyMVar
>        mgr <- getSystemEventManager_
>        reg <- registerFd mgr (\_ e -> putMVar m e) fd evt M.OneShot
>        pure (m, mgr, reg)
>      evt' <- takeMVar m `onException` unregisterFd_ mgr reg
>      if evt' `eventIs` evtClose
>        then ioError $ errnoToIOError "threadWait" eBADF Nothing Nothing
>        else return ()
>
> I'm not sure this is semantically equivalent.
>
> GHC can pre-empt a thread whenever it allocates memory, and I assume
> it can deliver exceptions at that granularity too.  So if
> `onException` allocates memory before the exception handler is set up,
> then an exception could possibly be delivered in that brief window,
> which would result in `unregisterFd_ mgr reg` never being called, and
> an exception being raised in the caller of `threadWait`.
>
>> On Thu, 17 Jan 2019 at 22:43, Andrew Martin <[hidden email]> wrote:
>>
>> Thanks. That makes sense. So, in the code I’m looking at, since takeMVar is the last effectful thing that happens, there is no semantic difference between having mask_ extend all the way down to the bottom of the block (as it currently does) and cutting it off right before the line involving takeMVar?
>>
>> Sent from my iPhone
>>
>> On Jan 17, 2019, at 5:23 PM, Michael Walker <[hidden email]> wrote:
>>
>>>> Although the entire function has asynchronous exceptions masked, the call to takeMVar uses onException to deal with the possibility of an exception. According to the docs in Control.Concurrent, takeMVar can throw exceptions. But my understand (which may be wrong) is that the only exception this could throw would be something like BlockedIndefinitelyOnMVar, which I don't believe can happen here.
>>>
>>> That's not quite right.  The mask_ function blocks asynchronous
>>> exceptions from being delivered to the thread while it's not
>>> "interruptible".  Most blocking functions, such as takeMVar, make the
>>> thread interruptible while they're blocked.  So any asynchronous
>>> exception (such as ThreadKilled) could be delivered to the thread if
>>> it blocks in the takeMVar.
>>>
>>> --
>>> Michael Walker (http://www.barrucadu.co.uk)
>
>
>
> --
> Michael Walker (http://www.barrucadu.co.uk)
_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries

_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries