mirror of
https://github.com/tangerineturtle1/NX2navi.git
synced 2026-11-15 17:09:57 -04:00
Make excl_mt check the exclusive monitor as it is: a compare-and-swap of the value loaded.
The test expected a store exclusive to fail after another thread changed the value with exclusive pairs and changed it back (ABA), as the console's monitor would. runtime/cpu.h's monitor (EXCL ldxN/stxN, ldxp128/stxp128) compares the memory with the value loaded, so a value restored to the same bits passes by design, and the test has failed since both came in with the base import. It now checks both cases the helpers define: one change fails the store (status 1), a change and its undoing does not (status 0), for the 32-bit, 64-bit and 128-bit forms, with the control and the contention runs as before. Its docstring, a comment and docs/building.md's row say that the monitor does not see the change-and-restore case. No runtime change. The test passes under .venv: the one-change cases fail the store, the two-change cases succeed, and every contention counter is exact. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -780,7 +780,7 @@ change passes these, all run with `.venv`:
|
||||
| `tests\flags_check.py` | the function-local flag helpers of `runtime\cpu.h` against its packed ones | no |
|
||||
| `tests\ds3_check.py` | the DualShock 3 report decoding and output report of `runtime\input_ds3.c` | no |
|
||||
| `tests\gpa_check.py` | the device address table of `runtime\vk_gpa.c` as a shader reads it: mapping, mirrors and twins, overlapping imports, and blocks given back and taken again while pool addresses climb (no GPU; any Python) | no |
|
||||
| `tests\excl_mt.py` | the exclusive monitor helpers under contention and ABA, on several threads | no |
|
||||
| `tests\excl_mt.py` | the exclusive monitor helpers under contention, a changed value, and the change-and-restore case the compare-and-swap monitor does not see, on several threads | no |
|
||||
| `tests\jumptables_check.py` | `recomp/jumptables.py` against a Unicorn oracle; must report 0 disagreements, on 1.4.3 too | yes |
|
||||
|
||||
A `--cpu v2` build passes the same differential tests with `--cflags=-march=x86-64-v2`; they compile `runtime/fma.c`
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
"""Multithreaded test of the exclusive monitor helpers in cpu.h (ldxN/stxN, ldxp128/stxp128).
|
||||
|
||||
- ABA: thread A loads X exclusively, thread B changes X->Y->X with its own exclusive pairs, then A's store exclusive
|
||||
must fail. Checked for the 32-bit, 64-bit and 128-bit pair forms, plus a control where B touches nothing.
|
||||
- A changed value: thread A loads X exclusively, thread B changes X to Y with its own exclusive pair, then A's store
|
||||
exclusive must fail. Checked for the 32-bit, 64-bit and 128-bit pair forms, plus a control where B touches nothing.
|
||||
- A value changed and changed back (ABA): B changes X to Y and back to X, and A's store exclusive succeeds. The monitor
|
||||
is a compare-and-swap of the value loaded (runtime/cpu.h, "exclusive monitor"), so it does not see this case, where
|
||||
the console's monitor would fail the store; the check holds the helpers to what they are.
|
||||
- Contention: N threads increment one counter with load/store-exclusive retry loops; the total must be exact.
|
||||
|
||||
Needs no game files. Usage: excl_mt.py [--runtime DIR] [--threads N] [--iters K]
|
||||
@@ -30,14 +33,15 @@ static int fails;
|
||||
static void wait_step(int s) { while (__atomic_load_n(&step, __ATOMIC_ACQUIRE) != s) {} }
|
||||
static void set_step(int s) { __atomic_store_n(&step, s, __ATOMIC_RELEASE); }
|
||||
|
||||
// Thread B: X -> Y -> X with exclusive pairs of the given width (0 = do nothing, the control).
|
||||
static int aba_width;
|
||||
// Thread B: aba_changes exclusive stores of the given width (0 = do nothing, the control): one changes X to Y, two
|
||||
// change it to Y and back to X.
|
||||
static int aba_width, aba_changes;
|
||||
static void *aba_b(void *arg) {
|
||||
(void)arg;
|
||||
Cpu c = {0};
|
||||
uint64_t a = (uint64_t)(uintptr_t)cell;
|
||||
wait_step(1);
|
||||
for (int k = 0; aba_width && k < 2; k++) {
|
||||
for (int k = 0; aba_width && k < aba_changes; k++) {
|
||||
for (;;) {
|
||||
if (aba_width == 32) { uint32_t v = (uint32_t)ldx32(&c, a); if (!stx32(&c, a, v ^ 0x5a5a5a5au)) break; }
|
||||
else if (aba_width == 64) { uint64_t v = ldx64(&c, a); if (!stx64(&c, a, v ^ 0x5a5a5a5a5a5a5a5aull)) break; }
|
||||
@@ -48,13 +52,14 @@ static void *aba_b(void *arg) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void aba_case(int width, int expect_fail) {
|
||||
static void aba_case(int width, int changes, int expect_fail) {
|
||||
pthread_t t;
|
||||
Cpu c = {0};
|
||||
uint64_t a = (uint64_t)(uintptr_t)cell;
|
||||
cell[0] = 0x1111222233334444ull;
|
||||
cell[1] = 0x5555666677778888ull;
|
||||
aba_width = width;
|
||||
aba_changes = changes;
|
||||
set_step(0);
|
||||
pthread_create(&t, 0, aba_b, 0);
|
||||
uint32_t st;
|
||||
@@ -76,7 +81,8 @@ static void aba_case(int width, int expect_fail) {
|
||||
}
|
||||
pthread_join(t, 0);
|
||||
int want = expect_fail != 0;
|
||||
printf("aba width %3d: stx status %u, want %d %s\n", width, st, want, st == (uint32_t)want ? "ok" : "FAIL");
|
||||
printf("aba width %3d, %d change%s: stx status %u, want %d %s\n", width, changes, changes == 1 ? "" : "s", st, want,
|
||||
st == (uint32_t)want ? "ok" : "FAIL");
|
||||
if (st != (uint32_t)want) fails++;
|
||||
}
|
||||
|
||||
@@ -112,10 +118,15 @@ static void inc_case(int width) {
|
||||
int main(int argc, char **argv) {
|
||||
nthreads = atoi(argv[1]);
|
||||
iters = atoi(argv[2]);
|
||||
aba_case(0, 0);
|
||||
aba_case(32, 1);
|
||||
aba_case(64, 1);
|
||||
aba_case(128, 1);
|
||||
aba_case(0, 0, 0);
|
||||
aba_case(32, 1, 1);
|
||||
aba_case(64, 1, 1);
|
||||
aba_case(128, 1, 1);
|
||||
// The monitor is a compare-and-swap of the value loaded (runtime/cpu.h, "exclusive monitor"): a value changed and
|
||||
// changed back is not seen, and the store exclusive succeeds where the console's monitor would fail it.
|
||||
aba_case(32, 2, 0);
|
||||
aba_case(64, 2, 0);
|
||||
aba_case(128, 2, 0);
|
||||
inc_case(32);
|
||||
inc_case(64);
|
||||
inc_case(128);
|
||||
|
||||
Reference in New Issue
Block a user