diff --git a/runtime/input.c b/runtime/input.c index 1bb0cd33..7839b8c8 100644 --- a/runtime/input.c +++ b/runtime/input.c @@ -667,9 +667,11 @@ static int hd_tick(void) { plat_mutex_unlock(&lock); if (close_pair) hd_pair_close(1, 0); if (due && kind == HD_PAIR) { - int ok = hd_pair_write(d); // waits for the two writes, so not under lock + // waits for the two writes, so not under lock; after input_rumble_hd_stop it writes nothing (hd_pair_end) + int ok = hd_pair_write(d); plat_mutex_lock(&lock); - if (pad && pad_hd == HD_PAIR) hd_wrote(ok, d, now, ok ? "" : SDL_GetError()); // unless the pair left meanwhile + // unless the pair left or the stop came meanwhile (hd_ended is written under lock as well) + if (pad && pad_hd == HD_PAIR && !hd_ended) hd_wrote(ok, d, now, ok ? "" : SDL_GetError()); plat_mutex_unlock(&lock); } return idle; @@ -732,7 +734,7 @@ void input_rumble_hd_stop(void) { } } plat_mutex_unlock(&lock); - if (pair) hd_pair_close(1, 100); + if (pair) hd_pair_end(100); if (wait) plat_wait_ns(30000000); // SDL writes the report on a thread of its own: time for it before the process ends } diff --git a/runtime/input_hd_pair.c b/runtime/input_hd_pair.c index 2cee6eb5..e17229be 100644 --- a/runtime/input_hd_pair.c +++ b/runtime/input_hd_pair.c @@ -31,6 +31,7 @@ static char *path[2]; // the HID paths hd_pair_find sa static PlatMutex dev_lock = PLAT_MUTEX_INIT; // guards dev[] and number[], and is held across a write static SDL_hid_device *dev[2]; static unsigned number[2]; // the next report's number, per device +static volatile int32_t ended; // hd_pair_end ran: hd_pair_write, under dev_lock, writes nothing int hd_pair_find(void) { char *found[2] = {NULL, NULL}; @@ -71,6 +72,11 @@ static int write_side(int s, const uint8_t v[RUMBLE_HD_SIDE]) { int hd_pair_write(const uint8_t data[RUMBLE_HD_DATA]) { int ok = 1; plat_mutex_lock(&dev_lock); + if (__atomic_load_n(&ended, __ATOMIC_ACQUIRE)) { // under the write's lock: no stop between this and the write + plat_mutex_unlock(&dev_lock); + SDL_SetError("HD rumble has ended"); + return 0; + } if (!dev[0] || !dev[1]) { close_devices(); char *p[2]; @@ -111,3 +117,8 @@ void hd_pair_close(int neutral, unsigned wait_ms) { close_devices(); plat_mutex_unlock(&dev_lock); } + +void hd_pair_end(unsigned wait_ms) { + __atomic_store_n(&ended, 1, __ATOMIC_RELEASE); // before the lock: a write that holds it now is the last one + hd_pair_close(1, wait_ms); +} diff --git a/runtime/input_hd_pair.h b/runtime/input_hd_pair.h index f17e57f4..76d401fb 100644 --- a/runtime/input_hd_pair.h +++ b/runtime/input_hd_pair.h @@ -10,10 +10,14 @@ int hd_pair_find(void); // Writes one rumble report to each Joy-Con: the left half of data to the left one, the right half to the right one. // The first call after hd_pair_find opens the two devices. 0 when a device could not be opened or written (both are -// closed then, and SDL's error text says why). Blocks for as long as the writes take: the HD rumble thread only. +// closed then, and SDL's error text says why), and after hd_pair_end. Blocks for as long as the writes take: the HD +// rumble thread only. int hd_pair_write(const uint8_t data[RUMBLE_HD_DATA]); // 1 while the two devices are open. int hd_pair_is_open(void); // Writes the neutral value to both Joy-Con, when neutral is set, and closes them. wait_ms: how long to wait for a // write that is still running on another thread before giving up (the process is ending); 0 waits for it. void hd_pair_close(int neutral, unsigned wait_ms); +// The process is ending (input.c input_rumble_hd_stop): hd_pair_close(1, wait_ms), and hd_pair_write writes nothing +// from here on, so a report the HD thread chose before the stop cannot open the two devices again after it. +void hd_pair_end(unsigned wait_ms);