mirror of
https://github.com/glfw/glfw.git
synced 2025-06-15 12:12:16 +00:00
Fix #1281: glfwPostEmptyEvent sometimes doesn't wake up glfwWaitEvents on X11.
This is a rare race that triggers often when running GLFW programs on GitHub Actions with Xvfb. The underlying issue is internal to Xlib: XSendEvent races with an external select() call and may get dispatched before select() has blocked on the display fd. The fix observes a pipe in the select() call, and writes to that pipe from glfwPostEmptyEvent to make select() return. The original issue reproduces locally once per minute when running the windowjs/windowjs tests in a loop; The bug hasn't triggered after running for several hours with the fix.
This commit is contained in:
parent
df8d7bc892
commit
1bef01f210
@ -121,6 +121,7 @@ information on what to include when reporting a bug.
|
|||||||
|
|
||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
|
- Bugfix: fixed glfwPostEmptyEvent not unblocking glfwWaitEvents on X11 (#1281)
|
||||||
- Added `GLFW_PLATFORM` init hint for runtime platform selection (#1958)
|
- Added `GLFW_PLATFORM` init hint for runtime platform selection (#1958)
|
||||||
- Added `GLFW_ANY_PLATFORM`, `GLFW_PLATFORM_WIN32`, `GLFW_PLATFORM_COCOA`,
|
- Added `GLFW_ANY_PLATFORM`, `GLFW_PLATFORM_WIN32`, `GLFW_PLATFORM_COCOA`,
|
||||||
`GLFW_PLATFORM_WAYLAND`, `GLFW_PLATFORM_X11` and `GLFW_PLATFORM_NULL` symbols to
|
`GLFW_PLATFORM_WAYLAND`, `GLFW_PLATFORM_X11` and `GLFW_PLATFORM_NULL` symbols to
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
|
||||||
// Translate the X11 KeySyms for a key to a GLFW key code
|
// Translate the X11 KeySyms for a key to a GLFW key code
|
||||||
@ -1053,6 +1054,35 @@ static int errorHandler(Display *display, XErrorEvent* event)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline GLFWbool selfPipe(int fds[2])
|
||||||
|
{
|
||||||
|
if (pipe(fds) != 0)
|
||||||
|
return GLFW_FALSE;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < 2; i++)
|
||||||
|
{
|
||||||
|
int flags = fcntl(fds[i], F_GETFD);
|
||||||
|
if (flags == -1)
|
||||||
|
break;
|
||||||
|
if (fcntl(fds[i], F_SETFD, flags | FD_CLOEXEC) == -1)
|
||||||
|
break;
|
||||||
|
flags = fcntl(fds[i], F_GETFL);
|
||||||
|
if (flags == -1)
|
||||||
|
break;
|
||||||
|
if (fcntl(fds[i], F_SETFL, flags | O_NONBLOCK) == -1)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == 2)
|
||||||
|
return GLFW_TRUE;
|
||||||
|
|
||||||
|
close(fds[0]);
|
||||||
|
close(fds[1]);
|
||||||
|
|
||||||
|
return GLFW_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
////// GLFW internal API //////
|
////// GLFW internal API //////
|
||||||
@ -1509,6 +1539,13 @@ int _glfwInitX11(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
_glfwPollMonitorsX11();
|
_glfwPollMonitorsX11();
|
||||||
|
|
||||||
|
if (!selfPipe(_glfw.x11.eventLoopData.pipe))
|
||||||
|
{
|
||||||
|
_glfwInputError(GLFW_PLATFORM_ERROR, "X11: failed to create pipe");
|
||||||
|
return GLFW_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
return GLFW_TRUE;
|
return GLFW_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1594,6 +1631,18 @@ void _glfwTerminateX11(void)
|
|||||||
_glfw.x11.xi.handle = NULL;
|
_glfw.x11.xi.handle = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_glfw.x11.eventLoopData.pipe[0] > 0)
|
||||||
|
{
|
||||||
|
close(_glfw.x11.eventLoopData.pipe[0]);
|
||||||
|
_glfw.x11.eventLoopData.pipe[0] = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_glfw.x11.eventLoopData.pipe[1] > 0)
|
||||||
|
{
|
||||||
|
close(_glfw.x11.eventLoopData.pipe[1]);
|
||||||
|
_glfw.x11.eventLoopData.pipe[1] = -1;
|
||||||
|
}
|
||||||
|
|
||||||
// NOTE: These need to be unloaded after XCloseDisplay, as they register
|
// NOTE: These need to be unloaded after XCloseDisplay, as they register
|
||||||
// cleanup callbacks that get called by that function
|
// cleanup callbacks that get called by that function
|
||||||
_glfwTerminateEGL();
|
_glfwTerminateEGL();
|
||||||
|
@ -871,6 +871,10 @@ typedef struct _GLFWlibraryX11
|
|||||||
PFN_XShapeQueryVersion QueryVersion;
|
PFN_XShapeQueryVersion QueryVersion;
|
||||||
PFN_XShapeCombineMask ShapeCombineMask;
|
PFN_XShapeCombineMask ShapeCombineMask;
|
||||||
} xshape;
|
} xshape;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
int pipe[2];
|
||||||
|
} eventLoopData;
|
||||||
} _GLFWlibraryX11;
|
} _GLFWlibraryX11;
|
||||||
|
|
||||||
// X11-specific per-monitor data
|
// X11-specific per-monitor data
|
||||||
|
@ -57,6 +57,16 @@
|
|||||||
#define _GLFW_XDND_VERSION 5
|
#define _GLFW_XDND_VERSION 5
|
||||||
|
|
||||||
|
|
||||||
|
static inline void drainPipe(int fd)
|
||||||
|
{
|
||||||
|
static char buf[256];
|
||||||
|
ssize_t len;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
len = read(fd, buf, sizeof(buf));
|
||||||
|
} while (len > 0 || (len < 0 && errno == EINTR));
|
||||||
|
}
|
||||||
|
|
||||||
// Wait for data to arrive using select
|
// Wait for data to arrive using select
|
||||||
// This avoids blocking other threads via the per-display Xlib lock that also
|
// This avoids blocking other threads via the per-display Xlib lock that also
|
||||||
// covers GLX functions
|
// covers GLX functions
|
||||||
@ -65,16 +75,19 @@ static GLFWbool waitForEvent(double* timeout)
|
|||||||
{
|
{
|
||||||
fd_set fds;
|
fd_set fds;
|
||||||
const int fd = ConnectionNumber(_glfw.x11.display);
|
const int fd = ConnectionNumber(_glfw.x11.display);
|
||||||
int count = fd + 1;
|
const int pipe = _glfw.x11.eventLoopData.pipe[0];
|
||||||
|
int max = fd > pipe ? fd : pipe;
|
||||||
|
|
||||||
#if defined(__linux__)
|
#if defined(__linux__)
|
||||||
if (_glfw.linjs.inotify > fd)
|
if (_glfw.linjs.inotify > max)
|
||||||
count = _glfw.linjs.inotify + 1;
|
max = _glfw.linjs.inotify;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
FD_ZERO(&fds);
|
FD_ZERO(&fds);
|
||||||
FD_SET(fd, &fds);
|
FD_SET(fd, &fds);
|
||||||
|
FD_SET(pipe, &fds);
|
||||||
#if defined(__linux__)
|
#if defined(__linux__)
|
||||||
if (_glfw.linjs.inotify > 0)
|
if (_glfw.linjs.inotify > 0)
|
||||||
FD_SET(_glfw.linjs.inotify, &fds);
|
FD_SET(_glfw.linjs.inotify, &fds);
|
||||||
@ -87,9 +100,12 @@ static GLFWbool waitForEvent(double* timeout)
|
|||||||
struct timeval tv = { seconds, microseconds };
|
struct timeval tv = { seconds, microseconds };
|
||||||
const uint64_t base = _glfwPlatformGetTimerValue();
|
const uint64_t base = _glfwPlatformGetTimerValue();
|
||||||
|
|
||||||
const int result = select(count, &fds, NULL, NULL, &tv);
|
const int result = select(max + 1, &fds, NULL, NULL, &tv);
|
||||||
const int error = errno;
|
const int error = errno;
|
||||||
|
|
||||||
|
if (FD_ISSET(pipe, &fds))
|
||||||
|
drainPipe(pipe);
|
||||||
|
|
||||||
*timeout -= (_glfwPlatformGetTimerValue() - base) /
|
*timeout -= (_glfwPlatformGetTimerValue() - base) /
|
||||||
(double) _glfwPlatformGetTimerFrequency();
|
(double) _glfwPlatformGetTimerFrequency();
|
||||||
|
|
||||||
@ -98,8 +114,17 @@ static GLFWbool waitForEvent(double* timeout)
|
|||||||
if ((result == -1 && error == EINTR) || *timeout <= 0.0)
|
if ((result == -1 && error == EINTR) || *timeout <= 0.0)
|
||||||
return GLFW_FALSE;
|
return GLFW_FALSE;
|
||||||
}
|
}
|
||||||
else if (select(count, &fds, NULL, NULL, NULL) != -1 || errno != EINTR)
|
else
|
||||||
return GLFW_TRUE;
|
{
|
||||||
|
const int result = select(max + 1, &fds, NULL, NULL, NULL);
|
||||||
|
const int error = errno;
|
||||||
|
|
||||||
|
if (FD_ISSET(pipe, &fds))
|
||||||
|
drainPipe(pipe);
|
||||||
|
|
||||||
|
if (result != -1 || error != EINTR)
|
||||||
|
return GLFW_TRUE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2805,6 +2830,12 @@ void _glfwPostEmptyEventX11(void)
|
|||||||
|
|
||||||
XSendEvent(_glfw.x11.display, _glfw.x11.helperWindowHandle, False, 0, &event);
|
XSendEvent(_glfw.x11.display, _glfw.x11.helperWindowHandle, False, 0, &event);
|
||||||
XFlush(_glfw.x11.display);
|
XFlush(_glfw.x11.display);
|
||||||
|
|
||||||
|
int result;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
result = write(_glfw.x11.eventLoopData.pipe[1], "x", 1);
|
||||||
|
} while (result < 0 && errno == EINTR);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _glfwGetCursorPosX11(_GLFWwindow* window, double* xpos, double* ypos)
|
void _glfwGetCursorPosX11(_GLFWwindow* window, double* xpos, double* ypos)
|
||||||
|
Loading…
Reference in New Issue
Block a user