Marge Bot pushed to branch wip/marge_bot_batch_merge_job at Glasgow Haskell Compiler / GHC

Commits:

19 changed files:

Changes:

  • changelog.d/T27046
    1
    +section: compiler
    
    2
    +issues: #27046
    
    3
    +mrs: !16031
    
    4
    +synopsis:
    
    5
    +  Avoid AArch64 register clobbering bug in MUL2
    
    6
    +description:
    
    7
    +  Fixes an issue in which, on AArch64, code generation for the MUL2 operation
    
    8
    +  could clobber one of the input operands when computing the lower bits, which
    
    9
    +  rendered invalid the subsequent computation of the high bits.

  • compiler/GHC/CmmToAsm/AArch64/CodeGen.hs
    ... ... @@ -2300,11 +2300,19 @@ genCCall target dest_regs arg_regs = do
    2300 2300
                   let lo = getRegisterReg platform (CmmLocal dst_lo)
    
    2301 2301
                       hi = getRegisterReg platform (CmmLocal dst_hi)
    
    2302 2302
                       nd = getRegisterReg platform (CmmLocal dst_needed)
    
    2303
    +
    
    2304
    +              -- Generate a fresh virtual register for the low word computation.
    
    2305
    +              -- This avoids clobbering reg_a or reg_b in the first MUL instruction,
    
    2306
    +              -- which could for example happen if 'lo' and 'reg_a' are the same
    
    2307
    +              -- virtual register.
    
    2308
    +              tmp_lo <- getNewRegNat II64
    
    2309
    +
    
    2303 2310
                   return $
    
    2304 2311
                       code_x `appOL`
    
    2305 2312
                       code_y `snocOL`
    
    2306
    -                  MUL   II64 (OpReg W64 lo) (OpReg W64 reg_a) (OpReg W64 reg_b) `snocOL`
    
    2313
    +                  MUL   II64 (OpReg W64 tmp_lo) (OpReg W64 reg_a) (OpReg W64 reg_b) `snocOL`
    
    2307 2314
                       SMULH (OpReg W64 hi) (OpReg W64 reg_a) (OpReg W64 reg_b) `snocOL`
    
    2315
    +                  MOV   (OpReg W64 lo) (OpReg W64 tmp_lo) `snocOL`
    
    2308 2316
                       -- Are all high bits equal to the sign bit of the low word?
    
    2309 2317
                       -- nd = (hi == ASR(lo,width-1)) ? 1 : 0
    
    2310 2318
                       CMP   (OpReg W64 hi) (OpRegShift W64 lo SASR (widthInBits w - 1)) `snocOL`
    

  • rts/Capability.c
    ... ... @@ -443,13 +443,6 @@ void
    443 443
     moreCapabilities (uint32_t from USED_IF_THREADS, uint32_t to USED_IF_THREADS)
    
    444 444
     {
    
    445 445
     #if defined(THREADED_RTS)
    
    446
    -    // We must disable the timer while we do this since the tick handler may
    
    447
    -    // call contextSwitchAllCapabilities, which may see the capabilities array
    
    448
    -    // as we free it. The alternative would be to protect the capabilities
    
    449
    -    // array with a lock but this seems more expensive than necessary.
    
    450
    -    // See #17289.
    
    451
    -    stopTimer();
    
    452
    -
    
    453 446
         if (to == 1) {
    
    454 447
             // THREADED_RTS must work on builds that don't have a mutable
    
    455 448
             // BaseReg (eg. unregisterised), so in this case
    
    ... ... @@ -470,8 +463,6 @@ moreCapabilities (uint32_t from USED_IF_THREADS, uint32_t to USED_IF_THREADS)
    470 463
         }
    
    471 464
     
    
    472 465
         debugTrace(DEBUG_sched, "allocated %d more capabilities", to - from);
    
    473
    -
    
    474
    -    startTimer();
    
    475 466
     #endif
    
    476 467
     }
    
    477 468
     
    

  • rts/Schedule.c
    ... ... @@ -37,6 +37,7 @@
    37 37
     #include "win32/AsyncWinIO.h"
    
    38 38
     #endif
    
    39 39
     #include "Trace.h"
    
    40
    +#include "eventlog/EventLog.h"
    
    40 41
     #include "RaiseAsync.h"
    
    41 42
     #include "Threads.h"
    
    42 43
     #include "Timer.h"
    
    ... ... @@ -2100,24 +2101,31 @@ forkProcess(HsStablePtr *entry
    2100 2101
         ACQUIRE_LOCK(&all_tasks_mutex);
    
    2101 2102
     #endif
    
    2102 2103
     
    
    2103
    -    stopTimer(); // See #4074
    
    2104
    -
    
    2105 2104
     #if defined(TRACING)
    
    2106
    -    flushAllCapsEventsBufs(); // so that child won't inherit dirty file buffers
    
    2105
    +#if defined(HAVE_PREEMPTION)
    
    2106
    +    // We must hold the eventlog global mutex over the fork to prevent the
    
    2107
    +    // timer thread from trying to post events. While holding the mutex we need
    
    2108
    +    // to flush the eventlogs (global and per-cap) so that child won't inherit
    
    2109
    +    // dirty eventlog buffers or file buffers.
    
    2110
    +    ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
    
    2111
    +#endif
    
    2112
    +    flushAllCapsEventsBufs_();
    
    2107 2113
     #endif
    
    2108 2114
     
    
    2109 2115
         pid = fork();
    
    2110 2116
     
    
    2111 2117
         if (pid) { // parent
    
    2112 2118
     
    
    2113
    -        startTimer(); // #4074
    
    2114
    -
    
    2115 2119
             RELEASE_LOCK(&sched_mutex);
    
    2116 2120
             RELEASE_LOCK(&sm_mutex);
    
    2117 2121
             RELEASE_LOCK(&stable_ptr_mutex);
    
    2118 2122
             RELEASE_LOCK(&stable_name_mutex);
    
    2119 2123
             RELEASE_LOCK(&task->lock);
    
    2120 2124
     
    
    2125
    +#if defined(TRACING) && defined(HAVE_PREEMPTION)
    
    2126
    +        RELEASE_LOCK_ALWAYS(&eventBufMutex);
    
    2127
    +#endif
    
    2128
    +
    
    2121 2129
     #if defined(THREADED_RTS)
    
    2122 2130
             /* N.B. releaseCapability_ below may need to take all_tasks_mutex */
    
    2123 2131
             RELEASE_LOCK(&all_tasks_mutex);
    
    ... ... @@ -2303,12 +2311,6 @@ setNumCapabilities (uint32_t new_n_capabilities USED_IF_THREADS)
    2303 2311
         cap = rts_lock();
    
    2304 2312
         task = cap->running_task;
    
    2305 2313
     
    
    2306
    -
    
    2307
    -    // N.B. We must stop the interval timer while we are changing the
    
    2308
    -    // capabilities array lest handle_tick may try to context switch
    
    2309
    -    // an old capability. See #17289.
    
    2310
    -    stopTimer();
    
    2311
    -
    
    2312 2314
         stopAllCapabilities(&cap, task);
    
    2313 2315
     
    
    2314 2316
         if (new_n_capabilities < enabled_capabilities)
    
    ... ... @@ -2364,9 +2366,7 @@ setNumCapabilities (uint32_t new_n_capabilities USED_IF_THREADS)
    2364 2366
                 tracingAddCapabilities(n_capabilities, new_n_capabilities);
    
    2365 2367
     #endif
    
    2366 2368
     
    
    2367
    -            // Resize the capabilities array
    
    2368
    -            // NB. after this, capabilities points somewhere new.  Any pointers
    
    2369
    -            // of type (Capability *) are now invalid.
    
    2369
    +            // Allocate and initialise the extra capabilities
    
    2370 2370
                 moreCapabilities(n_capabilities, new_n_capabilities);
    
    2371 2371
     
    
    2372 2372
                 // Resize and update storage manager data structures
    
    ... ... @@ -2394,8 +2394,6 @@ setNumCapabilities (uint32_t new_n_capabilities USED_IF_THREADS)
    2394 2394
         // Notify IO manager that the number of capabilities has changed.
    
    2395 2395
         notifyIOManagerCapabilitiesChanged(&cap);
    
    2396 2396
     
    
    2397
    -    startTimer();
    
    2398
    -
    
    2399 2397
         rts_unlock(cap);
    
    2400 2398
     
    
    2401 2399
     #endif // THREADED_RTS
    

  • rts/Timer.c
    ... ... @@ -28,11 +28,6 @@
    28 28
     #include "RtsSignals.h"
    
    29 29
     #include "rts/EventLogWriter.h"
    
    30 30
     
    
    31
    -// See Note [No timer on wasm32]
    
    32
    -#if !defined(wasm32_HOST_ARCH)
    
    33
    -#define HAVE_PREEMPTION
    
    34
    -#endif
    
    35
    -
    
    36 31
     // This global counter is used to allow multiple threads to stop the
    
    37 32
     // timer temporarily with a stopTimer()/startTimer() pair.  If
    
    38 33
     //      timer_enabled  == 0          timer is enabled
    

  • rts/eventlog/EventLog.c
    ... ... @@ -129,8 +129,11 @@ typedef struct _EventsBuf {
    129 129
     static EventsBuf *capEventBuf; // one EventsBuf for each Capability
    
    130 130
     
    
    131 131
     static EventsBuf eventBuf; // an EventsBuf not associated with any Capability
    
    132
    -#if defined(THREADED_RTS)
    
    133
    -static Mutex eventBufMutex; // protected by this mutex
    
    132
    +#if defined(HAVE_PREEMPTION)
    
    133
    +// Note that this mutex is used even in the non-threaded RTS, since the timer
    
    134
    +// thread posts events and flushes. So _all_ uses of this mutex must use
    
    135
    +// ACQUIRE_LOCK_ALWAYS/RELEASE_LOCK_ALWAYS.
    
    136
    +Mutex eventBufMutex; // protects eventBuf above
    
    134 137
     #endif
    
    135 138
     
    
    136 139
     // Event type
    
    ... ... @@ -393,8 +396,10 @@ initEventLogging(void)
    393 396
         moreCapEventBufs(0, get_n_capabilities());
    
    394 397
     
    
    395 398
         initEventsBuf(&eventBuf, EVENT_LOG_SIZE, (EventCapNo)(-1));
    
    396
    -#if defined(THREADED_RTS)
    
    399
    +#if defined(HAVE_PREEMPTION)
    
    397 400
         initMutex(&eventBufMutex);
    
    401
    +#endif
    
    402
    +#if defined(THREADED_RTS)
    
    398 403
         initMutex(&state_change_mutex);
    
    399 404
     #endif
    
    400 405
     }
    
    ... ... @@ -416,7 +421,7 @@ startEventLogging_(void)
    416 421
     {
    
    417 422
         initEventLogWriter();
    
    418 423
     
    
    419
    -    ACQUIRE_LOCK(&eventBufMutex);
    
    424
    +    ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
    
    420 425
         postHeaderEvents();
    
    421 426
     
    
    422 427
         /*
    
    ... ... @@ -425,7 +430,7 @@ startEventLogging_(void)
    425 430
          */
    
    426 431
         printAndClearEventBuf(&eventBuf);
    
    427 432
     
    
    428
    -    RELEASE_LOCK(&eventBufMutex);
    
    433
    +    RELEASE_LOCK_ALWAYS(&eventBufMutex);
    
    429 434
     
    
    430 435
         return true;
    
    431 436
     }
    
    ... ... @@ -495,7 +500,7 @@ endEventLogging(void)
    495 500
     
    
    496 501
         flushEventLog_(NULL);
    
    497 502
     
    
    498
    -    ACQUIRE_LOCK(&eventBufMutex);
    
    503
    +    ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
    
    499 504
     
    
    500 505
         // Mark end of events (data).
    
    501 506
         postEventTypeNum(&eventBuf, EVENT_DATA_END);
    
    ... ... @@ -503,7 +508,7 @@ endEventLogging(void)
    503 508
         // Flush the end of data marker.
    
    504 509
         printAndClearEventBuf(&eventBuf);
    
    505 510
     
    
    506
    -    RELEASE_LOCK(&eventBufMutex);
    
    511
    +    RELEASE_LOCK_ALWAYS(&eventBufMutex);
    
    507 512
     
    
    508 513
         stopEventLogWriter();
    
    509 514
         event_log_writer = NULL;
    
    ... ... @@ -666,7 +671,7 @@ void
    666 671
     postCapEvent (EventTypeNum  tag,
    
    667 672
                   EventCapNo    capno)
    
    668 673
     {
    
    669
    -    ACQUIRE_LOCK(&eventBufMutex);
    
    674
    +    ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
    
    670 675
         ensureRoomForEvent(&eventBuf, tag);
    
    671 676
     
    
    672 677
         postEventHeader(&eventBuf, tag);
    
    ... ... @@ -685,14 +690,14 @@ postCapEvent (EventTypeNum tag,
    685 690
             barf("postCapEvent: unknown event tag %d", tag);
    
    686 691
         }
    
    687 692
     
    
    688
    -    RELEASE_LOCK(&eventBufMutex);
    
    693
    +    RELEASE_LOCK_ALWAYS(&eventBufMutex);
    
    689 694
     }
    
    690 695
     
    
    691 696
     void postCapsetEvent (EventTypeNum tag,
    
    692 697
                           EventCapsetID capset,
    
    693 698
                           StgWord info)
    
    694 699
     {
    
    695
    -    ACQUIRE_LOCK(&eventBufMutex);
    
    700
    +    ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
    
    696 701
         ensureRoomForEvent(&eventBuf, tag);
    
    697 702
     
    
    698 703
         postEventHeader(&eventBuf, tag);
    
    ... ... @@ -726,7 +731,7 @@ void postCapsetEvent (EventTypeNum tag,
    726 731
             barf("postCapsetEvent: unknown event tag %d", tag);
    
    727 732
         }
    
    728 733
     
    
    729
    -    RELEASE_LOCK(&eventBufMutex);
    
    734
    +    RELEASE_LOCK_ALWAYS(&eventBufMutex);
    
    730 735
     }
    
    731 736
     
    
    732 737
     void postCapsetStrEvent (EventTypeNum tag,
    
    ... ... @@ -740,14 +745,14 @@ void postCapsetStrEvent (EventTypeNum tag,
    740 745
             return;
    
    741 746
         }
    
    742 747
     
    
    743
    -    ACQUIRE_LOCK(&eventBufMutex);
    
    748
    +    ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
    
    744 749
     
    
    745 750
         if (!hasRoomForVariableEvent(&eventBuf, size)){
    
    746 751
             printAndClearEventBuf(&eventBuf);
    
    747 752
     
    
    748 753
             if (!hasRoomForVariableEvent(&eventBuf, size)){
    
    749 754
                 errorBelch("Event size exceeds buffer size, bail out");
    
    750
    -            RELEASE_LOCK(&eventBufMutex);
    
    755
    +            RELEASE_LOCK_ALWAYS(&eventBufMutex);
    
    751 756
                 return;
    
    752 757
             }
    
    753 758
         }
    
    ... ... @@ -758,7 +763,7 @@ void postCapsetStrEvent (EventTypeNum tag,
    758 763
     
    
    759 764
         postBuf(&eventBuf, (StgWord8*) msg, strsize);
    
    760 765
     
    
    761
    -    RELEASE_LOCK(&eventBufMutex);
    
    766
    +    RELEASE_LOCK_ALWAYS(&eventBufMutex);
    
    762 767
     }
    
    763 768
     
    
    764 769
     void postCapsetVecEvent (EventTypeNum tag,
    
    ... ... @@ -783,14 +788,14 @@ void postCapsetVecEvent (EventTypeNum tag,
    783 788
             }
    
    784 789
         }
    
    785 790
     
    
    786
    -    ACQUIRE_LOCK(&eventBufMutex);
    
    791
    +    ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
    
    787 792
     
    
    788 793
         if (!hasRoomForVariableEvent(&eventBuf, size)){
    
    789 794
             printAndClearEventBuf(&eventBuf);
    
    790 795
     
    
    791 796
             if(!hasRoomForVariableEvent(&eventBuf, size)){
    
    792 797
                 errorBelch("Event size exceeds buffer size, bail out");
    
    793
    -            RELEASE_LOCK(&eventBufMutex);
    
    798
    +            RELEASE_LOCK_ALWAYS(&eventBufMutex);
    
    794 799
                 return;
    
    795 800
             }
    
    796 801
         }
    
    ... ... @@ -804,7 +809,7 @@ void postCapsetVecEvent (EventTypeNum tag,
    804 809
             postBuf(&eventBuf, (StgWord8*) argv[i], 1 + strlen(argv[i]));
    
    805 810
         }
    
    806 811
     
    
    807
    -    RELEASE_LOCK(&eventBufMutex);
    
    812
    +    RELEASE_LOCK_ALWAYS(&eventBufMutex);
    
    808 813
     }
    
    809 814
     
    
    810 815
     void postWallClockTime (EventCapsetID capset)
    
    ... ... @@ -813,7 +818,7 @@ void postWallClockTime (EventCapsetID capset)
    813 818
         StgWord64 sec;
    
    814 819
         StgWord32 nsec;
    
    815 820
     
    
    816
    -    ACQUIRE_LOCK(&eventBufMutex);
    
    821
    +    ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
    
    817 822
     
    
    818 823
         /* The EVENT_WALL_CLOCK_TIME event is intended to allow programs
    
    819 824
            reading the eventlog to match up the event timestamps with wall
    
    ... ... @@ -846,7 +851,7 @@ void postWallClockTime (EventCapsetID capset)
    846 851
         postWord64(&eventBuf, sec);
    
    847 852
         postWord32(&eventBuf, nsec);
    
    848 853
     
    
    849
    -    RELEASE_LOCK(&eventBufMutex);
    
    854
    +    RELEASE_LOCK_ALWAYS(&eventBufMutex);
    
    850 855
     }
    
    851 856
     
    
    852 857
     /*
    
    ... ... @@ -885,7 +890,7 @@ void postEventHeapInfo (EventCapsetID heap_capset,
    885 890
                             W_          mblockSize,
    
    886 891
                             W_          blockSize)
    
    887 892
     {
    
    888
    -    ACQUIRE_LOCK(&eventBufMutex);
    
    893
    +    ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
    
    889 894
         ensureRoomForEvent(&eventBuf, EVENT_HEAP_INFO_GHC);
    
    890 895
     
    
    891 896
         postEventHeader(&eventBuf, EVENT_HEAP_INFO_GHC);
    
    ... ... @@ -899,7 +904,7 @@ void postEventHeapInfo (EventCapsetID heap_capset,
    899 904
         postWord64(&eventBuf, mblockSize);
    
    900 905
         postWord64(&eventBuf, blockSize);
    
    901 906
     
    
    902
    -    RELEASE_LOCK(&eventBufMutex);
    
    907
    +    RELEASE_LOCK_ALWAYS(&eventBufMutex);
    
    903 908
     }
    
    904 909
     
    
    905 910
     void postEventGcStats  (Capability    *cap,
    
    ... ... @@ -952,7 +957,7 @@ void postTaskCreateEvent (EventTaskId taskId,
    952 957
                               EventCapNo capno,
    
    953 958
                               EventKernelThreadId tid)
    
    954 959
     {
    
    955
    -    ACQUIRE_LOCK(&eventBufMutex);
    
    960
    +    ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
    
    956 961
         ensureRoomForEvent(&eventBuf, EVENT_TASK_CREATE);
    
    957 962
     
    
    958 963
         postEventHeader(&eventBuf, EVENT_TASK_CREATE);
    
    ... ... @@ -961,14 +966,14 @@ void postTaskCreateEvent (EventTaskId taskId,
    961 966
         postCapNo(&eventBuf, capno);
    
    962 967
         postKernelThreadId(&eventBuf, tid);
    
    963 968
     
    
    964
    -    RELEASE_LOCK(&eventBufMutex);
    
    969
    +    RELEASE_LOCK_ALWAYS(&eventBufMutex);
    
    965 970
     }
    
    966 971
     
    
    967 972
     void postTaskMigrateEvent (EventTaskId taskId,
    
    968 973
                                EventCapNo capno,
    
    969 974
                                EventCapNo new_capno)
    
    970 975
     {
    
    971
    -    ACQUIRE_LOCK(&eventBufMutex);
    
    976
    +    ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
    
    972 977
         ensureRoomForEvent(&eventBuf, EVENT_TASK_MIGRATE);
    
    973 978
     
    
    974 979
         postEventHeader(&eventBuf, EVENT_TASK_MIGRATE);
    
    ... ... @@ -977,28 +982,28 @@ void postTaskMigrateEvent (EventTaskId taskId,
    977 982
         postCapNo(&eventBuf, capno);
    
    978 983
         postCapNo(&eventBuf, new_capno);
    
    979 984
     
    
    980
    -    RELEASE_LOCK(&eventBufMutex);
    
    985
    +    RELEASE_LOCK_ALWAYS(&eventBufMutex);
    
    981 986
     }
    
    982 987
     
    
    983 988
     void postTaskDeleteEvent (EventTaskId taskId)
    
    984 989
     {
    
    985
    -    ACQUIRE_LOCK(&eventBufMutex);
    
    990
    +    ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
    
    986 991
         ensureRoomForEvent(&eventBuf, EVENT_TASK_DELETE);
    
    987 992
     
    
    988 993
         postEventHeader(&eventBuf, EVENT_TASK_DELETE);
    
    989 994
         /* EVENT_TASK_DELETE (taskID) */
    
    990 995
         postTaskId(&eventBuf, taskId);
    
    991 996
     
    
    992
    -    RELEASE_LOCK(&eventBufMutex);
    
    997
    +    RELEASE_LOCK_ALWAYS(&eventBufMutex);
    
    993 998
     }
    
    994 999
     
    
    995 1000
     void
    
    996 1001
     postEventNoCap (EventTypeNum tag)
    
    997 1002
     {
    
    998
    -    ACQUIRE_LOCK(&eventBufMutex);
    
    1003
    +    ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
    
    999 1004
         ensureRoomForEvent(&eventBuf, tag);
    
    1000 1005
         postEventHeader(&eventBuf, tag);
    
    1001
    -    RELEASE_LOCK(&eventBufMutex);
    
    1006
    +    RELEASE_LOCK_ALWAYS(&eventBufMutex);
    
    1002 1007
     }
    
    1003 1008
     
    
    1004 1009
     void
    
    ... ... @@ -1042,9 +1047,9 @@ void postLogMsg(EventsBuf *eb, EventTypeNum type, char *msg, va_list ap)
    1042 1047
     
    
    1043 1048
     void postMsg(char *msg, va_list ap)
    
    1044 1049
     {
    
    1045
    -    ACQUIRE_LOCK(&eventBufMutex);
    
    1050
    +    ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
    
    1046 1051
         postLogMsg(&eventBuf, EVENT_LOG_MSG, msg, ap);
    
    1047
    -    RELEASE_LOCK(&eventBufMutex);
    
    1052
    +    RELEASE_LOCK_ALWAYS(&eventBufMutex);
    
    1048 1053
     }
    
    1049 1054
     
    
    1050 1055
     void postCapMsg(Capability *cap, char *msg, va_list ap)
    
    ... ... @@ -1138,32 +1143,32 @@ void postConcUpdRemSetFlush(Capability *cap)
    1138 1143
     
    
    1139 1144
     void postConcMarkEnd(StgWord32 marked_obj_count)
    
    1140 1145
     {
    
    1141
    -    ACQUIRE_LOCK(&eventBufMutex);
    
    1146
    +    ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
    
    1142 1147
         ensureRoomForEvent(&eventBuf, EVENT_CONC_MARK_END);
    
    1143 1148
         postEventHeader(&eventBuf, EVENT_CONC_MARK_END);
    
    1144 1149
         postWord32(&eventBuf, marked_obj_count);
    
    1145
    -    RELEASE_LOCK(&eventBufMutex);
    
    1150
    +    RELEASE_LOCK_ALWAYS(&eventBufMutex);
    
    1146 1151
     }
    
    1147 1152
     
    
    1148 1153
     void postNonmovingHeapCensus(uint16_t blk_size,
    
    1149 1154
                                  const struct NonmovingAllocCensus *census)
    
    1150 1155
     {
    
    1151
    -    ACQUIRE_LOCK(&eventBufMutex);
    
    1156
    +    ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
    
    1152 1157
         postEventHeader(&eventBuf, EVENT_NONMOVING_HEAP_CENSUS);
    
    1153 1158
         postWord16(&eventBuf, blk_size);
    
    1154 1159
         postWord32(&eventBuf, census->n_active_segs);
    
    1155 1160
         postWord32(&eventBuf, census->n_filled_segs);
    
    1156 1161
         postWord32(&eventBuf, census->n_live_blocks);
    
    1157
    -    RELEASE_LOCK(&eventBufMutex);
    
    1162
    +    RELEASE_LOCK_ALWAYS(&eventBufMutex);
    
    1158 1163
     }
    
    1159 1164
     
    
    1160 1165
     void postNonmovingPrunedSegments(uint32_t pruned_segments, uint32_t free_segments)
    
    1161 1166
     {
    
    1162
    -    ACQUIRE_LOCK(&eventBufMutex);
    
    1167
    +    ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
    
    1163 1168
         postEventHeader(&eventBuf, EVENT_NONMOVING_PRUNED_SEGMENTS);
    
    1164 1169
         postWord32(&eventBuf, pruned_segments);
    
    1165 1170
         postWord32(&eventBuf, free_segments);
    
    1166
    -    RELEASE_LOCK(&eventBufMutex);
    
    1171
    +    RELEASE_LOCK_ALWAYS(&eventBufMutex);
    
    1167 1172
     }
    
    1168 1173
     
    
    1169 1174
     void closeBlockMarker (EventsBuf *ebuf)
    
    ... ... @@ -1224,7 +1229,7 @@ static HeapProfBreakdown getHeapProfBreakdown(void)
    1224 1229
     
    
    1225 1230
     void postHeapProfBegin(void)
    
    1226 1231
     {
    
    1227
    -    ACQUIRE_LOCK(&eventBufMutex);
    
    1232
    +    ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
    
    1228 1233
         PROFILING_FLAGS *flags = &RtsFlags.ProfFlags;
    
    1229 1234
         StgWord modSelector_len   =
    
    1230 1235
             flags->modSelector ? strlen(flags->modSelector) : 0;
    
    ... ... @@ -1258,42 +1263,42 @@ void postHeapProfBegin(void)
    1258 1263
         postStringLen(&eventBuf, flags->ccsSelector, ccsSelector_len);
    
    1259 1264
         postStringLen(&eventBuf, flags->retainerSelector, retainerSelector_len);
    
    1260 1265
         postStringLen(&eventBuf, flags->bioSelector, bioSelector_len);
    
    1261
    -    RELEASE_LOCK(&eventBufMutex);
    
    1266
    +    RELEASE_LOCK_ALWAYS(&eventBufMutex);
    
    1262 1267
     }
    
    1263 1268
     
    
    1264 1269
     void postHeapProfSampleBegin(StgInt era)
    
    1265 1270
     {
    
    1266
    -    ACQUIRE_LOCK(&eventBufMutex);
    
    1271
    +    ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
    
    1267 1272
         ensureRoomForEvent(&eventBuf, EVENT_HEAP_PROF_SAMPLE_BEGIN);
    
    1268 1273
         postEventHeader(&eventBuf, EVENT_HEAP_PROF_SAMPLE_BEGIN);
    
    1269 1274
         postWord64(&eventBuf, era);
    
    1270
    -    RELEASE_LOCK(&eventBufMutex);
    
    1275
    +    RELEASE_LOCK_ALWAYS(&eventBufMutex);
    
    1271 1276
     }
    
    1272 1277
     
    
    1273 1278
     
    
    1274 1279
     void postHeapBioProfSampleBegin(StgInt era, StgWord64 time)
    
    1275 1280
     {
    
    1276
    -    ACQUIRE_LOCK(&eventBufMutex);
    
    1281
    +    ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
    
    1277 1282
         ensureRoomForEvent(&eventBuf, EVENT_HEAP_BIO_PROF_SAMPLE_BEGIN);
    
    1278 1283
         postEventHeader(&eventBuf, EVENT_HEAP_BIO_PROF_SAMPLE_BEGIN);
    
    1279 1284
         postWord64(&eventBuf, era);
    
    1280 1285
         postWord64(&eventBuf, time);
    
    1281
    -    RELEASE_LOCK(&eventBufMutex);
    
    1286
    +    RELEASE_LOCK_ALWAYS(&eventBufMutex);
    
    1282 1287
     }
    
    1283 1288
     
    
    1284 1289
     void postHeapProfSampleEnd(StgInt era)
    
    1285 1290
     {
    
    1286
    -    ACQUIRE_LOCK(&eventBufMutex);
    
    1291
    +    ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
    
    1287 1292
         ensureRoomForEvent(&eventBuf, EVENT_HEAP_PROF_SAMPLE_END);
    
    1288 1293
         postEventHeader(&eventBuf, EVENT_HEAP_PROF_SAMPLE_END);
    
    1289 1294
         postWord64(&eventBuf, era);
    
    1290
    -    RELEASE_LOCK(&eventBufMutex);
    
    1295
    +    RELEASE_LOCK_ALWAYS(&eventBufMutex);
    
    1291 1296
     }
    
    1292 1297
     
    
    1293 1298
     void postHeapProfSampleString(const char *label,
    
    1294 1299
                                   StgWord64 residency)
    
    1295 1300
     {
    
    1296
    -    ACQUIRE_LOCK(&eventBufMutex);
    
    1301
    +    ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
    
    1297 1302
         StgWord label_len = strlen(label);
    
    1298 1303
         StgWord len = 1+8+label_len+1;
    
    1299 1304
         CHECK(!ensureRoomForVariableEvent(&eventBuf, len));
    
    ... ... @@ -1303,7 +1308,7 @@ void postHeapProfSampleString(const char *label,
    1303 1308
         postWord8(&eventBuf, 0);
    
    1304 1309
         postWord64(&eventBuf, residency);
    
    1305 1310
         postStringLen(&eventBuf, label, label_len);
    
    1306
    -    RELEASE_LOCK(&eventBufMutex);
    
    1311
    +    RELEASE_LOCK_ALWAYS(&eventBufMutex);
    
    1307 1312
     }
    
    1308 1313
     
    
    1309 1314
     #if defined(PROFILING)
    
    ... ... @@ -1313,7 +1318,7 @@ void postHeapProfCostCentre(StgWord32 ccID,
    1313 1318
                                 const char *srcloc,
    
    1314 1319
                                 StgBool is_caf)
    
    1315 1320
     {
    
    1316
    -    ACQUIRE_LOCK(&eventBufMutex);
    
    1321
    +    ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
    
    1317 1322
         StgWord label_len = strlen(label);
    
    1318 1323
         StgWord module_len = strlen(module);
    
    1319 1324
         StgWord srcloc_len = strlen(srcloc);
    
    ... ... @@ -1326,13 +1331,13 @@ void postHeapProfCostCentre(StgWord32 ccID,
    1326 1331
         postStringLen(&eventBuf, module, module_len);
    
    1327 1332
         postStringLen(&eventBuf, srcloc, srcloc_len);
    
    1328 1333
         postWord8(&eventBuf, is_caf);
    
    1329
    -    RELEASE_LOCK(&eventBufMutex);
    
    1334
    +    RELEASE_LOCK_ALWAYS(&eventBufMutex);
    
    1330 1335
     }
    
    1331 1336
     
    
    1332 1337
     void postHeapProfSampleCostCentre(CostCentreStack *stack,
    
    1333 1338
                                       StgWord64 residency)
    
    1334 1339
     {
    
    1335
    -    ACQUIRE_LOCK(&eventBufMutex);
    
    1340
    +    ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
    
    1336 1341
         StgWord depth = 0;
    
    1337 1342
         CostCentreStack *ccs;
    
    1338 1343
         for (ccs = stack; ccs != NULL && ccs != CCS_MAIN; ccs = ccs->prevStack)
    
    ... ... @@ -1351,7 +1356,7 @@ void postHeapProfSampleCostCentre(CostCentreStack *stack,
    1351 1356
              depth>0 && ccs != NULL && ccs != CCS_MAIN;
    
    1352 1357
              ccs = ccs->prevStack, depth--)
    
    1353 1358
             postWord32(&eventBuf, ccs->cc->ccID);
    
    1354
    -    RELEASE_LOCK(&eventBufMutex);
    
    1359
    +    RELEASE_LOCK_ALWAYS(&eventBufMutex);
    
    1355 1360
     }
    
    1356 1361
     
    
    1357 1362
     
    
    ... ... @@ -1359,7 +1364,7 @@ void postProfSampleCostCentre(Capability *cap,
    1359 1364
                                   CostCentreStack *stack,
    
    1360 1365
                                   StgWord64 tick)
    
    1361 1366
     {
    
    1362
    -    ACQUIRE_LOCK(&eventBufMutex);
    
    1367
    +    ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
    
    1363 1368
         StgWord depth = 0;
    
    1364 1369
         CostCentreStack *ccs;
    
    1365 1370
         for (ccs = stack; ccs != NULL && ccs != CCS_MAIN; ccs = ccs->prevStack)
    
    ... ... @@ -1377,7 +1382,7 @@ void postProfSampleCostCentre(Capability *cap,
    1377 1382
              depth>0 && ccs != NULL && ccs != CCS_MAIN;
    
    1378 1383
              ccs = ccs->prevStack, depth--)
    
    1379 1384
             postWord32(&eventBuf, ccs->cc->ccID);
    
    1380
    -    RELEASE_LOCK(&eventBufMutex);
    
    1385
    +    RELEASE_LOCK_ALWAYS(&eventBufMutex);
    
    1381 1386
     }
    
    1382 1387
     
    
    1383 1388
     // This event is output at the start of profiling so the tick interval can
    
    ... ... @@ -1385,11 +1390,11 @@ void postProfSampleCostCentre(Capability *cap,
    1385 1390
     // can be calculated from how many samples there are.
    
    1386 1391
     void postProfBegin(void)
    
    1387 1392
     {
    
    1388
    -    ACQUIRE_LOCK(&eventBufMutex);
    
    1393
    +    ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
    
    1389 1394
         postEventHeader(&eventBuf, EVENT_PROF_BEGIN);
    
    1390 1395
         // The interval that each tick was sampled, in nanoseconds
    
    1391 1396
         postWord64(&eventBuf, TimeToNS(RtsFlags.MiscFlags.tickInterval));
    
    1392
    -    RELEASE_LOCK(&eventBufMutex);
    
    1397
    +    RELEASE_LOCK_ALWAYS(&eventBufMutex);
    
    1393 1398
     }
    
    1394 1399
     #endif /* PROFILING */
    
    1395 1400
     
    
    ... ... @@ -1415,11 +1420,11 @@ static void postTickyCounterDef(EventsBuf *eb, StgEntCounter *p)
    1415 1420
     
    
    1416 1421
     void postTickyCounterDefs(StgEntCounter *counters)
    
    1417 1422
     {
    
    1418
    -    ACQUIRE_LOCK(&eventBufMutex);
    
    1423
    +    ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
    
    1419 1424
         for (StgEntCounter *p = counters; p != NULL; p = p->link) {
    
    1420 1425
             postTickyCounterDef(&eventBuf, p);
    
    1421 1426
         }
    
    1422
    -    RELEASE_LOCK(&eventBufMutex);
    
    1427
    +    RELEASE_LOCK_ALWAYS(&eventBufMutex);
    
    1423 1428
     }
    
    1424 1429
     
    
    1425 1430
     static void postTickyCounterSample(EventsBuf *eb, StgEntCounter *p)
    
    ... ... @@ -1443,13 +1448,13 @@ static void postTickyCounterSample(EventsBuf *eb, StgEntCounter *p)
    1443 1448
     
    
    1444 1449
     void postTickyCounterSamples(StgEntCounter *counters)
    
    1445 1450
     {
    
    1446
    -    ACQUIRE_LOCK(&eventBufMutex);
    
    1451
    +    ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
    
    1447 1452
         ensureRoomForEvent(&eventBuf, EVENT_TICKY_COUNTER_SAMPLE);
    
    1448 1453
         postEventHeader(&eventBuf, EVENT_TICKY_COUNTER_BEGIN_SAMPLE);
    
    1449 1454
         for (StgEntCounter *p = counters; p != NULL; p = p->link) {
    
    1450 1455
             postTickyCounterSample(&eventBuf, p);
    
    1451 1456
         }
    
    1452
    -    RELEASE_LOCK(&eventBufMutex);
    
    1457
    +    RELEASE_LOCK_ALWAYS(&eventBufMutex);
    
    1453 1458
     }
    
    1454 1459
     #endif /* TICKY_TICKY */
    
    1455 1460
     void postIPE(const InfoProvEnt *ipe)
    
    ... ... @@ -1459,7 +1464,7 @@ void postIPE(const InfoProvEnt *ipe)
    1459 1464
     
    
    1460 1465
         // See Note [Maximum event length].
    
    1461 1466
         const StgWord MAX_IPE_STRING_LEN = 65535;
    
    1462
    -    ACQUIRE_LOCK(&eventBufMutex);
    
    1467
    +    ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
    
    1463 1468
         StgWord table_name_len = MIN(strlen(ipe->prov.table_name), MAX_IPE_STRING_LEN);
    
    1464 1469
         StgWord closure_desc_len = MIN(strlen(closure_desc_buf), MAX_IPE_STRING_LEN);
    
    1465 1470
         StgWord ty_desc_len = MIN(strlen(ipe->prov.ty_desc), MAX_IPE_STRING_LEN);
    
    ... ... @@ -1489,7 +1494,7 @@ void postIPE(const InfoProvEnt *ipe)
    1489 1494
         postBuf(&eventBuf, &colon, 1);
    
    1490 1495
         postStringLen(&eventBuf, ipe->prov.src_span, src_span_len);
    
    1491 1496
     
    
    1492
    -    RELEASE_LOCK(&eventBufMutex);
    
    1497
    +    RELEASE_LOCK_ALWAYS(&eventBufMutex);
    
    1493 1498
     }
    
    1494 1499
     
    
    1495 1500
     void printAndClearEventBuf (EventsBuf *ebuf)
    
    ... ... @@ -1601,14 +1606,21 @@ void flushLocalEventsBuf(Capability *cap)
    1601 1606
     // Flush all capabilities' event buffers when we already hold all capabilities.
    
    1602 1607
     // Used during forkProcess.
    
    1603 1608
     void flushAllCapsEventsBufs(void)
    
    1609
    +{
    
    1610
    +    ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
    
    1611
    +    flushAllCapsEventsBufs_();
    
    1612
    +    RELEASE_LOCK_ALWAYS(&eventBufMutex);
    
    1613
    +}
    
    1614
    +
    
    1615
    +// Unsafe version that does not acquire/release eventBufMutex. You must
    
    1616
    +// hold the eventBufMutex, which you must acquire with ACQUIRE_LOCK_ALWAYS!
    
    1617
    +void flushAllCapsEventsBufs_(void)
    
    1604 1618
     {
    
    1605 1619
         if (!event_log_writer) {
    
    1606 1620
             return;
    
    1607 1621
         }
    
    1608 1622
     
    
    1609
    -    ACQUIRE_LOCK(&eventBufMutex);
    
    1610 1623
         printAndClearEventBuf(&eventBuf);
    
    1611
    -    RELEASE_LOCK(&eventBufMutex);
    
    1612 1624
     
    
    1613 1625
         for (unsigned int i=0; i < getNumCapabilities(); i++) {
    
    1614 1626
             flushLocalEventsBuf(getCapability(i));
    
    ... ... @@ -1641,9 +1653,9 @@ static void flushEventLog_(Capability **cap USED_IF_THREADS)
    1641 1653
           return;
    
    1642 1654
         }
    
    1643 1655
     
    
    1644
    -    ACQUIRE_LOCK(&eventBufMutex);
    
    1656
    +    ACQUIRE_LOCK_ALWAYS(&eventBufMutex);
    
    1645 1657
         printAndClearEventBuf(&eventBuf);
    
    1646
    -    RELEASE_LOCK(&eventBufMutex);
    
    1658
    +    RELEASE_LOCK_ALWAYS(&eventBufMutex);
    
    1647 1659
     
    
    1648 1660
     #if defined(THREADED_RTS)
    
    1649 1661
         Task *task = newBoundTask();
    

  • rts/eventlog/EventLog.h
    ... ... @@ -18,6 +18,13 @@
    18 18
     #if defined(TRACING)
    
    19 19
     
    
    20 20
     extern bool eventlog_enabled;
    
    21
    +#if defined(HAVE_PREEMPTION)
    
    22
    +// Avoid using this mutex directly if at all possible. It is needed in the
    
    23
    +// implementation of forkProcess.
    
    24
    +//
    
    25
    +// All uses of this mutex must use ACQUIRE_LOCK_ALWAYS/RELEASE_LOCK_ALWAYS.
    
    26
    +extern Mutex eventBufMutex;
    
    27
    +#endif
    
    21 28
     
    
    22 29
     void initEventLogging(void);
    
    23 30
     void restartEventLogging(void);
    
    ... ... @@ -27,6 +34,7 @@ void abortEventLogging(void); // #4512 - after fork child needs to abort
    27 34
     void moreCapEventBufs (uint32_t from, uint32_t to);
    
    28 35
     void flushLocalEventsBuf(Capability *cap);
    
    29 36
     void flushAllCapsEventsBufs(void);
    
    37
    +void flushAllCapsEventsBufs_(void);
    
    30 38
     void flushAllEventsBufs(Capability *cap);
    
    31 39
     
    
    32 40
     typedef void (*EventlogInitPost)(void);
    

  • rts/include/rts/OSThreads.h
    ... ... @@ -14,6 +14,46 @@
    14 14
     
    
    15 15
     #pragma once
    
    16 16
     
    
    17
    +/* Note [Threads and preemption]
    
    18
    +   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    19
    +   All full-fat OSs that GHC works on have OS threads, and we use them even in
    
    20
    +   the non-threaded RTS for a few features:
    
    21
    +    * Haskell thread preemption;
    
    22
    +    * sample-based profiling;
    
    23
    +    * idle GC;
    
    24
    +    * periodic eventlog flushing.
    
    25
    +
    
    26
    +   We use defined(HAVE_PREEMPTION) to decide if these features are implemented
    
    27
    +   via OS threads.
    
    28
    +
    
    29
    +   On platforms like WASM/js we do not have OS threads in any conventional
    
    30
    +   sense, and the features above are either not available or are implemented
    
    31
    +   differently. See Note [No timer on wasm32].
    
    32
    +
    
    33
    +   In future if GHC is ported to platforms like bare-metal micro-controllers,
    
    34
    +   RTOSs or to run directly under hypervisors then such platforms may also not
    
    35
    +   have threads available and they should not define HAVE_PREEMPTION here. Or
    
    36
    +   for some micro-controller RTOSs like Zeypher one may have a choice about
    
    37
    +   whether to use threads or not (at a size cost). Here would be the right
    
    38
    +   place to control whether the feature list above is supported.
    
    39
    + */
    
    40
    +#if defined(wasm32_HOST_ARCH)
    
    41
    +  // See Note [No timer on wasm32]
    
    42
    +  // To confuse matters, WASM _does_ have pthread.h but it doesnt work.
    
    43
    +#elif defined(HAVE_PTHREAD_H) || defined(HAVE_WINDOWS_H)
    
    44
    +#define HAVE_PREEMPTION
    
    45
    +#else
    
    46
    +#error Decide if this platform has threads and pre-emption or not.
    
    47
    +#endif
    
    48
    +// And JS does all of this differently, without using this bit of the RTS.
    
    49
    +
    
    50
    +// Configuration sanity check
    
    51
    +#if defined(THREADED_RTS) && !defined(HAVE_PREEMPTION)
    
    52
    +//TODO we would like to be able to assert this:
    
    53
    +// #error Configuration error: THREADED_RTS should imply HAVE_PREEMPTION
    
    54
    +// however at the moment we cannot due to issue #27346.
    
    55
    +#endif
    
    56
    +
    
    17 57
     #if defined(HAVE_PTHREAD_H) && !defined(mingw32_HOST_OS)
    
    18 58
     
    
    19 59
     #if defined(CMINUSMINUS)
    
    ... ... @@ -210,9 +250,29 @@ extern bool timedWaitCondition ( Condition* pCond, Mutex* pMut, Time timeout)
    210 250
     //
    
    211 251
     // Mutexes
    
    212 252
     //
    
    253
    +// Even in the non-threaded RTS we use threads and mutexes! In particular the
    
    254
    +// timer/ticker is implemented using a thread. And using threads needs locks.
    
    255
    +// In particular we need locks for the data shared between the timer/ticker
    
    256
    +// thread and the thread running the main capability.
    
    257
    +#if defined(HAVE_PREEMPTION)
    
    213 258
     extern void initMutex             ( Mutex* pMut );
    
    214 259
     extern void closeMutex            ( Mutex* pMut );
    
    215 260
     
    
    261
    +// The "always" variants do locking in the threaded and non-threaded RTS.
    
    262
    +// The normal variants below are no-ops in the non-threaded RTS.
    
    263
    +#define ACQUIRE_LOCK_ALWAYS(l) OS_ACQUIRE_LOCK(l)
    
    264
    +#define TRY_ACQUIRE_LOCK_ALWAYS(l) OS_TRY_ACQUIRE_LOCK(l)
    
    265
    +#define RELEASE_LOCK_ALWAYS(l) OS_RELEASE_LOCK(l)
    
    266
    +#define ASSERT_LOCK_HELD_ALWAYS(l) OS_ASSERT_LOCK_HELD(l)
    
    267
    +#else
    
    268
    +// And just to be a bit confusing, the always variants are still no-ops when we
    
    269
    +// do not HAVE_PREEMPTION, since then we don't have threads or mutexes at all.
    
    270
    +#define ACQUIRE_LOCK_ALWAYS(l)
    
    271
    +#define TRY_ACQUIRE_LOCK_ALWAYS(l) 0
    
    272
    +#define RELEASE_LOCK_ALWAYS(l)
    
    273
    +#define ASSERT_LOCK_HELD_ALWAYS(l)
    
    274
    +#endif
    
    275
    +
    
    216 276
     // Processors and affinity
    
    217 277
     void setThreadAffinity (uint32_t n, uint32_t m);
    
    218 278
     void setThreadNode (uint32_t node);
    
    ... ... @@ -228,6 +288,7 @@ void releaseThreadNode (void);
    228 288
     
    
    229 289
     #else
    
    230 290
     
    
    291
    +// No-ops in the non-threaded RTS. See also the _ALWAYS variants above.
    
    231 292
     #define ACQUIRE_LOCK(l)
    
    232 293
     #define TRY_ACQUIRE_LOCK(l) 0
    
    233 294
     #define RELEASE_LOCK(l)
    

  • testsuite/tests/codeGen/should_run/T27046.hs
    1
    +{-# LANGUAGE MagicHash #-}
    
    2
    +{-# LANGUAGE ForeignFunctionInterface, GHCForeignImportPrim, UnliftedFFITypes #-}
    
    3
    +
    
    4
    +module Main where
    
    5
    +
    
    6
    +import Control.Monad
    
    7
    +  ( unless )
    
    8
    +import Data.Bits
    
    9
    +  ( shiftL )
    
    10
    +import GHC.Exts
    
    11
    +  ( Int64# )
    
    12
    +import GHC.Int
    
    13
    +  ( Int64(..) )
    
    14
    +
    
    15
    +foreign import prim "test_mul2_clobber"
    
    16
    +  test_mul2_clobber :: Int64# -> Int64# -> Int64#
    
    17
    +
    
    18
    +main :: IO ()
    
    19
    +main = do
    
    20
    +  let
    
    21
    +    I64# x = 1 `shiftL` 32
    
    22
    +    hi = I64# $ test_mul2_clobber x x
    
    23
    +
    
    24
    +  unless ( hi == 1 ) $
    
    25
    +    error $ unlines
    
    26
    +      [ "Incorrect result for Mul2 operation."
    
    27
    +      , "Expected high word: 1"
    
    28
    +      , "  Actual high word: " ++ show hi
    
    29
    +      ]

  • testsuite/tests/codeGen/should_run/T27046_cmm.cmm
    1
    +#include "Cmm.h"
    
    2
    +
    
    3
    +// Test for #27046
    
    4
    +test_mul2_clobber (bits64 x, bits64 y)
    
    5
    +{
    
    6
    +    bits64 hi, nd;
    
    7
    +
    
    8
    +    // Deliberately alias the destination 'lo' with the source 'x'
    
    9
    +    // This forces the NCG to use the same virtual register for both.
    
    10
    +    (nd, hi, x) = prim %mul2_64(x, y);
    
    11
    +
    
    12
    +    return (hi);
    
    13
    +}

  • testsuite/tests/codeGen/should_run/all.T
    ... ... @@ -260,6 +260,13 @@ test('T25364', normal, compile_and_run, [''])
    260 260
     test('T26061', normal, compile_and_run, [''])
    
    261 261
     test('T26537', normal, compile_and_run, ['-O2 -fregs-graph'])
    
    262 262
     test('T24016', normal, compile_and_run, ['-O1 -fPIC'])
    
    263
    +test('T27046',
    
    264
    +     [ req_cmm
    
    265
    +     , when(arch('i386'), skip) # i386 does not support MO_S_Mul2 W64
    
    266
    +     , when(arch('wasm32'), skip)
    
    267
    +     , js_skip
    
    268
    +     , when(unregisterised(), skip) # pprCallishMachOp_for_C: MO_S_Mul2 W64 not supported
    
    269
    +     ], compile_and_run, ['T27046_cmm.cmm'])
    
    263 270
     
    
    264 271
     # Check that GHC-generated finalizers run on Darwin. The Apple linker doesn't
    
    265 272
     # support --wrap, so we can't intercept hs_spt_remove directly.  Instead we
    

  • testsuite/tests/rts/T27131.hs
    ... ... @@ -30,16 +30,22 @@ foreign import ccall unsafe "has_local_stop_after_return"
    30 30
     main :: IO ()
    
    31 31
     main = do
    
    32 32
       setNumCapabilities 2
    
    33
    -  checkFlag
    
    34
    -    "TSO_STOP_NEXT_BREAKPOINT"
    
    35
    -    rts_enableStopNextBreakpoint
    
    36
    -    rts_disableStopNextBreakpoint
    
    37
    -    c_hasLocalStopNextBreakpoint
    
    38
    -  checkFlag
    
    39
    -    "TSO_STOP_AFTER_RETURN"
    
    40
    -    rts_enableStopAfterReturn
    
    41
    -    rts_disableStopAfterReturn
    
    42
    -    c_hasLocalStopAfterReturn
    
    33
    +  -- Bind to capability 0 so it can't float between capabilities while the
    
    34
    +  -- target thread runs on capability 1.
    
    35
    +  doneVar <- newEmptyMVar
    
    36
    +  _ <- forkOn 0 $ do
    
    37
    +    checkFlag
    
    38
    +      "TSO_STOP_NEXT_BREAKPOINT"
    
    39
    +      rts_enableStopNextBreakpoint
    
    40
    +      rts_disableStopNextBreakpoint
    
    41
    +      c_hasLocalStopNextBreakpoint
    
    42
    +    checkFlag
    
    43
    +      "TSO_STOP_AFTER_RETURN"
    
    44
    +      rts_enableStopAfterReturn
    
    45
    +      rts_disableStopAfterReturn
    
    46
    +      c_hasLocalStopAfterReturn
    
    47
    +    putMVar doneVar ()
    
    48
    +  takeMVar doneVar
    
    43 49
     
    
    44 50
     checkFlag
    
    45 51
       :: String
    
    ... ... @@ -58,6 +64,7 @@ checkFlag label enable disable isMyThreadFlagSet = do
    58 64
       ThreadId tid# <- forkOn 1 $ do
    
    59 65
         replicateM_ 2 $ do
    
    60 66
           replyVar <- takeMVar targetCheckVar
    
    67
    +      yield -- make sure we reprocess the mailbox
    
    61 68
           isSet <- (/= 0) <$> isMyThreadFlagSet
    
    62 69
           putMVar replyVar isSet
    
    63 70
     
    

  • testsuite/tests/rts/T27131.stdout
    1
    -(0,False)
    
    1
    +(0,True)
    
    2 2
     TSO_STOP_NEXT_BREAKPOINT set: ok
    
    3 3
     TSO_STOP_NEXT_BREAKPOINT unset: ok
    
    4
    -(0,False)
    
    4
    +(0,True)
    
    5 5
     TSO_STOP_AFTER_RETURN set: ok
    
    6 6
     TSO_STOP_AFTER_RETURN unset: ok

  • utils/haddock/haddock-api/haddock-api.cabal
    ... ... @@ -97,6 +97,7 @@ library
    97 97
                    , filepath
    
    98 98
                    , ghc-boot
    
    99 99
                    , mtl
    
    100
    +               , semaphore-compat
    
    100 101
                    , transformers
    
    101 102
                    , text
    
    102 103
     
    

  • utils/haddock/haddock-api/src/Haddock.hs
    ... ... @@ -29,6 +29,7 @@ module Haddock (
    29 29
       withGhc
    
    30 30
     ) where
    
    31 31
     
    
    32
    +import Control.Concurrent.MVar (modifyMVar, modifyMVar_, newMVar)
    
    32 33
     import Control.DeepSeq (force)
    
    33 34
     import Control.Monad hiding (forM_)
    
    34 35
     import Control.Monad.IO.Class (MonadIO(..))
    
    ... ... @@ -41,6 +42,7 @@ import Data.Maybe
    41 42
     import Data.IORef
    
    42 43
     import Data.Map.Strict (Map)
    
    43 44
     import Data.Version (makeVersion)
    
    45
    +import GHC.Conc (getNumProcessors)
    
    44 46
     import GHC.Parser.Lexer (ParserOpts)
    
    45 47
     import qualified GHC.Driver.Config.Parser as Parser
    
    46 48
     import qualified Data.Map.Strict as Map
    
    ... ... @@ -84,11 +86,55 @@ import Haddock.Options
    84 86
     import Haddock.Utils
    
    85 87
     import Haddock.GhcUtils (modifySessionDynFlags, setOutputDir)
    
    86 88
     import Haddock.Compat (getProcessID)
    
    89
    +import System.Semaphore (AbstractSem(..), openSemaphore, releaseSemaphoreToken, waitOnSemaphore)
    
    87 90
     
    
    88 91
     --------------------------------------------------------------------------------
    
    89 92
     -- * Exception handling
    
    90 93
     --------------------------------------------------------------------------------
    
    91 94
     
    
    95
    +concSemChoiceFromFlags :: [Flag] -> Maybe (Either FilePath (Maybe Int))
    
    96
    +concSemChoiceFromFlags =
    
    97
    +  List.foldl' step Nothing
    
    98
    +  where
    
    99
    +    step _ (Flag_ParCount n) = Just (Right n)
    
    100
    +    step _ (Flag_ParSemaphore sem) = Just (Left sem)
    
    101
    +    step acc _ = acc
    
    102
    +
    
    103
    +-- | Build the render concurrency semaphore selected by Haddock's parallelism flags.
    
    104
    +-- Without an explicit flag, render sequentially; @-j@ uses the host processor
    
    105
    +-- count, @-jN@ uses a local bounded semaphore, and @-jsem@ joins the external
    
    106
    +-- semaphore used for GHC jobserver coordination.
    
    107
    +concSemFromChoice :: Maybe (Either FilePath (Maybe Int)) -> IO AbstractSem
    
    108
    +concSemFromChoice choice =
    
    109
    +  case choice of
    
    110
    +    Nothing -> newBoundedSem 1
    
    111
    +    Just (Right Nothing) -> newBoundedSem =<< getNumProcessors
    
    112
    +    Just (Right (Just n)) -> newBoundedSem n
    
    113
    +    Just (Left semName) -> do
    
    114
    +      openSemaphore semName >>= \case
    
    115
    +        Left err -> throwIO err
    
    116
    +        Right sem -> do
    
    117
    +          tokens <- newMVar []
    
    118
    +          pure
    
    119
    +            AbstractSem
    
    120
    +              { acquireSem = mask $ \restore -> do
    
    121
    +                  token <- restore (waitOnSemaphore sem)
    
    122
    +                  modifyMVar_ tokens $ \held -> pure (token : held)
    
    123
    +              , releaseSem = mask_ $ do
    
    124
    +                  token <- modifyMVar tokens $ \case
    
    125
    +                    [] -> pure ([], Nothing)
    
    126
    +                    heldToken : heldTokens -> pure (heldTokens, Just heldToken)
    
    127
    +                  forM_ token releaseSemaphoreToken
    
    128
    +              }
    
    129
    +
    
    130
    +injectParFlags :: Maybe (Either FilePath (Maybe Int)) -> [Flag] -> [Flag]
    
    131
    +injectParFlags choice flags =
    
    132
    +  case choice of
    
    133
    +    Nothing -> flags
    
    134
    +    Just (Right Nothing) -> Flag_OptGhc "-j" : flags
    
    135
    +    Just (Right (Just n)) -> Flag_OptGhc ("-j" ++ show n) : flags
    
    136
    +    Just (Left sem) -> Flag_OptGhc "-jsem" : Flag_OptGhc sem : flags
    
    137
    +
    
    92 138
     
    
    93 139
     handleTopExceptions :: IO a -> IO a
    
    94 140
     handleTopExceptions =
    
    ... ... @@ -177,11 +223,12 @@ haddockWithGhc ghc args = handleTopExceptions $ do
    177 223
               Just "YES" | not noCompilation -> return $ Flag_OptGhc "-dynamic-too" : flags
    
    178 224
               _ -> return flags
    
    179 225
     
    
    180
    -  -- Inject `-j` into ghc options, if given to Haddock
    
    181
    -  flags' <- pure $ case optParCount flags'' of
    
    182
    -    Nothing       -> flags''
    
    183
    -    Just Nothing  -> Flag_OptGhc "-j" : flags''
    
    184
    -    Just (Just n) -> Flag_OptGhc ("-j" ++ show n) : flags''
    
    226
    +  let parChoice = concSemChoiceFromFlags flags''
    
    227
    +
    
    228
    +  -- Inject parallelism flags into ghc options, if given to Haddock
    
    229
    +  flags' <- pure $ injectParFlags parChoice flags''
    
    230
    +
    
    231
    +  concSem <- concSemFromChoice parChoice
    
    185 232
     
    
    186 233
       -- Whether or not to bypass the interface version check
    
    187 234
       let noChecks = Flag_BypassInterfaceVersonCheck `elem` flags
    
    ... ... @@ -238,7 +285,7 @@ haddockWithGhc ghc args = handleTopExceptions $ do
    238 285
               }
    
    239 286
     
    
    240 287
           -- Render the interfaces.
    
    241
    -      liftIO $ renderStep dflags parserOpts logger unit_state flags sinceQual qual packages ifaces
    
    288
    +      liftIO $ renderStep dflags parserOpts logger unit_state flags sinceQual qual concSem packages ifaces
    
    242 289
     
    
    243 290
         -- If we were not given any input files, error if documentation was
    
    244 291
         -- requested
    
    ... ... @@ -251,7 +298,7 @@ haddockWithGhc ghc args = handleTopExceptions $ do
    251 298
           packages <- liftIO $ readInterfaceFiles name_cache (readIfaceArgs flags) noChecks
    
    252 299
     
    
    253 300
           -- Render even though there are no input files (usually contents/index).
    
    254
    -      liftIO $ renderStep dflags parserOpts logger unit_state flags sinceQual qual packages []
    
    301
    +      liftIO $ renderStep dflags parserOpts logger unit_state flags sinceQual qual concSem packages []
    
    255 302
     
    
    256 303
     -- | Run the GHC action using a temporary output directory
    
    257 304
     withTempOutputDir :: Ghc a -> Ghc a
    
    ... ... @@ -311,10 +358,11 @@ renderStep
    311 358
       -> [Flag]
    
    312 359
       -> SinceQual
    
    313 360
       -> QualOption
    
    361
    +  -> AbstractSem
    
    314 362
       -> [(DocPaths, Visibility, FilePath, InterfaceFile)]
    
    315 363
       -> [Interface]
    
    316 364
       -> IO ()
    
    317
    -renderStep dflags parserOpts logger unit_state flags sinceQual nameQual pkgs interfaces = do
    
    365
    +renderStep dflags parserOpts logger unit_state flags sinceQual nameQual concSem pkgs interfaces = do
    
    318 366
       updateHTMLXRefs (map (\(docPath, _ifaceFilePath, _showModules, ifaceFile) ->
    
    319 367
                               ( case baseUrl flags of
    
    320 368
                                   Nothing  -> docPathsHtml docPath
    
    ... ... @@ -330,7 +378,7 @@ renderStep dflags parserOpts logger unit_state flags sinceQual nameQual pkgs int
    330 378
           (DocPaths {docPathsSources=Just path}, _, _, ifile) <- pkgs
    
    331 379
           iface <- ifInstalledIfaces ifile
    
    332 380
           return (instMod iface, path)
    
    333
    -  render dflags parserOpts logger unit_state flags sinceQual nameQual interfaces installedIfaces extSrcMap
    
    381
    +  render dflags parserOpts logger unit_state flags sinceQual nameQual concSem interfaces installedIfaces extSrcMap
    
    334 382
       where
    
    335 383
         -- get package name from unit-id
    
    336 384
         packageName :: Unit -> String
    
    ... ... @@ -348,11 +396,12 @@ render
    348 396
       -> [Flag]
    
    349 397
       -> SinceQual
    
    350 398
       -> QualOption
    
    399
    +  -> AbstractSem
    
    351 400
       -> [Interface]
    
    352 401
       -> [(FilePath, PackageInterfaces)]
    
    353 402
       -> Map Module FilePath
    
    354 403
       -> IO ()
    
    355
    -render dflags parserOpts logger unit_state flags sinceQual qual ifaces packages extSrcMap = do
    
    404
    +render dflags parserOpts logger unit_state flags sinceQual qual concSem ifaces packages extSrcMap = do
    
    356 405
       let
    
    357 406
         packageInfo = PackageInfo { piPackageName    = fromMaybe (PackageName mempty)
    
    358 407
                                                      $ optPackageName flags
    
    ... ... @@ -516,7 +565,7 @@ render dflags parserOpts logger unit_state flags sinceQual qual ifaces packages
    516 565
                       prologue
    
    517 566
                       themes opt_mathjax sourceUrls' opt_wiki_urls opt_base_url
    
    518 567
                       opt_contents_url opt_index_url unicode sincePkg packageInfo
    
    519
    -                  qual pretty withQuickjump
    
    568
    +                  qual pretty concSem withQuickjump
    
    520 569
           return ()
    
    521 570
         unless (withBaseURL || isJust (optOneShot flags)) $ do
    
    522 571
           copyHtmlBits odir libDir themes withQuickjump
    
    ... ... @@ -555,7 +604,7 @@ render dflags parserOpts logger unit_state flags sinceQual qual ifaces packages
    555 604
       when (Flag_HyperlinkedSource `elem` flags && not (null ifaces)) $ do
    
    556 605
         withTiming logger "ppHyperlinkedSource" (const ()) $ do
    
    557 606
           _ <- {-# SCC ppHyperlinkedSource #-}
    
    558
    -           ppHyperlinkedSource (verbosity flags) (isJust (optOneShot flags)) odir libDir opt_source_css pretty srcMap ifaces
    
    607
    +           ppHyperlinkedSource (verbosity flags) (isJust (optOneShot flags)) odir libDir opt_source_css pretty concSem srcMap ifaces
    
    559 608
           return ()
    
    560 609
     
    
    561 610
     
    
    ... ... @@ -842,4 +891,3 @@ getPrologue parserOpts flags =
    842 891
     rightOrThrowE :: Either String b -> IO b
    
    843 892
     rightOrThrowE (Left msg) = throwE msg
    
    844 893
     rightOrThrowE (Right x) = pure x
    845
    -

  • utils/haddock/haddock-api/src/Haddock/Backends/Hyperlinker.hs
    ... ... @@ -31,7 +31,8 @@ import Haddock.Backends.Hyperlinker.Utils
    31 31
     import Haddock.Backends.Xhtml.Utils (renderToBuilder)
    
    32 32
     import Haddock.InterfaceFile
    
    33 33
     import Haddock.Types
    
    34
    -import Haddock.Utils (Verbosity, out, verbose)
    
    34
    +import Haddock.Utils (Verbosity, out, verbose, mapConcurrentlyWith_)
    
    35
    +import System.Semaphore (AbstractSem)
    
    35 36
     import qualified Data.ByteString.Builder as Builder
    
    36 37
     
    
    37 38
     -- | Generate hyperlinked source for given interfaces.
    
    ... ... @@ -51,19 +52,21 @@ ppHyperlinkedSource
    51 52
       -- ^ Custom CSS file path
    
    52 53
       -> Bool
    
    53 54
       -- ^ Flag indicating whether to pretty-print HTML
    
    55
    +  -> AbstractSem
    
    56
    +  -- ^ Concurrency semaphore for module renders
    
    54 57
       -> M.Map Module SrcPath
    
    55 58
       -- ^ Paths to sources
    
    56 59
       -> [Interface]
    
    57 60
       -- ^ Interfaces for which we create source
    
    58 61
       -> IO ()
    
    59
    -ppHyperlinkedSource verbosity isOneShot outdir libdir mstyle pretty srcs' ifaces = do
    
    62
    +ppHyperlinkedSource verbosity isOneShot outdir libdir mstyle pretty concSem srcs' ifaces = do
    
    60 63
       createDirectoryIfMissing True srcdir
    
    61 64
       unless isOneShot $ do
    
    62 65
         let cssFile = fromMaybe (defaultCssFile libdir) mstyle
    
    63 66
         copyFile cssFile $ srcdir </> srcCssFile
    
    64 67
         copyFile (libdir </> "html" </> highlightScript) $
    
    65 68
           srcdir </> highlightScript
    
    66
    -  mapM_ (ppHyperlinkedModuleSource verbosity srcdir pretty srcs) ifaces
    
    69
    +  mapConcurrentlyWith_ concSem (ppHyperlinkedModuleSource verbosity srcdir pretty srcs) ifaces
    
    67 70
       where
    
    68 71
         srcdir = outdir </> hypSrcDir
    
    69 72
         srcs = (srcs', M.mapKeys moduleName srcs')
    

  • utils/haddock/haddock-api/src/Haddock/Backends/Xhtml.hs
    ... ... @@ -69,6 +69,7 @@ import Haddock.ModuleTree
    69 69
     import Haddock.Options (Visibility (..))
    
    70 70
     import Haddock.Types
    
    71 71
     import Haddock.Utils
    
    72
    +import System.Semaphore (AbstractSem)
    
    72 73
     import Haddock.Utils.Json
    
    73 74
     import Haddock.Version
    
    74 75
     
    
    ... ... @@ -115,6 +116,8 @@ ppHtml
    115 116
       -- ^ How to qualify names
    
    116 117
       -> Bool
    
    117 118
       -- ^ Output pretty html (newlines and indenting)
    
    119
    +  -> AbstractSem
    
    120
    +  -- ^ Concurrency semaphore for module renders
    
    118 121
       -> Bool
    
    119 122
       -- ^ Also write Quickjump index
    
    120 123
       -> IO ()
    
    ... ... @@ -138,6 +141,7 @@ ppHtml
    138 141
       packageInfo
    
    139 142
       qual
    
    140 143
       debug
    
    144
    +  concSem
    
    141 145
       withQuickjump = do
    
    142 146
         let
    
    143 147
           visible_ifaces = filter visible ifaces
    
    ... ... @@ -192,7 +196,7 @@ ppHtml
    192 196
             visible_ifaces
    
    193 197
             []
    
    194 198
     
    
    195
    -    mapM_
    
    199
    +    mapConcurrentlyWith_ concSem
    
    196 200
           ( ppHtmlModule
    
    197 201
               odir
    
    198 202
               doctitle
    

  • utils/haddock/haddock-api/src/Haddock/Options.hs
    ... ... @@ -29,6 +29,7 @@ module Haddock.Options
    29 29
       , wikiUrls
    
    30 30
       , baseUrl
    
    31 31
       , optParCount
    
    32
    +  , optParSemaphore
    
    32 33
       , optDumpInterfaceFile
    
    33 34
       , optShowInterfaceFile
    
    34 35
       , optLaTeXStyle
    
    ... ... @@ -48,7 +49,7 @@ module Haddock.Options
    48 49
     
    
    49 50
     import Control.Applicative
    
    50 51
     import qualified Data.Char as Char
    
    51
    -import Data.List (dropWhileEnd)
    
    52
    +import Data.List (dropWhileEnd, isPrefixOf)
    
    52 53
     import Data.Map (Map)
    
    53 54
     import qualified Data.Map as Map
    
    54 55
     import Data.Set (Set)
    
    ... ... @@ -122,6 +123,7 @@ data Flag
    122 123
       | Flag_SinceQualification String
    
    123 124
       | Flag_IgnoreLinkSymbol String
    
    124 125
       | Flag_ParCount (Maybe Int)
    
    126
    +  | Flag_ParSemaphore FilePath
    
    125 127
       | Flag_TraceArgs
    
    126 128
       | Flag_OneShot String
    
    127 129
       | Flag_NoCompilation
    
    ... ... @@ -406,6 +408,11 @@ options backwardsCompat =
    406 408
           []
    
    407 409
           (OptArg (\count -> Flag_ParCount (fmap read count)) "n")
    
    408 410
           "load modules in parallel"
    
    411
    +  , Option
    
    412
    +      []
    
    413
    +      ["jsem"]
    
    414
    +      (ReqArg Flag_ParSemaphore "SEM")
    
    415
    +      "use semaphore SEM to limit parallelism"
    
    409 416
       , Option
    
    410 417
           []
    
    411 418
           ["trace-args"]
    
    ... ... @@ -423,7 +430,7 @@ getUsage = do
    423 430
     
    
    424 431
     parseHaddockOpts :: [String] -> IO ([Flag], [String])
    
    425 432
     parseHaddockOpts params =
    
    426
    -  case getOpt Permute (options True) params of
    
    433
    +  case getOpt Permute (options True) (normalizeJsemArgs params) of
    
    427 434
         (flags, args, []) -> return (flags, args)
    
    428 435
         (_, _, errors) -> do
    
    429 436
           usage <- getUsage
    
    ... ... @@ -498,6 +505,18 @@ optMathjax flags = optLast [str | Flag_Mathjax str <- flags]
    498 505
     optParCount :: [Flag] -> Maybe (Maybe Int)
    
    499 506
     optParCount flags = optLast [n | Flag_ParCount n <- flags]
    
    500 507
     
    
    508
    +optParSemaphore :: [Flag] -> Maybe FilePath
    
    509
    +optParSemaphore flags = optLast [s | Flag_ParSemaphore s <- flags]
    
    510
    +
    
    511
    +normalizeJsemArgs :: [String] -> [String]
    
    512
    +normalizeJsemArgs = map rewrite
    
    513
    +  where
    
    514
    +    rewrite arg
    
    515
    +      | arg == "-jsem" = "--jsem"
    
    516
    +      | "-jsem=" `isPrefixOf` arg = "--jsem=" ++ drop 6 arg
    
    517
    +      | "-jsem" `isPrefixOf` arg = "--jsem=" ++ drop 5 arg
    
    518
    +      | otherwise = arg
    
    519
    +
    
    501 520
     qualification :: [Flag] -> Either String QualOption
    
    502 521
     qualification flags =
    
    503 522
       case map (map Char.toLower) [str | Flag_Qualification str <- flags] of
    

  • utils/haddock/haddock-api/src/Haddock/Utils.hs
    ... ... @@ -54,6 +54,10 @@ module Haddock.Utils
    54 54
       , replace
    
    55 55
       , spanWith
    
    56 56
     
    
    57
    +    -- * Concurrency utilities
    
    58
    +  , mapConcurrentlyWith_
    
    59
    +  , newBoundedSem
    
    60
    +
    
    57 61
         -- * Logging
    
    58 62
       , parseVerbosity
    
    59 63
       , Verbosity (..)
    
    ... ... @@ -86,6 +90,13 @@ import Haddock.Types
    86 90
     import Data.Text.Lazy (Text)
    
    87 91
     import qualified Data.Text.Lazy as LText
    
    88 92
     
    
    93
    +import Control.Concurrent (forkFinally)
    
    94
    +import Control.Concurrent.QSem (newQSem, signalQSem, waitQSem)
    
    95
    +import Control.Concurrent.MVar (newEmptyMVar, putMVar, takeMVar)
    
    96
    +import Control.Exception (throwIO)
    
    97
    +import Control.Monad (void)
    
    98
    +import System.Semaphore (AbstractSem (..))
    
    99
    +
    
    89 100
     --------------------------------------------------------------------------------
    
    90 101
     
    
    91 102
     -- * Logging
    
    ... ... @@ -334,6 +345,43 @@ html_xrefs = unsafePerformIO (readIORef html_xrefs_ref)
    334 345
     html_xrefs' :: Map ModuleName FilePath
    
    335 346
     html_xrefs' = unsafePerformIO (readIORef html_xrefs_ref')
    
    336 347
     
    
    348
    +-- * Concurrency utilities
    
    349
    +
    
    350
    +--------------------------------------------------------------------------------
    
    351
    +
    
    352
    +mapConcurrentlyWith_ :: AbstractSem -> (a -> IO ()) -> [a] -> IO ()
    
    353
    +mapConcurrentlyWith_ _ _ [] = return ()
    
    354
    +mapConcurrentlyWith_ concSem f xs = do
    
    355
    +  -- Create MVars to wait for completion and collect results
    
    356
    +  resultMVars <- mapM (const newEmptyMVar) xs
    
    357
    +
    
    358
    +  -- Fork a thread for each element
    
    359
    +  mapM_ (forkThread concSem) (zip xs resultMVars)
    
    360
    +
    
    361
    +  -- Wait for all threads and collect any errors
    
    362
    +  results <- mapM takeMVar resultMVars
    
    363
    +
    
    364
    +  -- Re-throw the first exception if any
    
    365
    +  case [err | Left err <- results] of
    
    366
    +    (err:_) -> throwIO err
    
    367
    +    [] -> return ()
    
    368
    +  where
    
    369
    +    forkThread concSem' (x, resultMVar) = do
    
    370
    +      acquireSem concSem'
    
    371
    +      void $ forkFinally (f x) $ \res -> do
    
    372
    +        releaseSem concSem'
    
    373
    +        putMVar resultMVar res
    
    374
    +
    
    375
    +newBoundedSem :: Int -> IO AbstractSem
    
    376
    +newBoundedSem maxThreads = do
    
    377
    +  sem <- newQSem (max 1 maxThreads)
    
    378
    +  pure
    
    379
    +    AbstractSem
    
    380
    +      { acquireSem = waitQSem sem
    
    381
    +      , releaseSem = signalQSem sem
    
    382
    +      }
    
    383
    +
    
    384
    +
    
    337 385
     -----------------------------------------------------------------------------
    
    338 386
     
    
    339 387
     -- * List utils