mirror of https://github.com/encounter/SDL.git
wayland: Use multi-thread event reading APIs
Wayland provides the prepare_read()/read_events() family of APIs for reading from the display fd in a deadlock-free manner across multiple threads in a multi-threaded application. Let's use those instead of trying to roll our own solution using a mutex. This fixes an issue where a call to SDL_GL_SwapWindow() doesn't swap buffers if it happens to collide with SDL_PumpEvents() in the main thread. It also allows coexistence with other code or toolkits in our process that may want read and dispatch events themselves.
This commit is contained in:
parent
4960cc3dcb
commit
408a93a1ec
|
@ -231,20 +231,18 @@ Wayland_PumpEvents(_THIS)
|
||||||
keyboard_repeat_handle(&input->keyboard_repeat, now);
|
keyboard_repeat_handle(&input->keyboard_repeat, now);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we're trying to dispatch the display in another thread,
|
/* wl_display_prepare_read() will return -1 if the default queue is not empty.
|
||||||
* we could trigger a race condition and end up blocking
|
* If the default queue is empty, it will prepare us for our SDL_IOReady() call. */
|
||||||
* in wl_display_dispatch() */
|
if (WAYLAND_wl_display_prepare_read(d->display) == 0) {
|
||||||
if (SDL_TryLockMutex(d->display_dispatch_lock) != 0) {
|
if (SDL_IOReady(WAYLAND_wl_display_get_fd(d->display), SDL_FALSE, 0) > 0) {
|
||||||
return;
|
WAYLAND_wl_display_read_events(d->display);
|
||||||
|
} else {
|
||||||
|
WAYLAND_wl_display_cancel_read(d->display);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SDL_IOReady(WAYLAND_wl_display_get_fd(d->display), SDL_FALSE, 0)) {
|
/* Dispatch any pre-existing pending events or new events we may have read */
|
||||||
err = WAYLAND_wl_display_dispatch(d->display);
|
err = WAYLAND_wl_display_dispatch_pending(d->display);
|
||||||
} else {
|
|
||||||
err = WAYLAND_wl_display_dispatch_pending(d->display);
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_UnlockMutex(d->display_dispatch_lock);
|
|
||||||
|
|
||||||
if (err == -1 && !d->display_disconnected) {
|
if (err == -1 && !d->display_disconnected) {
|
||||||
/* Something has failed with the Wayland connection -- for example,
|
/* Something has failed with the Wayland connection -- for example,
|
||||||
|
|
|
@ -136,33 +136,34 @@ Wayland_GLES_SwapWindow(_THIS, SDL_Window *window)
|
||||||
while (SDL_AtomicGet(&data->swap_interval_ready) == 0) {
|
while (SDL_AtomicGet(&data->swap_interval_ready) == 0) {
|
||||||
Uint32 now;
|
Uint32 now;
|
||||||
|
|
||||||
/* !!! FIXME: this is just the crucial piece of Wayland_PumpEvents */
|
|
||||||
WAYLAND_wl_display_flush(display);
|
WAYLAND_wl_display_flush(display);
|
||||||
if (WAYLAND_wl_display_dispatch_queue_pending(display, data->frame_event_queue) > 0) {
|
|
||||||
/* We dispatched some pending events. Check if the frame callback happened. */
|
/* wl_display_prepare_read_queue() will return -1 if the event queue is not empty.
|
||||||
|
* If the event queue is empty, it will prepare us for our SDL_IOReady() call. */
|
||||||
|
if (WAYLAND_wl_display_prepare_read_queue(display, data->frame_event_queue) != 0) {
|
||||||
|
/* We have some pending events. Check if the frame callback happened. */
|
||||||
|
WAYLAND_wl_display_dispatch_queue_pending(display, data->frame_event_queue);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Beyond this point, we must either call wl_display_cancel_read() or wl_display_read_events() */
|
||||||
|
|
||||||
now = SDL_GetTicks();
|
now = SDL_GetTicks();
|
||||||
if (SDL_TICKS_PASSED(now, max_wait)) {
|
if (SDL_TICKS_PASSED(now, max_wait)) {
|
||||||
/* Timeout expired */
|
/* Timeout expired. Cancel the read. */
|
||||||
|
WAYLAND_wl_display_cancel_read(display);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make sure we're not competing with SDL_PumpEvents() for any new
|
|
||||||
* events, or one of us may end up blocking in wl_display_dispatch */
|
|
||||||
if (SDL_TryLockMutex(videodata->display_dispatch_lock) != 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SDL_IOReady(WAYLAND_wl_display_get_fd(display), SDL_FALSE, max_wait - now) <= 0) {
|
if (SDL_IOReady(WAYLAND_wl_display_get_fd(display), SDL_FALSE, max_wait - now) <= 0) {
|
||||||
/* Error or timeout expired without any events for us */
|
/* Error or timeout expired without any events for us. Cancel the read. */
|
||||||
SDL_UnlockMutex(videodata->display_dispatch_lock);
|
WAYLAND_wl_display_cancel_read(display);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
WAYLAND_wl_display_dispatch_queue(display, data->frame_event_queue);
|
/* We have events. Read and dispatch them. */
|
||||||
SDL_UnlockMutex(videodata->display_dispatch_lock);
|
WAYLAND_wl_display_read_events(display);
|
||||||
|
WAYLAND_wl_display_dispatch_queue_pending(display, data->frame_event_queue);
|
||||||
}
|
}
|
||||||
SDL_AtomicSet(&data->swap_interval_ready, 0);
|
SDL_AtomicSet(&data->swap_interval_ready, 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,10 @@ SDL_WAYLAND_SYM(int, wl_display_dispatch, (struct wl_display *))
|
||||||
SDL_WAYLAND_SYM(int, wl_display_dispatch_queue, (struct wl_display *, struct wl_event_queue *))
|
SDL_WAYLAND_SYM(int, wl_display_dispatch_queue, (struct wl_display *, struct wl_event_queue *))
|
||||||
SDL_WAYLAND_SYM(int, wl_display_dispatch_queue_pending, (struct wl_display *, struct wl_event_queue *))
|
SDL_WAYLAND_SYM(int, wl_display_dispatch_queue_pending, (struct wl_display *, struct wl_event_queue *))
|
||||||
SDL_WAYLAND_SYM(int, wl_display_dispatch_pending, (struct wl_display *))
|
SDL_WAYLAND_SYM(int, wl_display_dispatch_pending, (struct wl_display *))
|
||||||
|
SDL_WAYLAND_SYM(int, wl_display_prepare_read, (struct wl_display *))
|
||||||
|
SDL_WAYLAND_SYM(int, wl_display_prepare_read_queue, (struct wl_display *, struct wl_event_queue *))
|
||||||
|
SDL_WAYLAND_SYM(int, wl_display_read_events, (struct wl_display *))
|
||||||
|
SDL_WAYLAND_SYM(void, wl_display_cancel_read, (struct wl_display *))
|
||||||
SDL_WAYLAND_SYM(int, wl_display_get_error, (struct wl_display *))
|
SDL_WAYLAND_SYM(int, wl_display_get_error, (struct wl_display *))
|
||||||
SDL_WAYLAND_SYM(int, wl_display_flush, (struct wl_display *))
|
SDL_WAYLAND_SYM(int, wl_display_flush, (struct wl_display *))
|
||||||
SDL_WAYLAND_SYM(int, wl_display_roundtrip, (struct wl_display *))
|
SDL_WAYLAND_SYM(int, wl_display_roundtrip, (struct wl_display *))
|
||||||
|
|
|
@ -169,7 +169,6 @@ Wayland_DeleteDevice(SDL_VideoDevice *device)
|
||||||
WAYLAND_wl_display_flush(data->display);
|
WAYLAND_wl_display_flush(data->display);
|
||||||
WAYLAND_wl_display_disconnect(data->display);
|
WAYLAND_wl_display_disconnect(data->display);
|
||||||
}
|
}
|
||||||
SDL_DestroyMutex(data->display_dispatch_lock);
|
|
||||||
SDL_free(data);
|
SDL_free(data);
|
||||||
SDL_free(device);
|
SDL_free(device);
|
||||||
SDL_WAYLAND_UnloadSymbols();
|
SDL_WAYLAND_UnloadSymbols();
|
||||||
|
@ -202,8 +201,6 @@ Wayland_CreateDevice(int devindex)
|
||||||
|
|
||||||
data->display = display;
|
data->display = display;
|
||||||
|
|
||||||
data->display_dispatch_lock = SDL_CreateMutex();
|
|
||||||
|
|
||||||
/* Initialize all variables that we clean on shutdown */
|
/* Initialize all variables that we clean on shutdown */
|
||||||
device = SDL_calloc(1, sizeof(SDL_VideoDevice));
|
device = SDL_calloc(1, sizeof(SDL_VideoDevice));
|
||||||
if (!device) {
|
if (!device) {
|
||||||
|
|
|
@ -86,7 +86,6 @@ typedef struct {
|
||||||
char *classname;
|
char *classname;
|
||||||
|
|
||||||
int relative_mouse_mode;
|
int relative_mouse_mode;
|
||||||
SDL_mutex *display_dispatch_lock;
|
|
||||||
} SDL_VideoData;
|
} SDL_VideoData;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
Loading…
Reference in New Issue