summaryrefslogtreecommitdiff
path: root/src/test
diff options
context:
space:
mode:
authorAndres Freund <andres@anarazel.de>2020-06-08 16:50:37 -0700
committerAndres Freund <andres@anarazel.de>2020-06-18 14:12:40 -0700
commitc2a84bee1228830e82b1d2e86dc836bc00ac812a (patch)
tree7356e99ee494855594d89989ff77edaed5f744c6 /src/test
parent3f66e1c5a5768f68f71d1bd1ef322af2202d5e3c (diff)
Fix deadlock danger when atomic ops are done under spinlock.
This was a danger only for --disable-spinlocks in combination with atomic operations unsupported by the current platform. While atomics.c was careful to signal that a separate semaphore ought to be used when spinlock emulation is active, spin.c didn't actually implement that mechanism. That's my (Andres') fault, it seems to have gotten lost during the development of the atomic operations support. Fix that issue and add test for nesting atomic operations inside a spinlock. Author: Andres Freund Discussion: https://postgr.es/m/20200605023302.g6v3ydozy5txifji@alap3.anarazel.de Backpatch: 9.5-
Diffstat (limited to 'src/test')
-rw-r--r--src/test/regress/regress.c52
1 files changed, 52 insertions, 0 deletions
diff --git a/src/test/regress/regress.c b/src/test/regress/regress.c
index 0d233b285cf..51f18d10775 100644
--- a/src/test/regress/regress.c
+++ b/src/test/regress/regress.c
@@ -1140,6 +1140,56 @@ test_spinlock(void)
#endif
}
+/*
+ * Verify that performing atomic ops inside a spinlock isn't a
+ * problem. Realistically that's only going to be a problem when both
+ * --disable-spinlocks and --disable-atomics are used, but it's cheap enough
+ * to just always test.
+ *
+ * The test works by initializing enough atomics that we'd conflict if there
+ * were an overlap between a spinlock and an atomic by holding a spinlock
+ * while manipulating more than NUM_SPINLOCK_SEMAPHORES atomics.
+ *
+ * NUM_TEST_ATOMICS doesn't really need to be more than
+ * NUM_SPINLOCK_SEMAPHORES, but it seems better to test a bit more
+ * extensively.
+ */
+static void
+test_atomic_spin_nest(void)
+{
+ slock_t lock;
+#define NUM_TEST_ATOMICS (NUM_SPINLOCK_SEMAPHORES + NUM_ATOMICS_SEMAPHORES + 27)
+ pg_atomic_uint32 atomics32[NUM_TEST_ATOMICS];
+ pg_atomic_uint64 atomics64[NUM_TEST_ATOMICS];
+
+ SpinLockInit(&lock);
+
+ for (int i = 0; i < NUM_TEST_ATOMICS; i++)
+ {
+ pg_atomic_init_u32(&atomics32[i], 0);
+ pg_atomic_init_u64(&atomics64[i], 0);
+ }
+
+ /* just so it's not all zeroes */
+ for (int i = 0; i < NUM_TEST_ATOMICS; i++)
+ {
+ EXPECT_EQ_U32(pg_atomic_fetch_add_u32(&atomics32[i], i), 0);
+ EXPECT_EQ_U64(pg_atomic_fetch_add_u64(&atomics64[i], i), 0);
+ }
+
+ /* test whether we can do atomic op with lock held */
+ SpinLockAcquire(&lock);
+ for (int i = 0; i < NUM_TEST_ATOMICS; i++)
+ {
+ EXPECT_EQ_U32(pg_atomic_fetch_sub_u32(&atomics32[i], i), i);
+ EXPECT_EQ_U32(pg_atomic_read_u32(&atomics32[i]), 0);
+ EXPECT_EQ_U64(pg_atomic_fetch_sub_u64(&atomics64[i], i), i);
+ EXPECT_EQ_U64(pg_atomic_read_u64(&atomics64[i]), 0);
+ }
+ SpinLockRelease(&lock);
+}
+#undef NUM_TEST_ATOMICS
+
PG_FUNCTION_INFO_V1(test_atomic_ops);
Datum
test_atomic_ops(PG_FUNCTION_ARGS)
@@ -1156,5 +1206,7 @@ test_atomic_ops(PG_FUNCTION_ARGS)
*/
test_spinlock();
+ test_atomic_spin_nest();
+
PG_RETURN_BOOL(true);
}