Magnus pushed to branch wip/mangoiv/ghc-9.12-bp at Glasgow Haskell Compiler / GHC

Commits:

20 changed files:

Changes:

  • rts/HeapStackCheck.cmm
    ... ... @@ -706,38 +706,24 @@ stg_block_throwto (P_ tso, P_ exception)
    706 706
     }
    
    707 707
     
    
    708 708
     #if defined(mingw32_HOST_OS)
    
    709
    -INFO_TABLE_RET ( stg_block_async, RET_SMALL, W_ info_ptr, W_ ares )
    
    709
    +INFO_TABLE_RET ( stg_block_async, RET_SMALL, W_ info_ptr, W_ len, W_ errCode )
    
    710 710
         return ()
    
    711 711
     {
    
    712
    -    W_ len, errC;
    
    713
    -
    
    714
    -    len = TO_W_(StgAsyncIOResult_len(ares));
    
    715
    -    errC = TO_W_(StgAsyncIOResult_errCode(ares));
    
    716
    -    ccall free(ares "ptr");
    
    717
    -    return (len, errC);
    
    712
    +    return (len, errCode);
    
    718 713
     }
    
    719 714
     
    
    720 715
     stg_block_async
    
    721 716
     {
    
    722
    -    Sp_adj(-2);
    
    723
    -    Sp(0) = stg_block_async_info;
    
    724
    -    BLOCK_GENERIC;
    
    725
    -}
    
    717
    +    W_ eintr;
    
    718
    +    (eintr) = ccall rts_EINTR();
    
    726 719
     
    
    727
    -/* Used by threadDelay implementation; it would be desirable to get rid of
    
    728
    - * this free()'ing void return continuation.
    
    729
    - */
    
    730
    -INFO_TABLE_RET ( stg_block_async_void, RET_SMALL, W_ info_ptr, W_ ares )
    
    731
    -    return ()
    
    732
    -{
    
    733
    -    ccall free(ares "ptr");
    
    734
    -    return ();
    
    735
    -}
    
    736
    -
    
    737
    -stg_block_async_void
    
    738
    -{
    
    739
    -    Sp_adj(-2);
    
    740
    -    Sp(0) = stg_block_async_void_info;
    
    720
    +    // Fill the stack frame with values that indicate that the operation
    
    721
    +    // has been interrupted. The IO manager will overwrite these with the
    
    722
    +    // actual results if the async operation completes.
    
    723
    +    Sp_adj(-3);
    
    724
    +    Sp(0) = stg_block_async_info;
    
    725
    +    Sp(1) = -1;                    // len:     -1 indicates error
    
    726
    +    Sp(2) = eintr;                 // errCode: interrupted
    
    741 727
         BLOCK_GENERIC;
    
    742 728
     }
    
    743 729
     
    

  • rts/IOManager.c
    ... ... @@ -561,10 +561,8 @@ void scavengeTSOIOManager(StgTSO *tso)
    561 561
                  */
    
    562 562
     
    
    563 563
                 /* case IO_MANAGER_WIN32_LEGACY:
    
    564
    -             * BlockedOn{Read,Write,DoProc} uses block_info.async_result
    
    565
    -             * The StgAsyncIOResult async_result is allocated on the C heap.
    
    566
    -             * It'd probably be better if it used the GC heap. If it did we'd
    
    567
    -             * scavenge it here.
    
    564
    +             * BlockedOn{Read,Write,DoProc} uses block_info.async_reqID
    
    565
    +             * which is a plain integer, so nothing to scavenge.
    
    568 566
                  */
    
    569 567
     
    
    570 568
             default:
    
    ... ... @@ -707,7 +705,7 @@ void awaitCompletedTimeoutsOrIO(Capability *cap)
    707 705
     }
    
    708 706
     
    
    709 707
     
    
    710
    -void syncIOWaitReady(Capability   *cap,
    
    708
    +bool syncIOWaitReady(Capability   *cap,
    
    711 709
                          StgTSO       *tso,
    
    712 710
                          IOReadOrWrite rw,
    
    713 711
                          HsInt         fd)
    
    ... ... @@ -724,7 +722,7 @@ void syncIOWaitReady(Capability *cap,
    724 722
                 tso->block_info.fd = fd;
    
    725 723
                 RELEASE_STORE(&tso->why_blocked, why_blocked);
    
    726 724
                 appendToIOBlockedQueue(cap, tso);
    
    727
    -            break;
    
    725
    +            return true;
    
    728 726
             }
    
    729 727
     #endif
    
    730 728
             default:
    
    ... ... @@ -747,7 +745,7 @@ void syncIOCancel(Capability *cap, StgTSO *tso)
    747 745
             case IO_MANAGER_WIN32_LEGACY:
    
    748 746
                 removeThreadFromDeQueue(cap, &cap->iomgr->blocked_queue_hd,
    
    749 747
                                              &cap->iomgr->blocked_queue_tl, tso);
    
    750
    -            abandonWorkRequest(tso->block_info.async_result->reqID);
    
    748
    +            abandonWorkRequest(tso->block_info.async_reqID);
    
    751 749
                 break;
    
    752 750
     #endif
    
    753 751
             default:
    
    ... ... @@ -761,7 +759,7 @@ static void insertIntoSleepingQueue(Capability *cap, StgTSO *tso, LowResTime tar
    761 759
     #endif
    
    762 760
     
    
    763 761
     
    
    764
    -void syncDelay(Capability *cap, StgTSO *tso, HsInt us_delay)
    
    762
    +bool syncDelay(Capability *cap, StgTSO *tso, HsInt us_delay)
    
    765 763
     {
    
    766 764
         debugTrace(DEBUG_iomanager, "thread %ld waiting for %lld us", tso->id, us_delay);
    
    767 765
         ASSERT(tso->why_blocked == NotBlocked);
    
    ... ... @@ -773,7 +771,7 @@ void syncDelay(Capability *cap, StgTSO *tso, HsInt us_delay)
    773 771
                 tso->block_info.target = target;
    
    774 772
                 RELEASE_STORE(&tso->why_blocked, BlockedOnDelay);
    
    775 773
                 insertIntoSleepingQueue(cap, tso, target);
    
    776
    -            break;
    
    774
    +            return true;
    
    777 775
             }
    
    778 776
     #endif
    
    779 777
     #if defined(IOMGR_ENABLED_WIN32_LEGACY)
    
    ... ... @@ -782,12 +780,7 @@ void syncDelay(Capability *cap, StgTSO *tso, HsInt us_delay)
    782 780
                  * would make the primops more consistent.
    
    783 781
                  */
    
    784 782
             {
    
    785
    -            StgAsyncIOResult *ares = stgMallocBytes(sizeof(StgAsyncIOResult),
    
    786
    -                                                    "syncDelay");
    
    787
    -            ares->reqID   = addDelayRequest(us_delay);
    
    788
    -            ares->len     = 0;
    
    789
    -            ares->errCode = 0;
    
    790
    -            tso->block_info.async_result = ares;
    
    783
    +            tso->block_info.async_reqID = addDelayRequest(us_delay);
    
    791 784
     
    
    792 785
                 /* Having all async-blocked threads reside on the blocked_queue
    
    793 786
                  * simplifies matters, so set the status to OnDoProc and put the
    
    ... ... @@ -795,7 +788,7 @@ void syncDelay(Capability *cap, StgTSO *tso, HsInt us_delay)
    795 788
                  */
    
    796 789
                 RELEASE_STORE(&tso->why_blocked, BlockedOnDoProc);
    
    797 790
                 appendToIOBlockedQueue(cap, tso);
    
    798
    -            break;
    
    791
    +            return true;
    
    799 792
             }
    
    800 793
     #endif
    
    801 794
             default:
    

  • rts/IOManager.h
    ... ... @@ -282,13 +282,20 @@ typedef enum { IORead, IOWrite } IOReadOrWrite;
    282 282
     /* Synchronous operations: I/O and delays. As synchronous operations they
    
    283 283
      * necessarily operate on threads. The thread is suspended until the operation
    
    284 284
      * completes.
    
    285
    + *
    
    286
    + * These are called from CMM primops. The ones returing int can perform heap
    
    287
    + * allocation, which might fail. They return 0 on success, or n > 0 on heap
    
    288
    + * allocation failure, needing n words. The CMM primops should invoke the
    
    289
    + * GC to free up at least n words and then retry the operation.
    
    285 290
      */
    
    286 291
     
    
    287
    -void syncIOWaitReady(Capability *cap, StgTSO *tso, IOReadOrWrite rw, HsInt fd);
    
    292
    +/* Result is true on success, or false on allocation failure. */
    
    293
    +bool syncIOWaitReady(Capability *cap, StgTSO *tso, IOReadOrWrite rw, HsInt fd);
    
    288 294
     
    
    289 295
     void syncIOCancel(Capability *cap, StgTSO *tso);
    
    290 296
     
    
    291
    -void syncDelay(Capability *cap, StgTSO *tso, HsInt us_delay);
    
    297
    +/* Result is true on success, or false on allocation failure. */
    
    298
    +bool syncDelay(Capability *cap, StgTSO *tso, HsInt us_delay);
    
    292 299
     
    
    293 300
     void syncDelayCancel(Capability *cap, StgTSO *tso);
    
    294 301
     
    

  • rts/Interpreter.c
    ... ... @@ -468,7 +468,7 @@ slow_spw(void *Sp, StgStack *cur_stack, StgWord offset_words){
    468 468
         }
    
    469 469
         // 2b. Access the element if there is no underflow frame, it must be right
    
    470 470
         // at the top of the stack.
    
    471
    -    else if(Sp_plusW(offset_words) < (StgPtr)(cur_stack->stack + cur_stack->stack_size)) {
    
    471
    +    else if((StgPtr)Sp_plusW(offset_words) < (StgPtr)(cur_stack->stack + cur_stack->stack_size)) {
    
    472 472
             // Still inside the stack chunk
    
    473 473
             return Sp_plusW(offset_words);
    
    474 474
         } else {
    
    ... ... @@ -1832,7 +1832,7 @@ run_BCO:
    1832 1832
                         threadStackUnderflow(cap, cap->r.rCurrentTSO);
    
    1833 1833
                         LOAD_STACK_POINTERS;
    
    1834 1834
                         by -= sp_to_uf;
    
    1835
    -                } else if (Sp_plusW(by) < (StgPtr)(stk->stack + stk->stack_size)) {
    
    1835
    +                } else if ((StgPtr)Sp_plusW(by) < (StgPtr)(stk->stack + stk->stack_size)) {
    
    1836 1836
                         // we're within the first stack chunk, this chunk has
    
    1837 1837
                         // no underflow frame
    
    1838 1838
                         break;
    

  • rts/PrimOps.cmm
    ... ... @@ -2561,54 +2561,55 @@ stg_whereFromzh (P_ clos, W_ buf)
    2561 2561
     
    
    2562 2562
     stg_waitReadzh ( W_ fd )
    
    2563 2563
     {
    
    2564
    -    ccall syncIOWaitReady(MyCapability() "ptr", CurrentTSO "ptr",
    
    2565
    -                          /* IORead */ 0::I32, fd);
    
    2566
    -    jump stg_block_noregs();
    
    2564
    +    CBool ok; /* Ok, or heap alloc failure. */
    
    2565
    +
    
    2566
    +    (ok) = ccall syncIOWaitReady(MyCapability() "ptr", CurrentTSO "ptr",
    
    2567
    +                                 /* IORead */ 0::I32, fd);
    
    2568
    +    if (ok != 0::CBool) (likely: True) {
    
    2569
    +        jump stg_block_noregs();
    
    2570
    +    } else {
    
    2571
    +        jump stg_raisezh(ghczminternal_GHCziInternalziIOziException_heapOverflow_closure);
    
    2572
    +    }
    
    2567 2573
     }
    
    2568 2574
     
    
    2569 2575
     stg_waitWritezh ( W_ fd )
    
    2570 2576
     {
    
    2571
    -    ccall syncIOWaitReady(MyCapability() "ptr", CurrentTSO "ptr",
    
    2572
    -                          /* IOWrite */ 1::I32, fd);
    
    2573
    -    jump stg_block_noregs();
    
    2577
    +    CBool ok; /* Ok, or heap alloc failure. */
    
    2578
    +
    
    2579
    +    (ok) = ccall syncIOWaitReady(MyCapability() "ptr", CurrentTSO "ptr",
    
    2580
    +                                 /* IOWrite */ 1::I32, fd);
    
    2581
    +    if (ok != 0::CBool) (likely: True) {
    
    2582
    +        jump stg_block_noregs();
    
    2583
    +    } else {
    
    2584
    +        jump stg_raisezh(ghczminternal_GHCziInternalziIOziException_heapOverflow_closure);
    
    2585
    +    }
    
    2574 2586
     }
    
    2575 2587
     
    
    2576 2588
     stg_delayzh ( W_ us_delay )
    
    2577 2589
     {
    
    2578
    -    ccall syncDelay(MyCapability() "ptr", CurrentTSO "ptr", us_delay);
    
    2590
    +    CBool ok; /* Ok, or heap alloc failure. */
    
    2579 2591
     
    
    2580
    -    /* Annoyingly, we cannot be consistent with how we wait and resume the
    
    2581
    -     * blocked thread. The reason is that the win32 legacy I/O manager
    
    2582
    -     * allocates a StgAsyncIOResult struct on the C heap which has to be
    
    2583
    -     * freed when the thread resumes. It's a bit awkward to arrange to
    
    2584
    -     * allocate it on the GC heap instead, so that's how it is for now. Sigh.
    
    2585
    -     */
    
    2586
    -#if defined(mingw32_HOST_OS)
    
    2587
    -    jump stg_block_async_void();
    
    2588
    -#else
    
    2589
    -    jump stg_block_noregs();
    
    2590
    -#endif
    
    2592
    +    (ok) = ccall syncDelay(MyCapability() "ptr", CurrentTSO "ptr", us_delay);
    
    2593
    +
    
    2594
    +    if (ok != 0::CBool) (likely: True) {
    
    2595
    +        jump stg_block_noregs();
    
    2596
    +    } else {
    
    2597
    +        jump stg_raisezh(ghczminternal_GHCziInternalziIOziException_heapOverflow_closure);
    
    2598
    +    }
    
    2591 2599
     }
    
    2592 2600
     
    
    2593 2601
     
    
    2594 2602
     #if defined(mingw32_HOST_OS)
    
    2595 2603
     stg_asyncReadzh ( W_ fd, W_ is_sock, W_ len, W_ buf )
    
    2596 2604
     {
    
    2597
    -    W_ ares;
    
    2598 2605
         CInt reqID;
    
    2599 2606
     
    
    2600 2607
     #if defined(THREADED_RTS)
    
    2601 2608
         ccall barf("asyncRead# on threaded RTS") never returns;
    
    2602 2609
     #else
    
    2603 2610
     
    
    2604
    -    /* could probably allocate this on the heap instead */
    
    2605
    -    ("ptr" ares) = ccall stgMallocBytes(SIZEOF_StgAsyncIOResult,
    
    2606
    -                                        "stg_asyncReadzh");
    
    2607 2611
         (reqID) = ccall addIORequest(fd, 0/*FALSE*/,is_sock,len,buf "ptr");
    
    2608
    -    StgAsyncIOResult_reqID(ares)   = reqID;
    
    2609
    -    StgAsyncIOResult_len(ares)     = 0;
    
    2610
    -    StgAsyncIOResult_errCode(ares) = 0;
    
    2611
    -    StgTSO_block_info(CurrentTSO)  = ares;
    
    2612
    +    StgTSO_block_info(CurrentTSO) = reqID;
    
    2612 2613
     
    
    2613 2614
         ASSERT(StgTSO_why_blocked(CurrentTSO) == NotBlocked::I32);
    
    2614 2615
         %release StgTSO_why_blocked(CurrentTSO) = BlockedOnRead::I32;
    
    ... ... @@ -2620,21 +2621,14 @@ stg_asyncReadzh ( W_ fd, W_ is_sock, W_ len, W_ buf )
    2620 2621
     
    
    2621 2622
     stg_asyncWritezh ( W_ fd, W_ is_sock, W_ len, W_ buf )
    
    2622 2623
     {
    
    2623
    -    W_ ares;
    
    2624 2624
         CInt reqID;
    
    2625 2625
     
    
    2626 2626
     #if defined(THREADED_RTS)
    
    2627 2627
         ccall barf("asyncWrite# on threaded RTS") never returns;
    
    2628 2628
     #else
    
    2629 2629
     
    
    2630
    -    ("ptr" ares) = ccall stgMallocBytes(SIZEOF_StgAsyncIOResult,
    
    2631
    -                                        "stg_asyncWritezh");
    
    2632 2630
         (reqID) = ccall addIORequest(fd, 1/*TRUE*/,is_sock,len,buf "ptr");
    
    2633
    -
    
    2634
    -    StgAsyncIOResult_reqID(ares)   = reqID;
    
    2635
    -    StgAsyncIOResult_len(ares)     = 0;
    
    2636
    -    StgAsyncIOResult_errCode(ares) = 0;
    
    2637
    -    StgTSO_block_info(CurrentTSO)  = ares;
    
    2631
    +    StgTSO_block_info(CurrentTSO) = reqID;
    
    2638 2632
     
    
    2639 2633
         ASSERT(StgTSO_why_blocked(CurrentTSO) == NotBlocked::I32);
    
    2640 2634
         %release StgTSO_why_blocked(CurrentTSO) = BlockedOnWrite::I32;
    
    ... ... @@ -2646,21 +2640,14 @@ stg_asyncWritezh ( W_ fd, W_ is_sock, W_ len, W_ buf )
    2646 2640
     
    
    2647 2641
     stg_asyncDoProczh ( W_ proc, W_ param )
    
    2648 2642
     {
    
    2649
    -    W_ ares;
    
    2650 2643
         CInt reqID;
    
    2651 2644
     
    
    2652 2645
     #if defined(THREADED_RTS)
    
    2653 2646
         ccall barf("asyncDoProc# on threaded RTS") never returns;
    
    2654 2647
     #else
    
    2655 2648
     
    
    2656
    -    /* could probably allocate this on the heap instead */
    
    2657
    -    ("ptr" ares) = ccall stgMallocBytes(SIZEOF_StgAsyncIOResult,
    
    2658
    -                                        "stg_asyncDoProczh");
    
    2659 2649
         (reqID) = ccall addDoProcRequest(proc "ptr",param "ptr");
    
    2660
    -    StgAsyncIOResult_reqID(ares)   = reqID;
    
    2661
    -    StgAsyncIOResult_len(ares)     = 0;
    
    2662
    -    StgAsyncIOResult_errCode(ares) = 0;
    
    2663
    -    StgTSO_block_info(CurrentTSO) = ares;
    
    2650
    +    StgTSO_block_info(CurrentTSO) = reqID;
    
    2664 2651
     
    
    2665 2652
         ASSERT(StgTSO_why_blocked(CurrentTSO) == NotBlocked::I32);
    
    2666 2653
         %release StgTSO_why_blocked(CurrentTSO) = BlockedOnDoProc::I32;
    

  • rts/RtsSymbols.c
    ... ... @@ -30,6 +30,7 @@
    30 30
     #include <shfolder.h> /* SHGetFolderPathW */
    
    31 31
     #include "IOManager.h"
    
    32 32
     #include "win32/AsyncWinIO.h"
    
    33
    +#include "win32/AsyncMIO.h"
    
    33 34
     #endif
    
    34 35
     
    
    35 36
     #if defined(openbsd_HOST_OS)
    
    ... ... @@ -168,6 +169,7 @@ extern char **environ;
    168 169
           SymI_HasProto(__stdio_common_vswprintf_s)          \
    
    169 170
           SymI_HasProto(__stdio_common_vswprintf)            \
    
    170 171
           SymI_HasProto(_errno)                              \
    
    172
    +      SymI_HasProto(rts_EINTR)                           \
    
    171 173
           /* see Note [Symbols for MinGW's printf] */        \
    
    172 174
           SymI_HasProto(_lock_file)                          \
    
    173 175
           SymI_HasProto(_unlock_file)                        \
    

  • rts/Threads.c
    ... ... @@ -928,7 +928,7 @@ printThreadBlockage(StgTSO *tso)
    928 928
       switch (ACQUIRE_LOAD(&tso->why_blocked)) {
    
    929 929
     #if defined(mingw32_HOST_OS)
    
    930 930
         case BlockedOnDoProc:
    
    931
    -    debugBelch("is blocked on proc (request: %u)", tso->block_info.async_result->reqID);
    
    931
    +    debugBelch("is blocked on proc (request: %" FMT_Word ")", tso->block_info.async_reqID);
    
    932 932
         break;
    
    933 933
     #endif
    
    934 934
     #if !defined(THREADED_RTS)
    

  • rts/include/rts/storage/ClosureMacros.h
    ... ... @@ -322,8 +322,10 @@ EXTERN_INLINE bool LOOKS_LIKE_PTR (const void* p)
    322 322
     EXTERN_INLINE bool LOOKS_LIKE_INFO_PTR_NOT_NULL (StgWord p);
    
    323 323
     EXTERN_INLINE bool LOOKS_LIKE_INFO_PTR_NOT_NULL (StgWord p)
    
    324 324
     {
    
    325
    +NO_WARN(-Warray-bounds,
    
    325 326
         StgInfoTable *info = INFO_PTR_TO_STRUCT((StgInfoTable *)p);
    
    326 327
         return info->type != INVALID_OBJECT && info->type < N_CLOSURE_TYPES;
    
    328
    +)
    
    327 329
     }
    
    328 330
     
    
    329 331
     EXTERN_INLINE bool LOOKS_LIKE_INFO_PTR (StgWord p);
    

  • rts/include/rts/storage/TSO.h
    ... ... @@ -37,15 +37,6 @@ typedef StgWord64 StgThreadID;
    37 37
      */
    
    38 38
     typedef unsigned int StgThreadReturnCode;
    
    39 39
     
    
    40
    -#if defined(mingw32_HOST_OS)
    
    41
    -/* results from an async I/O request + its request ID. */
    
    42
    -typedef struct {
    
    43
    -  unsigned int reqID;
    
    44
    -  int          len;
    
    45
    -  int          errCode;
    
    46
    -} StgAsyncIOResult;
    
    47
    -#endif
    
    48
    -
    
    49 40
     /* Reason for thread being blocked. See comment above struct StgTso_. */
    
    50 41
     typedef union {
    
    51 42
       StgClosure *closure;
    
    ... ... @@ -55,7 +46,7 @@ typedef union {
    55 46
       struct MessageWakeup_  *wakeup;
    
    56 47
       StgInt fd;    /* StgInt instead of int, so that it's the same size as the ptrs */
    
    57 48
     #if defined(mingw32_HOST_OS)
    
    58
    -  StgAsyncIOResult *async_result;
    
    49
    +  StgWord async_reqID;
    
    59 50
     #endif
    
    60 51
     #if !defined(THREADED_RTS)
    
    61 52
       StgWord target;
    

  • rts/include/stg/MiscClosures.h
    ... ... @@ -430,8 +430,6 @@ RTS_RET(stg_block_putmvar);
    430 430
     #if defined(mingw32_HOST_OS)
    
    431 431
     RTS_FUN_DECL(stg_block_async);
    
    432 432
     RTS_RET(stg_block_async);
    
    433
    -RTS_FUN_DECL(stg_block_async_void);
    
    434
    -RTS_RET(stg_block_async_void);
    
    435 433
     #endif
    
    436 434
     RTS_FUN_DECL(stg_block_stmwait);
    
    437 435
     RTS_FUN_DECL(stg_block_throwto);
    

  • rts/win32/AsyncMIO.c
    ... ... @@ -8,16 +8,19 @@
    8 8
      *       For the WINIO manager see base in the GHC.Event modules.
    
    9 9
      */
    
    10 10
     
    
    11
    -#if !defined(THREADED_RTS)
    
    12 11
     
    
    13 12
     #include "Rts.h"
    
    13
    +#include <errno.h>
    
    14
    +#include "win32/AsyncMIO.h"
    
    15
    +
    
    16
    +#if !defined(THREADED_RTS)
    
    17
    +
    
    14 18
     #include "RtsUtils.h"
    
    15 19
     #include <windows.h>
    
    16 20
     #include <stdio.h>
    
    17 21
     #include "Schedule.h"
    
    18 22
     #include "Capability.h"
    
    19 23
     #include "IOManagerInternals.h"
    
    20
    -#include "win32/AsyncMIO.h"
    
    21 24
     #include "win32/MIOManager.h"
    
    22 25
     
    
    23 26
     /*
    
    ... ... @@ -299,14 +302,9 @@ start:
    299 302
                     case BlockedOnRead:
    
    300 303
                     case BlockedOnWrite:
    
    301 304
                     case BlockedOnDoProc:
    
    302
    -                    if (tso->block_info.async_result->reqID == rID) {
    
    303
    -                        // Found the thread blocked waiting on request;
    
    304
    -                        // stodgily fill
    
    305
    -                        // in its result block.
    
    306
    -                        tso->block_info.async_result->len =
    
    307
    -                          completedTable[i].len;
    
    308
    -                        tso->block_info.async_result->errCode =
    
    309
    -                          completedTable[i].errCode;
    
    305
    +                    if (tso->block_info.async_reqID == rID) {
    
    306
    +                        HsInt len     = completedTable[i].len;
    
    307
    +                        HsInt errCode = completedTable[i].errCode;
    
    310 308
     
    
    311 309
                             // Drop the matched TSO from blocked_queue
    
    312 310
                             if (prev) {
    
    ... ... @@ -322,11 +320,14 @@ start:
    322 320
                             // Terminates the run queue + this inner for-loop.
    
    323 321
                             tso->_link = END_TSO_QUEUE;
    
    324 322
                             tso->why_blocked = NotBlocked;
    
    325
    -                        // save the StgAsyncIOResult in the
    
    326
    -                        // stg_block_async_info stack frame, because
    
    327
    -                        // the block_info field will be overwritten by
    
    328
    -                        // pushOnRunQueue().
    
    329
    -                        tso->stackobj->sp[1] = (W_)tso->block_info.async_result;
    
    323
    +                        // For stg_block_async frames (read/write/doProc),
    
    324
    +                        // write len and errCode directly to the stack.
    
    325
    +                        // For stg_block_noregs frames (delay), nothing
    
    326
    +                        // to write.
    
    327
    +                        if (tso->stackobj->sp[0] == (W_)&stg_block_async_info) {
    
    328
    +                            tso->stackobj->sp[1] = (W_)len;
    
    329
    +                            tso->stackobj->sp[2] = (W_)errCode;
    
    330
    +                        }
    
    330 331
                             pushOnRunQueue(&MainCapability, tso);
    
    331 332
                             break;
    
    332 333
                         }
    
    ... ... @@ -389,3 +390,8 @@ resetAbandonRequestWait( void )
    389 390
     }
    
    390 391
     
    
    391 392
     #endif /* !defined(THREADED_RTS) */
    
    393
    +
    
    394
    +HsInt rts_EINTR(void)
    
    395
    +{
    
    396
    +    return EINTR;
    
    397
    +}

  • rts/win32/AsyncMIO.h
    ... ... @@ -27,3 +27,4 @@ extern int awaitRequests(bool wait);
    27 27
     
    
    28 28
     extern void abandonRequestWait(void);
    
    29 29
     extern void resetAbandonRequestWait(void);
    
    30
    +extern HsInt rts_EINTR(void);

  • testsuite/tests/concurrent/should_run/T26341.hs
    1
    +{-# OPTIONS_GHC -O -fno-full-laziness #-}
    
    2
    +
    
    3
    +import Control.Concurrent (threadDelay, myThreadId, forkIO, killThread)
    
    4
    +import System.IO.Unsafe (unsafePerformIO)
    
    5
    +import Control.Exception
    
    6
    +import GHC.Exts
    
    7
    +
    
    8
    +compute :: Int
    
    9
    +compute = noinline unsafePerformIO $ do
    
    10
    +    mainThreadID <- myThreadId
    
    11
    +    _ <- forkIO $ do
    
    12
    +        threadDelay 500000
    
    13
    +        killThread mainThreadID
    
    14
    +    threadDelay 1000000
    
    15
    +    return 0
    
    16
    +
    
    17
    +main = do
    
    18
    +    catch (print compute) (\(e :: AsyncException) -> print $ "1:" ++ show e)
    
    19
    +    catch (print compute) (\(e :: AsyncException) -> print $ "2:" ++ show e)
    
    20
    +    print "done"

  • testsuite/tests/concurrent/should_run/T26341.stdout
    1
    +"1:thread killed"
    
    2
    +0
    
    3
    +"done"

  • testsuite/tests/concurrent/should_run/T26341a.hs
    1
    +-- Test that re-evaluating an AP_STACK from an interrupted async I/O call
    
    2
    +-- does not crash. On Windows non-threaded RTS, re-entry returns EINTR
    
    3
    +-- which readRawBufferPtr converts to IOException Interrupted. On the
    
    4
    +-- threaded RTS (any platform), the blocking read is re-attempted and
    
    5
    +-- succeeds because we write a byte to the pipe between evaluations.
    
    6
    +--
    
    7
    +-- Before the fix for #26341, re-evaluation on Windows would crash or read
    
    8
    +-- uninitialized memory from a freed StgAsyncIOResult.
    
    9
    +{-# OPTIONS_GHC -O -fno-full-laziness #-}
    
    10
    +
    
    11
    +import Control.Concurrent (threadDelay, myThreadId, forkIO, killThread, rtsSupportsBoundThreads)
    
    12
    +import Control.Exception
    
    13
    +import Data.IORef
    
    14
    +import Foreign
    
    15
    +import Foreign.C
    
    16
    +import GHC.Exts
    
    17
    +import GHC.IO.Exception (IOErrorType(..), IOException(..))
    
    18
    +import GHC.IO.FD (FD(..), readRawBufferPtr, writeRawBufferPtr)
    
    19
    +import System.Info (os)
    
    20
    +import System.IO.Unsafe (unsafePerformIO)
    
    21
    +import System.Process (createPipeFd)
    
    22
    +
    
    23
    +-- Store the write fd so main can feed data into the pipe between
    
    24
    +-- evaluations.  On Unix this unblocks the re-entered read; on Windows
    
    25
    +-- stg_block_async returns EINTR regardless.
    
    26
    +{-# NOINLINE writeFdRef #-}
    
    27
    +writeFdRef :: IORef CInt
    
    28
    +writeFdRef = unsafePerformIO $ newIORef (-1)
    
    29
    +
    
    30
    +-- | A thunk whose unsafePerformIO blocks on a pipe read.  A forked
    
    31
    +-- thread kills the main thread after 200ms, which creates an AP_STACK.
    
    32
    +{-# NOINLINE blockedRead #-}
    
    33
    +blockedRead :: ()
    
    34
    +blockedRead = noinline unsafePerformIO $ do
    
    35
    +    (readFd, writeFd) <- createPipeFd
    
    36
    +    writeIORef writeFdRef writeFd
    
    37
    +    buf <- mallocBytes 1
    
    38
    +    mainTid <- myThreadId
    
    39
    +    _ <- forkIO $ do
    
    40
    +        threadDelay 200000  -- 200ms
    
    41
    +        killThread mainTid
    
    42
    +    -- readRawBufferPtr dispatches to asyncReadRawBufferPtr on Windows
    
    43
    +    -- non-threaded RTS; on Unix it uses threadWaitRead + read().
    
    44
    +    _ <- readRawBufferPtr "blockedRead" (FD readFd 0) buf 0 1
    
    45
    +    return ()
    
    46
    +
    
    47
    +main :: IO ()
    
    48
    +main = do
    
    49
    +    -- First evaluation: the thunk blocks on the pipe read, gets killed.
    
    50
    +    catch (evaluate blockedRead)
    
    51
    +          (\(e :: AsyncException) -> putStrLn $ "caught: " ++ show e)
    
    52
    +
    
    53
    +    -- Write a byte so the re-entered read can complete on Unix.
    
    54
    +    wfd <- readIORef writeFdRef
    
    55
    +    buf <- mallocBytes 1
    
    56
    +    poke buf 0
    
    57
    +    _ <- writeRawBufferPtr "unblock" (FD wfd 0) buf 0 1
    
    58
    +
    
    59
    +    -- Second evaluation: AP_STACK re-enters.
    
    60
    +    -- Non-threaded Windows: asyncRead returns (-1, EINTR) → IOException
    
    61
    +    -- Threaded / Unix:      read succeeds → returns normally
    
    62
    +    let expectEINTR = os == "mingw32" && not rtsSupportsBoundThreads
    
    63
    +    result <- try (evaluate blockedRead)
    
    64
    +    case result of
    
    65
    +        Left e
    
    66
    +            | Just ioe <- fromException e
    
    67
    +            , ioe_type (ioe :: IOException) == Interrupted
    
    68
    +            -> putStrLn "re-evaluated ok"
    
    69
    +            | otherwise
    
    70
    +            -> putStrLn $ "unexpected: " ++ show e
    
    71
    +        Right ()
    
    72
    +            | expectEINTR -> putStrLn "unexpected: expected EINTR"
    
    73
    +            | otherwise   -> putStrLn "re-evaluated ok"
    
    74
    +
    
    75
    +    putStrLn "done"

  • testsuite/tests/concurrent/should_run/T26341a.stdout
    1
    +caught: thread killed
    
    2
    +re-evaluated ok
    
    3
    +done

  • testsuite/tests/concurrent/should_run/T26341b.hs
    1
    +-- Stress test for #26341: repeatedly interrupt async-blocked threads and
    
    2
    +-- re-enter their AP_STACKs. Before the fix, re-entering a thunk whose
    
    3
    +-- unsafePerformIO was blocked on an async I/O call (Windows non-threaded
    
    4
    +-- RTS) would read uninitialized memory or free a dangling pointer,
    
    5
    +-- because stg_block_async reserved a stack slot for a heap-allocated
    
    6
    +-- StgAsyncIOResult that became invalid after an async exception.
    
    7
    +--
    
    8
    +-- This test spawns many concurrent workers, each of which:
    
    9
    +--   1. Creates a pipe.
    
    10
    +--   2. Builds a thunk that blocks on a pipe read via unsafePerformIO.
    
    11
    +--   3. Evaluates the thunk and kills it with an async exception.
    
    12
    +--   4. Re-evaluates the thunk (AP_STACK re-entry).
    
    13
    +--   5. Repeats many times.
    
    14
    +--
    
    15
    +-- On threaded RTS / Unix the re-entered read succeeds (we write a byte
    
    16
    +-- first). On Windows non-threaded RTS the re-entered async call returns
    
    17
    +-- EINTR. Both paths exercise the fixed stack-frame layout.
    
    18
    +{-# OPTIONS_GHC -O -fno-full-laziness #-}
    
    19
    +
    
    20
    +import Control.Concurrent
    
    21
    +import Control.Exception
    
    22
    +import Foreign
    
    23
    +import Foreign.C
    
    24
    +import GHC.Exts
    
    25
    +import GHC.IO.Exception (IOErrorType(..), IOException(..))
    
    26
    +import GHC.IO.FD (FD(..), readRawBufferPtr, writeRawBufferPtr)
    
    27
    +import System.IO (hFlush, stdout)
    
    28
    +import System.IO.Unsafe (unsafePerformIO)
    
    29
    +import System.Posix.Internals (c_close)
    
    30
    +import System.Process (createPipeFd)
    
    31
    +
    
    32
    +iterations :: Int
    
    33
    +iterations = 200
    
    34
    +
    
    35
    +workers :: Int
    
    36
    +workers = 4
    
    37
    +
    
    38
    +-- Each worker independently performs `iterations` rounds of:
    
    39
    +-- block on pipe read → interrupt → re-evaluate the AP_STACK.
    
    40
    +worker :: Int -> MVar () -> IO ()
    
    41
    +worker wid done = do
    
    42
    +    buf <- mallocBytes 1
    
    43
    +    let go 0 = return ()
    
    44
    +        go n = do
    
    45
    +            (readFd, writeFd) <- createPipeFd
    
    46
    +
    
    47
    +            -- Build a fresh thunk each iteration so we get a new AP_STACK.
    
    48
    +            let {-# NOINLINE blockedThunk #-}
    
    49
    +                blockedThunk :: ()
    
    50
    +                blockedThunk = noinline unsafePerformIO $ do
    
    51
    +                    tid <- myThreadId
    
    52
    +                    _ <- forkIO $ do
    
    53
    +                        threadDelay 1000  -- 1ms: tight window
    
    54
    +                        killThread tid
    
    55
    +                    _ <- readRawBufferPtr "stress" (FD readFd 0) buf 0 1
    
    56
    +                    return ()
    
    57
    +
    
    58
    +            -- First evaluation: block and get killed.
    
    59
    +            catch (evaluate blockedThunk)
    
    60
    +                  (\(_ :: SomeException) -> return ())
    
    61
    +
    
    62
    +            -- Write a byte so the re-entered read can complete on
    
    63
    +            -- threaded RTS / Unix.
    
    64
    +            poke buf 0
    
    65
    +            _ <- writeRawBufferPtr "unblock" (FD writeFd 0) buf 0 1
    
    66
    +
    
    67
    +            -- Second evaluation: AP_STACK re-entry.
    
    68
    +            result <- try (evaluate blockedThunk)
    
    69
    +            case result of
    
    70
    +                Left e
    
    71
    +                    | Just ioe <- fromException e
    
    72
    +                    , ioe_type (ioe :: IOException) == Interrupted
    
    73
    +                    -> return ()  -- expected on Windows non-threaded
    
    74
    +                    | otherwise
    
    75
    +                    -> throwIO (userError $
    
    76
    +                         "worker " ++ show wid ++ " iteration " ++ show n ++
    
    77
    +                         ": unexpected exception: " ++ show e)
    
    78
    +                Right () -> return ()  -- expected on threaded / Unix
    
    79
    +
    
    80
    +            -- Close the pipe fds.
    
    81
    +            _ <- c_close readFd
    
    82
    +            _ <- c_close writeFd
    
    83
    +
    
    84
    +            go (n - 1)
    
    85
    +
    
    86
    +    go iterations
    
    87
    +    putMVar done ()
    
    88
    +
    
    89
    +main :: IO ()
    
    90
    +main = do
    
    91
    +    dones <- mapM (\wid -> do
    
    92
    +        done <- newEmptyMVar
    
    93
    +        _ <- forkIO (worker wid done)
    
    94
    +        return done
    
    95
    +        ) [1..workers]
    
    96
    +
    
    97
    +    -- Wait for all workers to finish.
    
    98
    +    mapM_ takeMVar dones
    
    99
    +
    
    100
    +    putStrLn "stress test passed"
    
    101
    +    hFlush stdout

  • testsuite/tests/concurrent/should_run/T26341b.stdout
    1
    +stress test passed

  • testsuite/tests/concurrent/should_run/all.T
    ... ... @@ -298,3 +298,19 @@ test('hs_try_putmvar003',
    298 298
     
    
    299 299
     # Check forkIO exception determinism under optimization
    
    300 300
     test('T13330', normal, compile_and_run, ['-O'])
    
    301
    +
    
    302
    +test('T26341', normal, compile_and_run, [''])
    
    303
    +
    
    304
    +# Test EINTR for async I/O interrupted by an exception (#26341)
    
    305
    +test('T26341a'
    
    306
    +    # test uses pipe operations which are not supported by the JS/wasm backends
    
    307
    +    , when(arch('wasm32') or arch('javascript'), skip)
    
    308
    +    , compile_and_run, ['-package process'])
    
    309
    +
    
    310
    +# Stress test: many threads repeatedly interrupt and re-enter async-blocked
    
    311
    +# thunks (#26341). Before the fix, this would crash due to dangling
    
    312
    +# StgAsyncIOResult pointers on the stack.
    
    313
    +test('T26341b'
    
    314
    +    # test uses pipe operations which are not supported by the JS/wasm backends
    
    315
    +    , when(arch('wasm32') or arch('javascript'), skip)
    
    316
    +    , compile_and_run, ['-package process'])

  • utils/deriveConstants/Main.hs
    ... ... @@ -646,12 +646,7 @@ wanteds os = concat
    646 646
                -- Note that this conditional part only affects the C headers.
    
    647 647
                -- That's important, as it means we get the same PlatformConstants
    
    648 648
                -- type on all platforms.
    
    649
    -          ,if os == Just Windows
    
    650
    -           then concat [structSize  C "StgAsyncIOResult"
    
    651
    -                       ,structField C "StgAsyncIOResult" "reqID"
    
    652
    -                       ,structField C "StgAsyncIOResult" "len"
    
    653
    -                       ,structField C "StgAsyncIOResult" "errCode"]
    
    654
    -           else []
    
    649
    +          ,[]
    
    655 650
     
    
    656 651
                -- struct HsIface
    
    657 652
               ,structField C "HsIface" "processRemoteCompletion_closure"
    
    ... ... @@ -814,9 +809,6 @@ getWanted verbose os tmpdir gccProgram gccFlags nmProgram mobjdumpProgram
    814 809
                          "",
    
    815 810
                          "#define PROFILING",
    
    816 811
                          "#define THREADED_RTS",
    
    817
    -                     -- We need to define this if we want StgAsyncIOResult
    
    818
    -                     -- struct to be present after CPP
    
    819
    -                     --
    
    820 812
                          -- FIXME: rts/PosixSource.h should include ghcplatform.h
    
    821 813
                          -- which should set this. There is a mismatch host/target
    
    822 814
                          -- again...