Opened 7 years ago

Closed 7 years ago

#5866 closed bug (fixed)

Interrupting interleaved STM and resuming it produces segfault

Reported by: joeyadams Owned by: simonmar
Priority: high Milestone: 7.4.2
Component: Runtime System Version: 7.4.1
Keywords: Cc:
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: Runtime crash Test Case:
Blocked By: Blocking:
Related Tickets: Differential Rev(s):
Wiki Page:

Description

The following program produces a segfault:

import Control.Concurrent.STM
import Control.Exception
import System.IO.Unsafe
import System.Timeout

main :: IO ()
main = do
    x <- unsafeInterleaveIO $ atomically retry
    _ <- timeout 1000000 $ evaluate x
    evaluate x

When a thread is interrupted while evaluating interleaved IO, and that IO is blocked on an STM transaction, resuming the suspended computation produces a segfault.

This segfault happens with GHC 7.0.3, 7.2.2, and 7.4.1, on both Windows 32-bit and Linux 64-bit, with and without -threaded.

Compiling with -debug on 7.5.20120211, I get an assertion failure in rts/STM.c, line 1525:

ASSERT (trec != NO_TREC);

In general, pure computations are not always suspended by asynchronous exceptions:

import Control.Concurrent
import System.IO.Unsafe
import System.Timeout

main :: IO ()
main = do
    -- Resuming interleaved takeMVar works correctly
    mv <- newEmptyMVar
    _ <- forkIO $ threadDelay 2000000 >> putMVar mv "Hello"
    s <- unsafeInterleaveIO $ takeMVar mv
    _ <- timeout 1000000 $ putStrLn s
    putStrLn s

    -- But resuming getLine repeats the asynchronous exception, contrary to the
    -- documentation of 'throwTo'
    s <- unsafeInterleaveIO getLine
    _ <- timeout 1000000 $ putStrLn s
    putStrLn s

Note issue #5859 when testing: unsafeInterleaveIO being INLINE causes computations to be duplicated when shared among threads. The examples above dodge this issue by keeping the work in the current thread.

Change History (6)

comment:1 Changed 7 years ago by simonmar

difficulty: Unknown
Milestone: 7.4.2
Owner: set to simonmar
Priority: normalhigh

Another nice bug report. Thanks!

comment:2 Changed 7 years ago by simonmar

I have a fix for the segfault that I'll commit shortly.

The reason that getLine does not resume correctly is that the IO library does not recognise Timeout as an asynchronous exception - it looks for the AsyncException type. Really this is wrong, because whether an exception is asynchronous is a property of how it was thrown, not the exception itself, but we don't currently have a way for the handler to tell whether an exception was thrown asynchronously or not. That is a topic for another ticket, which I'll create...

comment:3 Changed 7 years ago by simonmar

Related tickets: #2401, #3997, #2558

comment:4 Changed 7 years ago by marlowsd@…

commit 014f1e1feee4c85a82f787ef8f01b44072051172

Author: Simon Marlow <marlowsd@gmail.com>
Date:   Mon Feb 27 14:32:44 2012 +0000

    raiseAsync: cope with ATOMICALLY_FRAMES inside UPDATE_FRAMES (#5866)

 includes/stg/MiscClosures.h |    1 +
 rts/PrimOps.cmm             |   10 ++++++
 rts/RaiseAsync.c            |   67 ++++++++++++++++++++++++++++++++++++-------
 3 files changed, 67 insertions(+), 11 deletions(-)

comment:5 Changed 7 years ago by simonmar

Status: newmerge

See #5902 for the remaining bug.

comment:6 Changed 7 years ago by pcapriotti

Resolution: fixed
Status: mergeclosed
Note: See TracTickets for help on using tickets.