Duncan Coutts pushed to branch wip/dcoutts/io-manager-io-primop-exceptions at Glasgow Haskell Compiler / GHC

Commits:

14 changed files:

Changes:

  • compiler/GHC/Builtin/primops.txt.pp
    ... ... @@ -3305,21 +3305,28 @@ section "Delay/wait operations"
    3305 3305
     
    
    3306 3306
     primop  DelayOp "delay#" GenPrimOp
    
    3307 3307
        Int# -> State# s -> State# s
    
    3308
    -   {Sleep specified number of microseconds.}
    
    3308
    +   {Suspend the calling thread for a specified number of microseconds. This
    
    3309
    +    operation is /interruptible/ by async exceptions.}
    
    3309 3310
        with
    
    3310 3311
        effect = ReadWriteEffect
    
    3311 3312
        out_of_line      = True
    
    3312 3313
     
    
    3313 3314
     primop  WaitReadOp "waitRead#" GenPrimOp
    
    3314 3315
        Int# -> State# s -> State# s
    
    3315
    -   {Block until input is available on specified file descriptor.}
    
    3316
    +   {Suspend the calling thread until input is available (or an I/O error
    
    3317
    +    occurs) on the given file descriptor. This operation can throw exceptions
    
    3318
    +    of type 'IOException'. The operation is also /interruptible/ by async
    
    3319
    +    exceptions.}
    
    3316 3320
        with
    
    3317 3321
        effect = ReadWriteEffect
    
    3318 3322
        out_of_line      = True
    
    3319 3323
     
    
    3320 3324
     primop  WaitWriteOp "waitWrite#" GenPrimOp
    
    3321 3325
        Int# -> State# s -> State# s
    
    3322
    -   {Block until output is possible on specified file descriptor.}
    
    3326
    +   {Suspend the calling thread until output is possible (or an I/O error
    
    3327
    +    occurs) on the given file descriptor. This operation can throw exceptions
    
    3328
    +    of type 'IOException'. The operation is also /interruptible/ by async
    
    3329
    +    exceptions.}
    
    3323 3330
        with
    
    3324 3331
        effect = ReadWriteEffect
    
    3325 3332
        out_of_line      = True
    

  • libraries/ghc-internal/include/RtsIfaceSymbols.h
    ... ... @@ -23,11 +23,6 @@ CLOSURE(GHCziInternalziIOziException, cannotCompactMutable_closure)
    23 23
     CLOSURE(GHCziInternalziControlziExceptionziBase, nonTermination_closure)
    
    24 24
     CLOSURE(GHCziInternalziControlziExceptionziBase, nestedAtomically_closure)
    
    25 25
     CLOSURE(GHCziInternalziControlziExceptionziBase, noMatchingContinuationPrompt_closure)
    
    26
    -#if defined(mingw32_HOST_OS)
    
    27
    -UNDEF_CLOSURE(GHCziInternalziEventziThread, blockedOnBadFD_closure)
    
    28
    -#else
    
    29
    -CLOSURE(GHCziInternalziEventziThread, blockedOnBadFD_closure)
    
    30
    -#endif
    
    31 26
     CLOSURE(GHCziInternalziConcziSync, runSparks_closure)
    
    32 27
     CLOSURE(GHCziInternalziConcziIO, ensureIOManagerIsRunning_closure)
    
    33 28
     CLOSURE(GHCziInternalziConcziIO, interruptIOManager_closure)
    

  • libraries/ghc-internal/src/GHC/Internal/Event/Thread.hs
    ... ... @@ -23,14 +23,13 @@ import GHC.Internal.Types ()
    23 23
         , closeFdWith
    
    24 24
         , threadDelay
    
    25 25
         , registerDelay
    
    26
    -    , blockedOnBadFD -- used by RTS
    
    27 26
         ) where
    
    28 27
     
    
    29 28
     
    
    30 29
     -- TODO: Use new Windows I/O manager
    
    31 30
     import qualified GHC.Internal.Stack.Types as Rebindable
    
    32 31
     import GHC.Internal.Base
    
    33
    -import GHC.Internal.Control.Exception (finally, SomeException, toException)
    
    32
    +import GHC.Internal.Control.Exception (finally)
    
    34 33
     import GHC.Internal.Data.Foldable (forM_, mapM_, sequence_)
    
    35 34
     import GHC.Internal.Data.IORef (IORef, newIORef, readIORef, writeIORef, atomicWriteIORef)
    
    36 35
     import GHC.Internal.Data.Maybe (fromMaybe)
    
    ... ... @@ -185,10 +184,6 @@ threadWait evt fd = mask_ $ do
    185 184
         then ioError $ errnoToIOError "threadWait" eBADF Nothing Nothing
    
    186 185
         else return ()
    
    187 186
     
    
    188
    --- used at least by RTS in 'select()' IO manager backend
    
    189
    -blockedOnBadFD :: SomeException
    
    190
    -blockedOnBadFD = toException $ errnoToIOError "awaitEvent" eBADF Nothing Nothing
    
    191
    -
    
    192 187
     threadWaitSTM :: Event -> Fd -> IO (STM (), IO ())
    
    193 188
     threadWaitSTM evt fd = mask_ $ do
    
    194 189
       m <- newTVarIO Nothing
    

  • rts/HeapStackCheck.cmm
    ... ... @@ -40,6 +40,8 @@ import CLOSURE stg_ret_l_info;
    40 40
     import CLOSURE stg_ret_n_info;
    
    41 41
     import CLOSURE stg_ret_p_info;
    
    42 42
     import CLOSURE stg_stack_save_entries;
    
    43
    +import CLOSURE stg_block_io_unit_info;
    
    44
    +import CLOSURE stg_block_io_int_info;
    
    43 45
     #endif
    
    44 46
     
    
    45 47
     /* Stack/Heap Check Failure
    
    ... ... @@ -791,3 +793,169 @@ stg_block_async
    791 793
     }
    
    792 794
     #endif
    
    793 795
     
    
    796
    +/* Note [Thread blocking for new I/O primops]
    
    797
    +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    798
    +
    
    799
    +The classic I/O primops are:
    
    800
    +
    
    801
    +  waitRead#, waitWrite# :: Int# -> State# s -> State# s
    
    802
    +
    
    803
    +Note that they have no result, with wrapper types returning IO (). Each one
    
    804
    +simply suspends the calling thread until a condition occurs (I/O or time).
    
    805
    +
    
    806
    +Despite this external simplicity, internally there is more complexity with a
    
    807
    +number of cases to handle:
    
    808
    +
    
    809
    +At submission time:
    
    810
    + - synchronous failure, e.g. bad fd
    
    811
    + - synchronous success, e.g. waiting on a regular file, which is always ready
    
    812
    + - proceed asynchronously: block thread, return to scheduler
    
    813
    +
    
    814
    +Later, at I/O completion, I/O error, or async exception time:
    
    815
    + - reported failure, e.g. stale fd
    
    816
    + - success
    
    817
    +
    
    818
    +The waitRead/Write# also have non-reported failures, such as I/O errors,
    
    819
    +but these are treated as the success case since these primops are defined to
    
    820
    +wait for I/O or an error. We only report failures, via exceptions, for cases
    
    821
    +where it would be unsafe to proceed with a subsequent I/O operation on the
    
    822
    +same fd. In particular this includes when the fd was closed while a thread was
    
    823
    +waiting on the fd (since it's very unsafe to read/write an fd that is closed or
    
    824
    +worse: has been recycled to refer to a different file).
    
    825
    +
    
    826
    +The synchronous success and failure cases are handled by the initiating
    
    827
    +primops. In the blocking case, it relies on one of these stg_block_ functions,
    
    828
    +stack frames, and stack frame info table entry code. The key bits of the
    
    829
    +mechanism are:
    
    830
    +
    
    831
    + - The stg_block_ cmm function sets up the stack frame for the blocked thread.
    
    832
    + - The stack frame entries can be overwritten by the I/O manager, e.g. upon I/O
    
    833
    +   completion or error.
    
    834
    + - The stack frame's info table entry code is invoked when the thread is
    
    835
    +   resumed.
    
    836
    + - The entries in the stack frame become arguments to the info table entry code.
    
    837
    + - The result(s) returned by the info table entry code _are_ the result(s) of
    
    838
    +   the I/O primop.
    
    839
    +
    
    840
    +It is important to keep clear the distinction between the stack frame entries
    
    841
    +and the final primop result(s): the stack frame's info table entry code
    
    842
    +transforms the former into the latter.
    
    843
    +
    
    844
    +The convention we follow is that the stack frame contains two fields:
    
    845
    + - an outcome, using IOOpOutcome values: in-flight, success, failed, cancelled.
    
    846
    + - a result or error code. For outcome success it is a result. For failed it is
    
    847
    +   the error code (using posix errno codes), while for cancelled and in-flight
    
    848
    +   outcomes there is no result.
    
    849
    +
    
    850
    +This outcome+result/failure encoding is exactly the same as the corresponding
    
    851
    +fields in the StgAsyncIOOp. One day we might just use the StgAsyncIOOp itself,
    
    852
    +once all I/O managers use them.
    
    853
    +
    
    854
    +The waitRead/waitWrite# primops do not use a result, but later I/O primops will
    
    855
    +do. In particular read/write on files return a length, and other I/O primitives
    
    856
    +also have a single result.
    
    857
    +
    
    858
    +So this gives us two block functions and corresponding info tables:
    
    859
    +
    
    860
    + 1. stg_block_io_unit: with a stack frame containing outcome+result/failure.
    
    861
    +    On resume, throw exception on failure and otherwise return no args.
    
    862
    +    This is for primops with result IO ().
    
    863
    +
    
    864
    + 2. stg_block_io_int: with a stack frame containing outcome+result/failure.
    
    865
    +    On resume, throw exception on failure and otherwise return result.
    
    866
    +    This is for primops with result IO Int or IO Word.
    
    867
    +
    
    868
    + 3. stg_block_io_aiop: in future if we add async I/O primitives that return an
    
    869
    +    StgAsyncIOOp we would also need versions that return that (since even
    
    870
    +    submitting async I/O can block if there's too much I/O in flight).
    
    871
    +
    
    872
    +The stack frame layout used is:
    
    873
    + Sp[1]   stg_block_io_unit_info or stg_block_io_int_info
    
    874
    + Sp[1]   outcome
    
    875
    + Sp[2]   result or error
    
    876
    +
    
    877
    +C code should use setTsoIOOpOutcome to set this. But update this if the layout
    
    878
    +changes!
    
    879
    +
    
    880
    +
    
    881
    +Note [Calling convention for raisePrimIOException]
    
    882
    +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    883
    +
    
    884
    +In cases 1 & 2 we check the outcome and, on failure, we throw an exception. Our
    
    885
    +approach is to use a helper function implemeted in generated Haskell code, in
    
    886
    +ghc-internal. We need to perform a tail call to this helper function:
    
    887
    +
    
    888
    +    raisePrimIOException :: Int# -> State# s -> (# State# s, a #)
    
    889
    +
    
    890
    +The calling convention for top-level generated Haskell function like this uses
    
    891
    +R2 because it has two arguments -- the Int# and State# -- but the State#
    
    892
    +argument has a zero-width repesentation. The cmm call uses R1, R2, ... etc so
    
    893
    +to pass in R2 we have to use a dummy first argument which will be passed in R1
    
    894
    +(and ignored).
    
    895
    +*/
    
    896
    +
    
    897
    +/* stg_block_io_unit_info : the return info table for stg_block_io_unit */
    
    898
    +INFO_TABLE_RET ( stg_block_io_unit, RET_SMALL, W_ info_ptr,
    
    899
    +                 W_ outcome, W_ result )
    
    900
    +    return ()
    
    901
    +{
    
    902
    +    if (outcome == 2 :: W_ /*IOOpOutcomeFailed*/) (likely: False) {
    
    903
    +        /* See Note [Calling convention for raisePrimIOException] */
    
    904
    +        W_ dummyR1;
    
    905
    +        dummyR1 = 0;
    
    906
    +        jump %ENTRY_CODE(HsIface_raisePrimIOException_info(W_[ghc_hs_iface]))
    
    907
    +             (dummyR1, result);
    
    908
    +    } else {
    
    909
    +        /* success or cancelled outcomes */
    
    910
    +        return ();
    
    911
    +    }
    
    912
    +}
    
    913
    +
    
    914
    +/* Blocking for I/O primops with result IO ().
    
    915
    + * See Note [Thread blocking for new I/O primops].
    
    916
    + */
    
    917
    +stg_block_io_unit
    
    918
    +{
    
    919
    +    /* Fill out the stack frame with dummy values. The IO manager will
    
    920
    +     * overwrite these with the actual results when the I/O operation
    
    921
    +     * completes, fails, or is cancelled.
    
    922
    +     */
    
    923
    +    Sp_adj(-3);
    
    924
    +    Sp(0) = stg_block_io_unit_info;
    
    925
    +    Sp(1) = 0;                    // outcome: 0 indicates IOOpOutcomeInFlight
    
    926
    +    Sp(2) = 0;                    // result/errno
    
    927
    +    BLOCK_GENERIC;
    
    928
    +}
    
    929
    +
    
    930
    +/* stg_block_io_int_info : the return info table for stg_block_io_int */
    
    931
    +INFO_TABLE_RET ( stg_block_io_int, RET_SMALL, W_ info_ptr,
    
    932
    +                 W_ outcome, W_ result )
    
    933
    +    return ()
    
    934
    +{
    
    935
    +    if (outcome == 2 :: W_ /*IOOpOutcomeFailed*/) (likely: False) {
    
    936
    +        /* See Note [Calling convention for raisePrimIOException] */
    
    937
    +        W_ dummyR1;
    
    938
    +        dummyR1 = 0;
    
    939
    +        jump %ENTRY_CODE(HsIface_raisePrimIOException_info(W_[ghc_hs_iface]))
    
    940
    +             (dummyR1, result);
    
    941
    +    } else {
    
    942
    +        /* success or cancelled outcomes */
    
    943
    +        return (result);
    
    944
    +    }
    
    945
    +}
    
    946
    +
    
    947
    +/* Blocking for I/O primops with result IO Int or Word (or rather Int#/Word#).
    
    948
    + * See Note [Thread blocking for new I/O primops].
    
    949
    + */
    
    950
    +stg_block_io_int
    
    951
    +{
    
    952
    +    /* Fill out the stack frame with dummy values. The IO manager will
    
    953
    +     * overwrite these with the actual results when the I/O operation
    
    954
    +     * completes, fails, or is cancelled.
    
    955
    +     */
    
    956
    +    Sp_adj(-3);
    
    957
    +    Sp(0) = stg_block_io_int_info;
    
    958
    +    Sp(1) = 0;                    // outcome: 0 indicates IOOpOutcomeInFlight
    
    959
    +    Sp(2) = 0;                    // result/errno
    
    960
    +    BLOCK_GENERIC;
    
    961
    +}

  • rts/IOManager.c
    ... ... @@ -399,21 +399,6 @@ void startIOManager(void)
    399 399
     
    
    400 400
         switch (iomgr_type) {
    
    401 401
     
    
    402
    -#if defined(IOMGR_ENABLED_SELECT) || defined(IOMGR_ENABLED_POLL)
    
    403
    -#if defined(IOMGR_ENABLED_SELECT)
    
    404
    -        case IO_MANAGER_SELECT:
    
    405
    -#endif
    
    406
    -#if defined(IOMGR_ENABLED_POLL)
    
    407
    -        case IO_MANAGER_POLL:
    
    408
    -#endif
    
    409
    -            /* Make the exception CAF a GC root. See initBuiltinGcRoots for
    
    410
    -             * similar examples. We throw this exception if a thread tries to
    
    411
    -             * wait on an invalid FD.
    
    412
    -             */
    
    413
    -            getStablePtr((StgPtr)blockedOnBadFD_closure);
    
    414
    -            break;
    
    415
    -#endif
    
    416
    -
    
    417 402
     #if defined(IOMGR_ENABLED_MIO_POSIX)
    
    418 403
             case IO_MANAGER_MIO_POSIX:
    
    419 404
                 /* Posix implementation in posix/Signals.c
    
    ... ... @@ -776,10 +761,10 @@ void interruptIOManager(CapIOManager *iomgr)
    776 761
     
    
    777 762
     
    
    778 763
     /* CMM primop. Result is true on success, or false on allocation failure. */
    
    779
    -bool syncIOWaitReady(CapIOManager *iomgr,
    
    780
    -                     StgTSO       *tso,
    
    781
    -                     IOReadOrWrite rw,
    
    782
    -                     HsInt         fd)
    
    764
    +IOSubmitResult syncIOWaitReady(CapIOManager *iomgr,
    
    765
    +                               StgTSO       *tso,
    
    766
    +                               IOReadOrWrite rw,
    
    767
    +                               HsInt         fd)
    
    783 768
     {
    
    784 769
         debugTrace(DEBUG_iomanager,
    
    785 770
                    "thread %ld waiting for %s I/O readiness on fd %d",
    
    ... ... @@ -795,7 +780,7 @@ bool syncIOWaitReady(CapIOManager *iomgr,
    795 780
                 tso->block_info.fd = fd;
    
    796 781
                 appendToIOBlockedQueue(iomgr, tso);
    
    797 782
                 RELEASE_STORE(&tso->why_blocked, why_blocked);
    
    798
    -            return true;
    
    783
    +            return IOSubmitResultAsyncContinue;
    
    799 784
             }
    
    800 785
     #endif
    
    801 786
     #if defined(IOMGR_ENABLED_POLL)
    

  • rts/IOManager.h
    ... ... @@ -325,8 +325,33 @@ typedef enum { IORead = 0, IOWrite = 1 } IOReadOrWrite;
    325 325
      * false on heap allocation failure.
    
    326 326
      */
    
    327 327
     
    
    328
    +/* Note [Encoding of result of I/O manager operations]
    
    329
    +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    330
    +We have quite a bit of information that to return from the I/O manager calls
    
    331
    +back to the I/O primops. It (somewhat uncomfortably) fits into a C int.
    
    332
    +
    
    333
    +We use the following encoding of the result:
    
    334
    +
    
    335
    + * negative: synchronous failure, with -errno as the result.
    
    336
    + * 0: ok, result successful, proceed to async: suspend the TSO.
    
    337
    + * 1: synchronous success, return without suspending the TSO. This can occur
    
    338
    +      for example when waiting on readiness of a regular file (which is always
    
    339
    +      ready).
    
    340
    + * 2: heap overflow
    
    341
    + * >2: unallocated status codes
    
    342
    +*/
    
    343
    +typedef int IOSubmitResult;
    
    344
    +
    
    345
    +/* Provide constants for IOSubmitResult */
    
    346
    +enum IOSubmitResultCodes {
    
    347
    +  /* negative numbers are -errno error codes */
    
    348
    +  IOSubmitResultAsyncContinue = 0,
    
    349
    +  IOSubmitResultSyncSuccess   = 1,
    
    350
    +  IOSubmitResultHeapOverflow  = 2
    
    351
    +};
    
    352
    +
    
    328 353
     /* Called from CMM primop */
    
    329
    -bool syncIOWaitReady(CapIOManager *iomgr, StgTSO *tso, IOReadOrWrite rw, HsInt fd);
    
    354
    +IOSubmitResult syncIOWaitReady(CapIOManager *iomgr, StgTSO *tso, IOReadOrWrite rw, HsInt fd);
    
    330 355
     
    
    331 356
     void syncIOCancel(CapIOManager *iomgr, StgTSO *tso);
    
    332 357
     
    

  • rts/IOManagerInternals.h
    ... ... @@ -81,5 +81,25 @@ struct _CapIOManager {
    81 81
     
    
    82 82
     };
    
    83 83
     
    
    84
    +/* Fill in the outcome and result/error on the TSO's stack frame.
    
    85
    + * The TSO must be blocked using one of the stg_block_io_* blocking actions.
    
    86
    + *
    
    87
    + * Once the thread is resumed (by the scheduler) the code for stg_block_*_info
    
    88
    + * will pick up the info from the stack frame and use it to see if there's been
    
    89
    + * a failure, and if so to raise a PrimIOException.
    
    90
    + *
    
    91
    + * See Note [Thread blocking for new I/O primops].
    
    92
    + */
    
    93
    +INLINE_HEADER void setTsoIOOpOutcome (StgTSO *tso,
    
    94
    +                                      enum IOOpOutcome outcome,
    
    95
    +                                      uint32_t result)
    
    96
    +{
    
    97
    +    ASSERT((StgPtr *)tso->stackobj->sp[0] == (StgPtr *)&stg_block_io_unit_info
    
    98
    +        || (StgPtr *)tso->stackobj->sp[0] == (StgPtr *)&stg_block_io_int_info);
    
    99
    +
    
    100
    +    tso->stackobj->sp[1] = (W_)outcome;
    
    101
    +    tso->stackobj->sp[2] = (W_)result;
    
    102
    +}
    
    103
    +
    
    84 104
     #include "EndPrivate.h"
    
    85 105
     

  • rts/Prelude.h
    ... ... @@ -65,8 +65,6 @@ extern StgClosure ZCMain_main_closure;
    65 65
     #define overflowException_closure ghc_hs_iface->overflowException_closure
    
    66 66
     #define divZeroException_closure  ghc_hs_iface->divZZeroException_closure
    
    67 67
     
    
    68
    -#define blockedOnBadFD_closure    ghc_hs_iface->blockedOnBadFD_closure
    
    69
    -
    
    70 68
     #define Czh_con_info              ghc_hs_iface->Czh_con_info
    
    71 69
     #define Izh_con_info              ghc_hs_iface->Izh_con_info
    
    72 70
     #define Fzh_con_info              ghc_hs_iface->Fzh_con_info
    

  • rts/PrimOps.cmm
    ... ... @@ -2297,32 +2297,53 @@ stg_whereFromzh (P_ clos, W_ buf)
    2297 2297
        Thread I/O blocking primitives
    
    2298 2298
        -------------------------------------------------------------------------- */
    
    2299 2299
     
    
    2300
    -stg_waitReadzh ( W_ fd )
    
    2300
    +stg_waitReadyFd ( W_ fd, CInt rw )
    
    2301 2301
     {
    
    2302
    -    CBool ok; /* Ok, or heap alloc failure. */
    
    2302
    +    CInt result;
    
    2303 2303
     
    
    2304
    -    (ok) = ccall syncIOWaitReady(Capability_iomgr(MyCapability()) "ptr",
    
    2305
    -                                 CurrentTSO "ptr",
    
    2306
    -                                 /* IORead */ 0::CInt, fd);
    
    2307
    -    if (ok != 0::CBool) (likely: True) {
    
    2308
    -        jump stg_block_noregs();
    
    2309
    -    } else {
    
    2304
    +    (result) = ccall syncIOWaitReady(Capability_iomgr(MyCapability()) "ptr",
    
    2305
    +                                     CurrentTSO "ptr", rw, fd);
    
    2306
    +
    
    2307
    +    /* See Note [Encoding of result of I/O manager operations] */
    
    2308
    +
    
    2309
    +    /* case IOSubmitResultAsyncContinue */
    
    2310
    +    if (result == 0::CInt) (likely: True) {
    
    2311
    +        /* See Note [Thread blocking for new I/O primops] */
    
    2312
    +        jump stg_block_io_unit();
    
    2313
    +    }
    
    2314
    +
    
    2315
    +    /* case IOSubmitResultSyncSuccess*/
    
    2316
    +    if (result == 1::CInt) {
    
    2317
    +        /* Success, don't even go via scheduler. */
    
    2318
    +        return ();
    
    2319
    +    }
    
    2320
    +
    
    2321
    +    /* negative numbers are -errno error codes */
    
    2322
    +    if (result < 0::CInt) {
    
    2323
    +        /* See Note [Calling convention for raisePrimIOException] */
    
    2324
    +        W_ dummyR1; W_ errno;
    
    2325
    +        dummyR1 = 0;
    
    2326
    +        errno   = TO_W_(-result);
    
    2327
    +        jump %ENTRY_CODE(HsIface_raisePrimIOException_info(W_[ghc_hs_iface]))
    
    2328
    +                                                          (dummyR1, errno);
    
    2329
    +    }
    
    2330
    +
    
    2331
    +    /* case IOSubmitResultHeapOverflow */
    
    2332
    +    if (result == 2::CInt) {
    
    2310 2333
             jump stg_raisezh(HsIface_heapOverflow_closure(W_[ghc_hs_iface]));
    
    2334
    +
    
    2311 2335
         }
    
    2336
    +    ccall sbarf("syncIOWaitReady result encoding error") never returns;
    
    2312 2337
     }
    
    2313 2338
     
    
    2314
    -stg_waitWritezh ( W_ fd )
    
    2339
    +stg_waitReadzh ( W_ fd )
    
    2315 2340
     {
    
    2316
    -    CBool ok; /* Ok, or heap alloc failure. */
    
    2341
    +    jump stg_waitReadyFd(fd, /* IORead */ 0::CInt);
    
    2342
    +}
    
    2317 2343
     
    
    2318
    -    (ok) = ccall syncIOWaitReady(Capability_iomgr(MyCapability()) "ptr",
    
    2319
    -                                 CurrentTSO "ptr",
    
    2320
    -                                 /* IOWrite */ 1::CInt, fd);
    
    2321
    -    if (ok != 0::CBool) (likely: True) {
    
    2322
    -        jump stg_block_noregs();
    
    2323
    -    } else {
    
    2324
    -        jump stg_raisezh(HsIface_heapOverflow_closure(W_[ghc_hs_iface]));
    
    2325
    -    }
    
    2344
    +stg_waitWritezh ( W_ fd )
    
    2345
    +{
    
    2346
    +    jump stg_waitReadyFd(fd, /* IOWrite */ 1::CInt);
    
    2326 2347
     }
    
    2327 2348
     
    
    2328 2349
     stg_delayzh ( W_ us_delay )
    

  • rts/include/rts/RtsToHsIface.h
    ... ... @@ -28,7 +28,6 @@ typedef struct {
    28 28
         StgClosure *nonTermination_closure;  // GHC.Internal.Control.Exception.Base.nonTermination_closure
    
    29 29
         StgClosure *nestedAtomically_closure;  // GHC.Internal.Control.Exception.Base.nestedAtomically_closure
    
    30 30
         StgClosure *noMatchingContinuationPrompt_closure;  // GHC.Internal.Control.Exception.Base.noMatchingContinuationPrompt_closure
    
    31
    -    StgClosure *blockedOnBadFD_closure;  // GHC.Internal.Event.Thread.blockedOnBadFD_closure
    
    32 31
         StgClosure *raisePrimIOException_info; // GHC.Internal.Conc.IO.raisePrimIOException_info
    
    33 32
         StgClosure *runSparks_closure;  // GHC.Internal.Conc.Sync.runSparks_closure
    
    34 33
         StgClosure *ensureIOManagerIsRunning_closure;  // GHC.Internal.Conc.IO.ensureIOManagerIsRunning_closure
    

  • rts/include/stg/MiscClosures.h
    ... ... @@ -386,6 +386,10 @@ RTS_FUN_DECL(stg_block_throwto);
    386 386
     RTS_RET(stg_block_throwto);
    
    387 387
     
    
    388 388
     /* Blocking for I/O primops */
    
    389
    +RTS_FUN_DECL(stg_block_io_unit);
    
    390
    +RTS_RET(stg_block_io_unit);
    
    391
    +RTS_FUN_DECL(stg_block_io_int);
    
    392
    +RTS_RET(stg_block_io_int);
    
    389 393
     #if defined(mingw32_HOST_OS)
    
    390 394
     RTS_FUN_DECL(stg_block_async);
    
    391 395
     RTS_RET(stg_block_async);
    

  • rts/posix/Poll.c
    ... ... @@ -20,7 +20,6 @@
    20 20
     #include "Prelude.h"
    
    21 21
     #include "RtsUtils.h"
    
    22 22
     #include "rts/Time.h"
    
    23
    -#include "RaiseAsync.h"
    
    24 23
     #include "Trace.h"
    
    25 24
     
    
    26 25
     #include "Poll.h"
    
    ... ... @@ -171,14 +170,13 @@ void freeCapabilityIOManagerPoll(CapIOManager *iomgr)
    171 170
     }
    
    172 171
     
    
    173 172
     
    
    174
    -/* Used to implement syncIOWaitReady.
    
    175
    - * Result is true on success, or false on allocation failure. */
    
    176
    -bool syncIOWaitReadyPoll(CapIOManager *iomgr, StgTSO *tso,
    
    177
    -                         IOReadOrWrite rw, HsInt fd)
    
    173
    +/* Used to implement syncIOWaitReady. */
    
    174
    +IOSubmitResult syncIOWaitReadyPoll(CapIOManager *iomgr, StgTSO *tso,
    
    175
    +                                   IOReadOrWrite rw, HsInt fd)
    
    178 176
     {
    
    179 177
         StgAsyncIOOp *aiop;
    
    180 178
         aiop = (StgAsyncIOOp *)allocateMightFail(iomgr->cap, sizeofW(StgAsyncIOOp));
    
    181
    -    if (RTS_UNLIKELY(aiop == NULL)) return false;
    
    179
    +    if (RTS_UNLIKELY(aiop == NULL)) return IOSubmitResultHeapOverflow;
    
    182 180
         SET_HDR(aiop, &stg_ASYNCIOOP_info, iomgr->cap->r.rCCCS);
    
    183 181
         aiop->notify.tso     = tso;
    
    184 182
         aiop->notify_type    = NotifyTSO;
    
    ... ... @@ -189,13 +187,12 @@ bool syncIOWaitReadyPoll(CapIOManager *iomgr, StgTSO *tso,
    189 187
         return asyncIOWaitReadyPoll(iomgr, aiop, rw, fd);
    
    190 188
     }
    
    191 189
     
    
    192
    -/* Result is true on success, or false on allocation failure. */
    
    193
    -bool asyncIOWaitReadyPoll(CapIOManager *iomgr, StgAsyncIOOp *aiop,
    
    194
    -                         IOReadOrWrite rw, int fd)
    
    190
    +IOSubmitResult asyncIOWaitReadyPoll(CapIOManager *iomgr, StgAsyncIOOp *aiop,
    
    191
    +                                    IOReadOrWrite rw, int fd)
    
    195 192
     {
    
    196 193
         if (RTS_UNLIKELY(isFullClosureTable(&iomgr->aiop_table))) {
    
    197 194
             bool ok = enlargeTables(iomgr);
    
    198
    -        if (RTS_UNLIKELY(!ok)) return false;
    
    195
    +        if (RTS_UNLIKELY(!ok)) return IOSubmitResultHeapOverflow;
    
    199 196
         }
    
    200 197
     
    
    201 198
         int ix = insertClosureTable(iomgr->cap, &iomgr->aiop_table, aiop);
    
    ... ... @@ -216,7 +213,7 @@ bool asyncIOWaitReadyPoll(CapIOManager *iomgr, StgAsyncIOOp *aiop,
    216 213
                                        .events  = rw == IORead ? POLLIN : POLLOUT,
    
    217 214
                                        .revents = 0
    
    218 215
                                      };
    
    219
    -    return true;
    
    216
    +    return IOSubmitResultAsyncContinue;
    
    220 217
     }
    
    221 218
     
    
    222 219
     
    
    ... ... @@ -226,6 +223,7 @@ void syncIOCancelPoll(CapIOManager *iomgr, StgTSO *tso)
    226 223
         ASSERT(aiop->notify_type == NotifyTSO);
    
    227 224
         ASSERT(indexClosureTable(&iomgr->aiop_table, aiop->index) == aiop);
    
    228 225
         ioCancel(iomgr, aiop);
    
    226
    +    setTsoIOOpOutcome(tso, aiop->outcome, aiop->result);
    
    229 227
         /* We cannot use the normal notifyIOCompletion here. We are in the context
    
    230 228
          * of throwTo, interrupting a thread blocked on IO via an async exception.
    
    231 229
          * We don't put the TSO back on the run queue or change the why_blocked
    
    ... ... @@ -284,26 +282,18 @@ static void notifyIOCompletion(CapIOManager *iomgr, StgAsyncIOOp *aiop)
    284 282
         switch (aiop->notify_type) {
    
    285 283
             case NotifyTSO:
    
    286 284
             {
    
    287
    -            if (aiop->outcome == IOOpOutcomeFailed && aiop->error == EBADF) {
    
    288
    -                /* The fd is invalid: raise an IOError exception in the blocked
    
    289
    -                 * thread. (See bug #4934 for what happens without this.)
    
    290
    -                 */
    
    291
    -                StgTSO *tso = aiop->notify.tso;
    
    292
    -                debugTrace(DEBUG_iomanager,
    
    293
    -                           "Raising exception in thread %" FMT_StgThreadID
    
    294
    -                           " blocked on an invalid fd", tso->id);
    
    295
    -                raiseAsync(iomgr->cap, tso,
    
    296
    -                           (StgClosure *)blockedOnBadFD_closure,
    
    297
    -                           false, NULL);
    
    298
    -            } else {
    
    299
    -                /* We should be guaranteed that the tso is still on the same
    
    300
    -                 * cap because the tso was not on the run queue of any cap and
    
    301
    -                 * so is not subject to thread migration.
    
    302
    -                 */
    
    303
    -                StgTSO *tso = aiop->notify.tso;
    
    304
    -                pushOnRunQueue(iomgr->cap, tso);
    
    305
    -                RELEASE_STORE(&tso->why_blocked, NotBlocked);
    
    306
    -            }
    
    285
    +            /* We should be guaranteed that the tso is still on the same
    
    286
    +             * cap because the tso was not on the run queue of any cap and
    
    287
    +             * so is not subject to thread migration.
    
    288
    +             */
    
    289
    +            StgTSO *tso = aiop->notify.tso;
    
    290
    +            ASSERT(tso->cap == iomgr->cap);
    
    291
    +
    
    292
    +            /* Fill in the outcome and result/error on the TSO's stack frame */
    
    293
    +            setTsoIOOpOutcome(tso, aiop->outcome, aiop->result);
    
    294
    +            pushOnRunQueue(iomgr->cap, tso);
    
    295
    +            RELEASE_STORE(&tso->why_blocked, NotBlocked);
    
    296
    +
    
    307 297
                 /* For the TSO case, the aiop was only reachable from the TSO
    
    308 298
                  * itself, and thus it is now no longer be reachable at all.
    
    309 299
                  */
    

  • rts/posix/Poll.h
    ... ... @@ -20,13 +20,13 @@ void initCapabilityIOManagerPoll(CapIOManager *iomgr);
    20 20
     void freeCapabilityIOManagerPoll(CapIOManager *iomgr);
    
    21 21
     
    
    22 22
     /* Synchronous I/O and timer operations */
    
    23
    -bool syncIOWaitReadyPoll(CapIOManager *iomgr, StgTSO *tso,
    
    24
    -                         IOReadOrWrite rw, HsInt fd);
    
    23
    +IOSubmitResult syncIOWaitReadyPoll(CapIOManager *iomgr, StgTSO *tso,
    
    24
    +                                   IOReadOrWrite rw, HsInt fd);
    
    25 25
     void syncIOCancelPoll(CapIOManager *iomgr, StgTSO *tso);
    
    26 26
     
    
    27 27
     /* Asynchronous operations */
    
    28
    -bool asyncIOWaitReadyPoll(CapIOManager *iomgr, StgAsyncIOOp *aiop,
    
    29
    -                          IOReadOrWrite rw, int fd);
    
    28
    +IOSubmitResult asyncIOWaitReadyPoll(CapIOManager *iomgr, StgAsyncIOOp *aiop,
    
    29
    +                                    IOReadOrWrite rw, int fd);
    
    30 30
     void asyncIOCancelPoll(CapIOManager *iomgr, StgAsyncIOOp *aiop);
    
    31 31
     
    
    32 32
     /* Scheduler operations */
    

  • rts/posix/Select.c
    ... ... @@ -15,7 +15,6 @@
    15 15
     #include "Signals.h"
    
    16 16
     #include "Schedule.h"
    
    17 17
     #include "Prelude.h"
    
    18
    -#include "RaiseAsync.h"
    
    19 18
     #include "RtsUtils.h"
    
    20 19
     #include "Capability.h"
    
    21 20
     #include "Select.h"
    
    ... ... @@ -480,13 +479,18 @@ awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait)
    480 479
                       IF_DEBUG(scheduler,
    
    481 480
                           debugBelch("Killing blocked thread %" FMT_StgThreadID
    
    482 481
                                      " on bad fd=%i\n", tso->id, fd));
    
    483
    -                  raiseAsync(iomgr->cap, tso,
    
    484
    -                      (StgClosure *)blockedOnBadFD_closure, false, NULL);
    
    482
    +
    
    483
    +                  /* Fill in the outcome and error on the TSO's stack frame */
    
    484
    +                  setTsoIOOpOutcome(tso, IOOpOutcomeFailed, EBADF);
    
    485
    +                  pushOnRunQueue(iomgr->cap,tso);
    
    486
    +                  RELEASE_STORE(&tso->why_blocked, NotBlocked);
    
    485 487
                       break;
    
    486 488
                   case RTS_FD_IS_READY:
    
    487 489
                       IF_DEBUG(scheduler,
    
    488 490
                           debugBelch("Waking up blocked thread %" FMT_StgThreadID "\n",
    
    489 491
                                      tso->id));
    
    492
    +                 /* Fill in the outcome and result on the TSO's stack frame */
    
    493
    +                  setTsoIOOpOutcome(tso, IOOpOutcomeSuccess, 0);
    
    490 494
                       pushOnRunQueue(iomgr->cap,tso);
    
    491 495
                       RELEASE_STORE(&tso->why_blocked, NotBlocked);
    
    492 496
                       break;