gfx_widgets: the menu state a widget iterate reads travels with the frame

The netplay ping widget asked the menu whether it was open, from
gfx_widget_netplay_ping_iterate(), which the threaded wrapper runs on
the video thread. The menu's flags are a read-modify-write on the main
thread - runloop_check_state() clears two of them in the same
expression - so the read raced the write, on a word the whole menu
keeps its state in.

The frame already carries those flags: video_driver_frame() copies them
where the menu cannot be changing. The worker latches that copy before
it iterates, and the widget reads the latch.

Found with networking on. The threaded video suites had been run
against a build configured without it, so the file this lives in was
never compiled and the widget never ran.
This commit is contained in:
libretroadmin committed 2026-09-13 15:23:36 +02:00
1 parent 3da8ee7485
commit 99d852a93b
3 files changed
+11 -1

No files matched your search

+1
View File
@@ -2790,6 +2790,7 @@ void gfx_widgets_worker_step(void *data,
video_info->width, video_info->height);
/* What the frame carried, not the settings the main thread writes:
* this runs on the video thread under the threaded wrapper. */
p_dispwidget->frame_menu_st_flags = (uint16_t)video_info->menu_st_flags;
gfx_widgets_iterate_frame(
video_info->width, video_info->height, video_info->fullscreen,
video_info->widget_dir_assets,
+6
View File
@@ -335,6 +335,12 @@ typedef struct dispgfx_widget
char monochrome_png_path[PATH_MAX_LENGTH];
char gfx_widgets_path[PATH_MAX_LENGTH];
/* The menu state the frame being iterated was built with. A widget
* that wants to know whether the menu is open reads this rather
* than the menu's own flags: those are a read-modify-write on the
* main thread, and an iterate runs on the thread that draws. */
uint16_t frame_menu_st_flags;
} dispgfx_widget_t;
/* A widget */
+4 -1
View File
@@ -10643,7 +10643,10 @@ static void gfx_widget_netplay_ping_iterate(void *user_data,
netplay_t *netplay = net_st->data;
settings_t *settings = config_get_ptr();
#ifdef HAVE_MENU
bool menu_open = menu_state_get_ptr()->flags &
/* What the frame this iterate belongs to was built with: the menu's
* own flags are a read-modify-write on the main thread, and this
* runs on the thread that draws. */
bool menu_open = dispwidget_get_ptr()->frame_menu_st_flags &
MENU_ST_FLAG_ALIVE;
#endif
bool show_ping = settings->bools.netplay_ping_show;