tasks: the Bluetooth scan does not spin at the end of its window

The queue runs a parked task up to half a millisecond early, so the
scan's last wait, under a millisecond, was run again at once, over
and over until the window was out. What is left within a millisecond
of the end is no longer waited.

The scan test stands in for the driver's new done call, and pins that
a driver done early ends the scan before the window.
This commit is contained in:
LibretroAdmin authored and libretroadmin committed 2026-10-22 09:02:15 +02:00
1 parent 6153138ea9
commit ee359e3cd9
3 files changed
+38 -13

No files matched your search

+5 -4
View File
@@ -1267,10 +1267,11 @@ jobs:
# The Bluetooth scan task on the real threaded task queue,
# with the discovery window shortened. The scan used to wait
# out the window inside one handler call, holding the task
# thread; it now begins, reschedules itself for the end of
# the window, and ends. Pins that begin and end run once each
# a window apart, that another task runs during the window,
# and that the callback fires.
# thread; it now begins, is parked, looks every poll whether
# the driver is done, and ends. Pins that begin and end run
# once each a window apart, that another task runs during the
# window, that the callback fires, and that a driver done
# early ends the scan then.
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
timeout 60 ./bluetooth_scan_test
@@ -4,16 +4,17 @@
* The scan ran inside one handler call and the drivers waited out the
* ten-second discovery window in it - bluez in a sleep, bluetoothctl in
* a blocking "scan on" - holding the one task thread, and every task
* queued behind it, for the whole window. It is now two steps: begin,
* then the task reschedules itself for the end of the window and the
* queue runs other tasks meanwhile.
* queued behind it, for the whole window. It now begins, is parked and
* looks every poll whether the driver is done, and the queue runs
* other tasks meanwhile.
*
* The contract this pins, with the window shortened to 300 ms:
*
* begin, then end -> once each, a window apart
* during the window -> another task runs at once
* the callback -> fires after end, with the task done
* the end of the window -> a pass or two, not a run of them
* the window -> a pass a poll, not a run of them
* a driver done early -> the scan ends then, not at the window
*/
#include <stdio.h>
@@ -50,6 +51,11 @@ void driver_bluetooth_scan_end(void)
{ end_at = mono_ms(); retro_atomic_fetch_add_int(&ends, 1); }
const char *msg_hash_to_str(enum msg_hash_enums msg) { return "msg"; }
/* the driver says it has found all it will */
static retro_atomic_int_t driver_done = RETRO_ATOMIC_INT_INITIALIZER(0);
bool driver_bluetooth_scan_done(void)
{ return retro_atomic_load_acquire_int(&driver_done) != 0; }
/* ---- the task under test's neighbours ---- */
static retro_atomic_int_t other_ran = RETRO_ATOMIC_INT_INITIALIZER(0);
@@ -123,10 +129,26 @@ int main(void)
"a window after it began");
check(retro_atomic_load_acquire_int(&scan_done), "the scan's callback fires");
check(retro_atomic_load_acquire_int(&begins) == 1, "and it began only once");
/* The queue may start the end of the window a little early; the
* scan waits out the rest parked, not in a run of passes. */
check(retro_atomic_load_acquire_int(&scan_passes) <= 4,
"the window ends in a pass or two");
/* a pass a poll through the window, parked between them */
check(retro_atomic_load_acquire_int(&scan_passes)
<= BLUETOOTH_SCAN_WINDOW_US / BLUETOOTH_SCAN_POLL_US + 3,
"the window passes in a pass a poll");
/* a driver that has found all it will 100 ms in: ended then */
retro_atomic_store_release_int(&scan_done, 0);
check(task_push_bluetooth_scan(scan_cb), "a second scan is pushed");
for (i = 0; i < 1000 && retro_atomic_load_acquire_int(&begins) < 2; i++)
usleep(1000);
usleep(100000);
retro_atomic_store_release_int(&driver_done, 1);
for (i = 0; i < 3000 && !retro_atomic_load_acquire_int(&scan_done); i++)
{
task_queue_check();
usleep(1000);
}
check(retro_atomic_load_acquire_int(&ends) == 2
&& end_at - begin_at < 280,
"a driver done early ends the scan before the window");
task_queue_deinit();
+3 -1
View File
@@ -54,7 +54,9 @@ static void task_bluetooth_scan_handler(retro_task_t *task)
&& !driver_bluetooth_scan_done())
{
retro_time_t now = cpu_features_get_time_usec();
if (now < task_bluetooth_scan_deadline)
/* the queue runs a parked task up to half a millisecond early:
* what is left within a millisecond of the end is not waited */
if (now + 1000 < task_bluetooth_scan_deadline)
{
retro_time_t wait = task_bluetooth_scan_deadline - now;
if (wait > BLUETOOTH_SCAN_POLL_US)