summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Gamari <ben@smart-cactus.org>2022-11-10 18:29:45 -0500
committerMarge Bot <ben+marge-bot@smart-cactus.org>2022-12-16 16:12:45 -0500
commit86f20258ab7dbfb56e323ee811e9eaef80b077d3 (patch)
tree48ffebf1bd6adea439e3197cfa3a9d14eabacc2b
parent605d954722a314c0da59ea07efc26d8a7cb59296 (diff)
downloadhaskell-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.c23
-rw-r--r--rts/Timer.c16
-rw-r--r--rts/include/stg/SMP.h19
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)
/* ----------------------------------------------------------------------------