Make handling of windows that don't respond to _NET_WM_SYNC_REQUEST reliable

Previously, we were handling failure to respond to _NET_WM_SYNC_REQUEST
in the code path for throttling motion events. But this meant that
if a window didn't respond to _NET_WM_SYNC_REQUEST and there were no
motion events - for a keyboard resize, or after the end of the grab
operation - it would end up in a stuck state.

Use a separate per-window timeout to reliably catch the failure to respond
to _NET_WM_SYNC_REQUEST.

https://bugzilla.gnome.org/show_bug.cgi?id=694046
This commit is contained in:
Owen W. Taylor 2013-03-13 15:31:11 -04:00
parent 592374bc62
commit 97a4cc8c9b
3 changed files with 73 additions and 74 deletions

View File

@ -3962,8 +3962,6 @@ meta_display_begin_grab_op (MetaDisplay *display,
display->grab_window->sync_request_counter != None) display->grab_window->sync_request_counter != None)
{ {
meta_window_create_sync_request_alarm (display->grab_window); meta_window_create_sync_request_alarm (display->grab_window);
window->sync_request_time.tv_sec = 0;
window->sync_request_time.tv_usec = 0;
} }
#endif #endif
} }

View File

@ -364,7 +364,7 @@ struct _MetaWindow
XSyncCounter sync_request_counter; XSyncCounter sync_request_counter;
gint64 sync_request_serial; gint64 sync_request_serial;
gint64 sync_request_wait_serial; gint64 sync_request_wait_serial;
GTimeVal sync_request_time; guint sync_request_timeout_id;
/* alarm monitoring client's _NET_WM_SYNC_REQUEST_COUNTER */ /* alarm monitoring client's _NET_WM_SYNC_REQUEST_COUNTER */
XSyncAlarm sync_request_alarm; XSyncAlarm sync_request_alarm;
#endif #endif

View File

