diff options
author | Ben Gamari <ben@smart-cactus.org> | 2022-11-10 18:29:45 -0500 |
---|---|---|
committer | Marge Bot <ben+marge-bot@smart-cactus.org> | 2022-12-16 16:12:45 -0500 |
commit | 86f20258ab7dbfb56e323ee811e9eaef80b077d3 (patch) | |
tree | 48ffebf1bd6adea439e3197cfa3a9d14eabacc2b | |
parent | 605d954722a314c0da59ea07efc26d8a7cb59296 (diff) | |
download | haskell-86f20258ab7dbfb56e323ee811e9eaef80b077d3.tar.gz |
rts/Timer: Always use atomic operations
As noted in #22447, the existence of the pthread-based ITimer
implementation means that we cannot assume that the program is
single-threaded.
-rw-r--r-- | rts/Proftimer.c | 23 | ||||
-rw-r--r-- | rts/Timer.c | 16 | ||||
-rw-r--r-- | rts/include/stg/SMP.h | 19 |
3 files changed, 41 insertions, 17 deletions
diff --git a/rts/Proftimer.c b/rts/Proftimer.c index 6717731a6b..59758f8540 100644 --- a/rts/Proftimer.c +++ b/rts/Proftimer.c @@ -14,6 +14,11 @@ #include "Capability.h" #include "Trace.h" +/* + * N.B. These flags must all always be accessed via atomics since even in the + * non-threaded runtime the timer may be provided by way of a signal. + */ + #if defined(PROFILING) static bool do_prof_ticks = false; // enable profiling ticks #endif @@ -41,7 +46,7 @@ void stopProfTimer( void ) { #if defined(PROFILING) - RELAXED_STORE(&do_prof_ticks, false); + RELAXED_STORE_ALWAYS(&do_prof_ticks, false); #endif } @@ -49,7 +54,7 @@ void startProfTimer( void ) { #if defined(PROFILING) - RELAXED_STORE(&do_prof_ticks, true); + RELAXED_STORE_ALWAYS(&do_prof_ticks, true); #endif } @@ -57,7 +62,7 @@ void stopHeapProfTimer( void ) { if (RtsFlags.ProfFlags.doHeapProfile){ - RELAXED_STORE(&heap_prof_timer_active, false); + RELAXED_STORE_ALWAYS(&heap_prof_timer_active, false); pauseHeapProfTimer(); } } @@ -66,14 +71,14 @@ void startHeapProfTimer( void ) { if (RtsFlags.ProfFlags.doHeapProfile){ - RELAXED_STORE(&heap_prof_timer_active, true); + RELAXED_STORE_ALWAYS(&heap_prof_timer_active, true); resumeHeapProfTimer(); } } void pauseHeapProfTimer ( void ) { - RELAXED_STORE(&do_heap_prof_ticks, false); + RELAXED_STORE_ALWAYS(&do_heap_prof_ticks, false); } @@ -81,7 +86,7 @@ void resumeHeapProfTimer ( void ) { if (RtsFlags.ProfFlags.doHeapProfile && RtsFlags.ProfFlags.heapProfileIntervalTicks > 0) { - RELAXED_STORE(&do_heap_prof_ticks, true); + RELAXED_STORE_ALWAYS(&do_heap_prof_ticks, true); } } @@ -89,7 +94,7 @@ void requestHeapCensus( void ){ // If no profiling mode is passed then just ignore the call. if (RtsFlags.ProfFlags.doHeapProfile){ - RELAXED_STORE(&performHeapProfile, true); + RELAXED_STORE_ALWAYS(&performHeapProfile, true); } } @@ -115,7 +120,7 @@ handleProfTick(void) { #if defined(PROFILING) total_ticks++; - if (RELAXED_LOAD(&do_prof_ticks)) { + if (RELAXED_LOAD_ALWAYS(&do_prof_ticks)) { uint32_t n; for (n=0; n < getNumCapabilities(); n++) { capabilities[n]->r.rCCCS->time_ticks++; @@ -134,7 +139,7 @@ handleProfTick(void) } #endif - if (RELAXED_LOAD(&do_heap_prof_ticks) && RELAXED_LOAD(&heap_prof_timer_active)) { + if (RELAXED_LOAD_ALWAYS(&do_heap_prof_ticks) && RELAXED_LOAD_ALWAYS(&heap_prof_timer_active)) { ticks_to_heap_profile--; if (ticks_to_heap_profile <= 0) { ticks_to_heap_profile = RtsFlags.ProfFlags.heapProfileIntervalTicks; diff --git a/rts/Timer.c b/rts/Timer.c index 332334129d..5961e5a27b 100644 --- a/rts/Timer.c +++ b/rts/Timer.c @@ -110,7 +110,7 @@ handle_tick(int unused STG_UNUSED) { handleProfTick(); if (RtsFlags.ConcFlags.ctxtSwitchTicks > 0 - && SEQ_CST_LOAD(&timer_disabled) == 0) + && SEQ_CST_LOAD_ALWAYS(&timer_disabled) == 0) { ticks_to_ctxt_switch--; if (ticks_to_ctxt_switch <= 0) { @@ -134,16 +134,16 @@ handle_tick(int unused STG_UNUSED) * for threads that are deadlocked. However, ensure we wait * at least interIdleGCWait (+RTS -Iw) between idle GCs. */ - switch (SEQ_CST_LOAD(&recent_activity)) { + switch (SEQ_CST_LOAD_ALWAYS(&recent_activity)) { case ACTIVITY_YES: - SEQ_CST_STORE(&recent_activity, ACTIVITY_MAYBE_NO); + SEQ_CST_STORE_ALWAYS(&recent_activity, ACTIVITY_MAYBE_NO); idle_ticks_to_gc = RtsFlags.GcFlags.idleGCDelayTime / RtsFlags.MiscFlags.tickInterval; break; case ACTIVITY_MAYBE_NO: if (idle_ticks_to_gc == 0 && inter_gc_ticks_to_gc == 0) { if (RtsFlags.GcFlags.doIdleGC) { - SEQ_CST_STORE(&recent_activity, ACTIVITY_INACTIVE); + SEQ_CST_STORE_ALWAYS(&recent_activity, ACTIVITY_INACTIVE); inter_gc_ticks_to_gc = RtsFlags.GcFlags.interIdleGCWait / RtsFlags.MiscFlags.tickInterval; #if defined(THREADED_RTS) @@ -152,7 +152,7 @@ handle_tick(int unused STG_UNUSED) // the GC. #endif } else { - SEQ_CST_STORE(&recent_activity, ACTIVITY_DONE_GC); + SEQ_CST_STORE_ALWAYS(&recent_activity, ACTIVITY_DONE_GC); // disable timer signals (see #1623, #5991, #9105) // but only if we're not profiling (e.g. passed -h or -p RTS // flags). If we are profiling we need to keep the timer active @@ -184,7 +184,7 @@ initTimer(void) if (RtsFlags.MiscFlags.tickInterval != 0) { initTicker(RtsFlags.MiscFlags.tickInterval, handle_tick); } - SEQ_CST_STORE(&timer_disabled, 1); + SEQ_CST_STORE_ALWAYS(&timer_disabled, 1); #endif } @@ -192,7 +192,7 @@ void startTimer(void) { #if defined(HAVE_PREEMPTION) - if (atomic_dec(&timer_disabled) == 0) { + if (SEQ_CST_SUB_ALWAYS(&timer_disabled, 1) == 0) { if (RtsFlags.MiscFlags.tickInterval != 0) { startTicker(); } @@ -204,7 +204,7 @@ void stopTimer(void) { #if defined(HAVE_PREEMPTION) - if (atomic_inc(&timer_disabled, 1) == 1) { + if (SEQ_CST_ADD_ALWAYS(&timer_disabled, 1) == 1) { if (RtsFlags.MiscFlags.tickInterval != 0) { stopTicker(); } diff --git a/rts/include/stg/SMP.h b/rts/include/stg/SMP.h index 0800c87786..c9ff736032 100644 --- a/rts/include/stg/SMP.h +++ b/rts/include/stg/SMP.h @@ -18,6 +18,25 @@ void arm_atomic_spin_lock(void); void arm_atomic_spin_unlock(void); #endif +// Unconditionally atomic operations +// These are atomic even in the non-threaded RTS. These are necessary in the +// Proftimer implementation, which may be called from the pthreads-based +// ITimer implementation. +#define RELAXED_LOAD_ALWAYS(ptr) __atomic_load_n(ptr, __ATOMIC_RELAXED) +#define RELAXED_STORE_ALWAYS(ptr,val) __atomic_store_n(ptr, val, __ATOMIC_RELAXED) +#define RELAXED_ADD_ALWAYS(ptr,val) __atomic_add_fetch(ptr, val, __ATOMIC_RELAXED) + +// Acquire/release atomic operations +#define ACQUIRE_LOAD_ALWAYS(ptr) __atomic_load_n(ptr, __ATOMIC_ACQUIRE) +#define RELEASE_STORE_ALWAYS(ptr,val) __atomic_store_n(ptr, val, __ATOMIC_RELEASE) + +// Sequentially consistent atomic operations +#define SEQ_CST_LOAD_ALWAYS(ptr) __atomic_load_n(ptr, __ATOMIC_SEQ_CST) +#define SEQ_CST_STORE_ALWAYS(ptr,val) __atomic_store_n(ptr, val, __ATOMIC_SEQ_CST) +#define SEQ_CST_ADD_ALWAYS(ptr,val) __atomic_add_fetch(ptr, val, __ATOMIC_SEQ_CST) +#define SEQ_CST_SUB_ALWAYS(ptr,val) __atomic_sub_fetch(ptr, val, __ATOMIC_SEQ_CST) + + #if defined(THREADED_RTS) /* ---------------------------------------------------------------------------- |