@ -1020,8 +1020,7 @@ meta_window_new_with_attrs (MetaDisplay *display,
#ifdef HAVE_XSYNC #ifdef HAVE_XSYNC
window->sync_request_counter = None; window->sync_request_counter = None;
window->sync_request_serial = 0; window->sync_request_serial = 0;
window->sync_request_time.tv_sec = 0; window->sync_request_timeout_id = 0;
window->sync_request_time.tv_usec = 0;
window->sync_request_alarm = None; window->sync_request_alarm = None;
#endif #endif
@ -1774,6 +1773,12 @@ meta_window_unmanage (MetaWindow *window,
invalidate_work_areas (window); invalidate_work_areas (window);
} }
if (window->sync_request_timeout_id)
{
g_source_remove (window->sync_request_timeout_id);
window->sync_request_timeout_id = 0;
}
if (window->display->grab_window == window) if (window->display->grab_window == window)
meta_display_end_grab_op (window->display, timestamp); meta_display_end_grab_op (window->display, timestamp);
@ -4616,6 +4621,38 @@ meta_window_destroy_sync_request_alarm (MetaWindow *window)
} }
#ifdef HAVE_XSYNC #ifdef HAVE_XSYNC
static gboolean
sync_request_timeout (gpointer data)
{
MetaWindow *window = data;
window->sync_request_timeout_id = 0;
/* We have now waited for more than a second for the
* application to respond to the sync request
*/
window->disable_sync = TRUE;
/* Reset the wait serial, so we don't continue freezing
* window updates
*/
window->sync_request_wait_serial = 0;
meta_compositor_set_updates_frozen (window->display->compositor, window,
meta_window_updates_are_frozen (window));
if (window == window->display->grab_window &&
meta_grab_op_is_resizing (window->display->grab_op))
{
update_resize (window,
window->display->grab_last_user_action_was_snap,
window->display->grab_latest_motion_x,
window->display->grab_latest_motion_y,
TRUE);
}
return FALSE;
}
static void static void
send_sync_request (MetaWindow *window) send_sync_request (MetaWindow *window)
{ {
@ -4654,7 +4691,13 @@ send_sync_request (MetaWindow *window)
XSendEvent (window->display->xdisplay, XSendEvent (window->display->xdisplay,
window->xwindow, False, 0, (XEvent*) &ev); window->xwindow, False, 0, (XEvent*) &ev);
g_get_current_time (&window->sync_request_time); /* We give the window 1 sec to respond to _NET_WM_SYNC_REQUEST;
* if this time expires, we consider the window unresponsive
* and resize it unsynchonized.
*/
window->sync_request_timeout_id = g_timeout_add (1000,
sync_request_timeout,
window);
meta_compositor_set_updates_frozen (window->display->compositor, window, meta_compositor_set_updates_frozen (window->display->compositor, window,
meta_window_updates_are_frozen (window)); meta_window_updates_are_frozen (window));
@ -5206,8 +5249,7 @@ meta_window_move_resize_internal (MetaWindow *window,
!window->disable_sync && !window->disable_sync &&
window->sync_request_counter != None && window->sync_request_counter != None &&
window->sync_request_alarm != None && window->sync_request_alarm != None &&
window->sync_request_time.tv_usec == 0 && window->sync_request_timeout_id == 0)
window->sync_request_time.tv_sec == 0)
{ {
send_sync_request (window); send_sync_request (window);
} }
@ -8821,81 +8863,39 @@ check_moveresize_frequency (MetaWindow *window,
gdouble *remaining) gdouble *remaining)
{ {
GTimeVal current_time; GTimeVal current_time;
const double max_resizes_per_second = 25.0;
const double ms_between_resizes = 1000.0 / max_resizes_per_second;
double elapsed;
g_get_current_time (&current_time); g_get_current_time (&current_time);
#ifdef HAVE_XSYNC #ifdef HAVE_XSYNC
/* If we are throttling via _NET_WM_SYNC_REQUEST, we don't need
* an artificial timeout-based throttled */
if (!window->disable_sync && if (!window->disable_sync &&
window->sync_request_alarm != None) window->sync_request_alarm != None)
{ return TRUE;
if (window->sync_request_time.tv_sec != 0 ||
window->sync_request_time.tv_usec != 0)
{
double elapsed =
time_diff (&current_time, &window->sync_request_time);
if (elapsed < 1000.0)
{
/* We want to be sure that the timeout happens at
* a time where elapsed will definitely be
* greater than 1000, so we can disable sync
*/
if (remaining)
*remaining = 1000.0 - elapsed + 100;
return FALSE;
}
else
{
/* We have now waited for more than a second for the
* application to respond to the sync request
*/
window->disable_sync = TRUE;
/* Reset the wait serial, so we don't continue freezing
* window updates
*/
window->sync_request_wait_serial = 0;
meta_compositor_set_updates_frozen (window->display->compositor, window,
meta_window_updates_are_frozen (window));
return TRUE;
}
}
else
{
/* No outstanding sync requests. Go ahead and resize
*/
return TRUE;
}
}
else
#endif /* HAVE_XSYNC */ #endif /* HAVE_XSYNC */
elapsed = time_diff (&current_time, &window->display->grab_last_moveresize_time);
if (elapsed >= 0.0 && elapsed < ms_between_resizes)
{ {
const double max_resizes_per_second = 25.0;
const double ms_between_resizes = 1000.0 / max_resizes_per_second;
double elapsed;
elapsed = time_diff (&current_time, &window->display->grab_last_moveresize_time);
if (elapsed >= 0.0 && elapsed < ms_between_resizes)
{
meta_topic (META_DEBUG_RESIZING,
"Delaying move/resize as only %g of %g ms elapsed\n",
elapsed, ms_between_resizes);
if (remaining)
*remaining = (ms_between_resizes - elapsed);
return FALSE;
}
meta_topic (META_DEBUG_RESIZING, meta_topic (META_DEBUG_RESIZING,
" Checked moveresize freq, allowing move/resize now (%g of %g seconds elapsed)\n", "Delaying move/resize as only %g of %g ms elapsed\n",
elapsed / 1000.0, 1.0 / max_resizes_per_second); elapsed, ms_between_resizes);
return TRUE; if (remaining)
*remaining = (ms_between_resizes - elapsed);
return FALSE;
} }
meta_topic (META_DEBUG_RESIZING,
" Checked moveresize freq, allowing move/resize now (%g of %g seconds elapsed)\n",
elapsed / 1000.0, 1.0 / max_resizes_per_second);
return TRUE;
} }
static gboolean static gboolean
@ -9625,8 +9625,9 @@ meta_window_update_sync_request_counter (MetaWindow *window,
* busy with a pagefault or a long computation). * busy with a pagefault or a long computation).
*/ */
window->disable_sync = FALSE; window->disable_sync = FALSE;
window->sync_request_time.tv_sec = 0;
window->sync_request_time.tv_usec = 0; g_source_remove (window->sync_request_timeout_id);
window->sync_request_timeout_id = 0;
/* This means we are ready for another configure; /* This means we are ready for another configure;
* no pointer round trip here, to keep in sync */ * no pointer round trip here, to keep in sync */