Fix GHOST/Wayland thread-unsafe key-repeat timer checks
Resolve a thread safety issue reported by valgrind's helgrind checker, although I wasn't able to redo the error in practice. NULL check on the key-repeat timer also needs to lock, otherwise it's possible the timer is set in another thread before the lock is acquired. Now all key-repeat timer access which may run from a thread locks the timer mutex before any checks or timer manipulation.
This commit is contained in:
parent
b642dc7bc7
commit
d3949a4fdb
|
@ -768,7 +768,12 @@ struct GWL_Seat {
|
|||
int32_t rate = 0;
|
||||
/** Time (milliseconds) after which to start repeating keys. */
|
||||
int32_t delay = 0;
|
||||
/** Timer for key repeats. */
|
||||
/**
|
||||
* Timer for key repeats.
|
||||
*
|
||||
* \note For as long as #USE_EVENT_BACKGROUND_THREAD is defined, any access to this
|
||||
* (including null checks, must lock `timer_mutex` first.
|
||||
*/
|
||||
GHOST_ITimerTask *timer = nullptr;
|
||||
} key_repeat;
|
||||
|
||||
|
@ -832,6 +837,30 @@ static bool gwl_seat_key_depressed_suppress_warning(const GWL_Seat *seat)
|
|||
return suppress_warning;
|
||||
}
|
||||
|
||||
/**
|
||||
* \note Caller must lock `timer_mutex`.
|
||||
*/
|
||||
static void gwl_seat_key_repeat_timer_add(GWL_Seat *seat,
|
||||
GHOST_TimerProcPtr key_repeat_fn,
|
||||
GHOST_TUserDataPtr payload,
|
||||
const bool use_delay)
|
||||
{
|
||||
GHOST_SystemWayland *system = seat->system;
|
||||
const uint64_t time_step = 1000 / seat->key_repeat.rate;
|
||||
const uint64_t time_start = use_delay ? seat->key_repeat.delay : time_step;
|
||||
seat->key_repeat.timer = system->installTimer(time_start, time_step, key_repeat_fn, payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* \note The caller must lock `timer_mutex`.
|
||||
*/
|
||||
static void gwl_seat_key_repeat_timer_remove(GWL_Seat *seat)
|
||||
{
|
||||
GHOST_SystemWayland *system = seat->system;
|
||||
system->removeTimer(seat->key_repeat.timer);
|
||||
seat->key_repeat.timer = nullptr;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
@ -3718,9 +3747,14 @@ static void keyboard_handle_leave(void *data,
|
|||
GWL_Seat *seat = static_cast<GWL_Seat *>(data);
|
||||
seat->keyboard.wl_surface_window = nullptr;
|
||||
|
||||
/* Losing focus must stop repeating text. */
|
||||
if (seat->key_repeat.timer) {
|
||||
keyboard_handle_key_repeat_cancel(seat);
|
||||
{
|
||||
#ifdef USE_EVENT_BACKGROUND_THREAD
|
||||
std::lock_guard lock_timer_guard{*seat->system->timer_mutex};
|
||||
#endif
|
||||
/* Losing focus must stop repeating text. */
|
||||
if (seat->key_repeat.timer) {
|
||||
keyboard_handle_key_repeat_cancel(seat);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_GNOME_KEYBOARD_SUPPRESS_WARNING
|
||||
|
@ -3780,36 +3814,32 @@ static xkb_keysym_t xkb_state_key_get_one_sym_without_modifiers(
|
|||
return sym;
|
||||
}
|
||||
|
||||
/**
|
||||
* \note Caller must lock `timer_mutex`.
|
||||
*/
|
||||
static void keyboard_handle_key_repeat_cancel(GWL_Seat *seat)
|
||||
{
|
||||
#ifdef USE_EVENT_BACKGROUND_THREAD
|
||||
std::lock_guard lock_timer_guard{*seat->system->timer_mutex};
|
||||
#endif
|
||||
GHOST_ASSERT(seat->key_repeat.timer != nullptr, "Caller much check for timer");
|
||||
delete static_cast<GWL_KeyRepeatPlayload *>(seat->key_repeat.timer->getUserData());
|
||||
seat->system->removeTimer(seat->key_repeat.timer);
|
||||
seat->key_repeat.timer = nullptr;
|
||||
|
||||
gwl_seat_key_repeat_timer_remove(seat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restart the key-repeat timer.
|
||||
* \param use_delay: When false, use the interval
|
||||
* (prevents pause when the setting changes while the key is held).
|
||||
*
|
||||
* \note Caller must lock `timer_mutex`.
|
||||
*/
|
||||
static void keyboard_handle_key_repeat_reset(GWL_Seat *seat, const bool use_delay)
|
||||
{
|
||||
#ifdef USE_EVENT_BACKGROUND_THREAD
|
||||
std::lock_guard lock_timer_guard{*seat->system->timer_mutex};
|
||||
#endif
|
||||
GHOST_ASSERT(seat->key_repeat.timer != nullptr, "Caller much check for timer");
|
||||
GHOST_SystemWayland *system = seat->system;
|
||||
GHOST_ITimerTask *timer = seat->key_repeat.timer;
|
||||
GHOST_TimerProcPtr key_repeat_fn = timer->getTimerProc();
|
||||
GHOST_TimerProcPtr key_repeat_fn = seat->key_repeat.timer->getTimerProc();
|
||||
GHOST_TUserDataPtr payload = seat->key_repeat.timer->getUserData();
|
||||
seat->system->removeTimer(seat->key_repeat.timer);
|
||||
const uint64_t time_step = 1000 / seat->key_repeat.rate;
|
||||
const uint64_t time_start = use_delay ? seat->key_repeat.delay : time_step;
|
||||
seat->key_repeat.timer = system->installTimer(time_start, time_step, key_repeat_fn, payload);
|
||||
|
||||
gwl_seat_key_repeat_timer_remove(seat);
|
||||
gwl_seat_key_repeat_timer_add(seat, key_repeat_fn, payload, use_delay);
|
||||
}
|
||||
|
||||
static void keyboard_handle_key(void *data,
|
||||
|
@ -3848,6 +3878,11 @@ static void keyboard_handle_key(void *data,
|
|||
break;
|
||||
}
|
||||
|
||||
#ifdef USE_EVENT_BACKGROUND_THREAD
|
||||
/* Any access to `seat->key_repeat.timer` must lock. */
|
||||
std::lock_guard lock_timer_guard{*seat->system->timer_mutex};
|
||||
#endif
|
||||
|
||||
struct GWL_KeyRepeatPlayload *key_repeat_payload = nullptr;
|
||||
|
||||
/* Delete previous timer. */
|
||||
|
@ -3886,23 +3921,14 @@ static void keyboard_handle_key(void *data,
|
|||
break;
|
||||
}
|
||||
case RESET: {
|
||||
#ifdef USE_EVENT_BACKGROUND_THREAD
|
||||
std::lock_guard lock_timer_guard{*seat->system->timer_mutex};
|
||||
#endif
|
||||
/* The payload will be added again. */
|
||||
seat->system->removeTimer(seat->key_repeat.timer);
|
||||
seat->key_repeat.timer = nullptr;
|
||||
gwl_seat_key_repeat_timer_remove(seat);
|
||||
break;
|
||||
}
|
||||
case CANCEL: {
|
||||
#ifdef USE_EVENT_BACKGROUND_THREAD
|
||||
std::lock_guard lock_timer_guard{*seat->system->timer_mutex};
|
||||
#endif
|
||||
delete key_repeat_payload;
|
||||
key_repeat_payload = nullptr;
|
||||
|
||||
seat->system->removeTimer(seat->key_repeat.timer);
|
||||
seat->key_repeat.timer = nullptr;
|
||||
gwl_seat_key_repeat_timer_remove(seat);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -3956,8 +3982,8 @@ static void keyboard_handle_key(void *data,
|
|||
utf8_buf));
|
||||
}
|
||||
};
|
||||
seat->key_repeat.timer = seat->system->installTimer(
|
||||
seat->key_repeat.delay, 1000 / seat->key_repeat.rate, key_repeat_fn, key_repeat_payload);
|
||||
|
||||
gwl_seat_key_repeat_timer_add(seat, key_repeat_fn, key_repeat_payload, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3982,8 +4008,13 @@ static void keyboard_handle_modifiers(void *data,
|
|||
|
||||
/* A modifier changed so reset the timer,
|
||||
* see comment in #keyboard_handle_key regarding this behavior. */
|
||||
if (seat->key_repeat.timer) {
|
||||
keyboard_handle_key_repeat_reset(seat, true);
|
||||
{
|
||||
#ifdef USE_EVENT_BACKGROUND_THREAD
|
||||
std::lock_guard lock_timer_guard{*seat->system->timer_mutex};
|
||||
#endif
|
||||
if (seat->key_repeat.timer) {
|
||||
keyboard_handle_key_repeat_reset(seat, true);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_GNOME_KEYBOARD_SUPPRESS_WARNING
|
||||
|
@ -4002,9 +4033,14 @@ static void keyboard_repeat_handle_info(void *data,
|
|||
seat->key_repeat.rate = rate;
|
||||
seat->key_repeat.delay = delay;
|
||||
|
||||
/* Unlikely possible this setting changes while repeating. */
|
||||
if (seat->key_repeat.timer) {
|
||||
keyboard_handle_key_repeat_reset(seat, false);
|
||||
{
|
||||
#ifdef USE_EVENT_BACKGROUND_THREAD
|
||||
std::lock_guard lock_timer_guard{*seat->system->timer_mutex};
|
||||
#endif
|
||||
/* Unlikely possible this setting changes while repeating. */
|
||||
if (seat->key_repeat.timer) {
|
||||
keyboard_handle_key_repeat_reset(seat, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4275,8 +4311,14 @@ static void gwl_seat_capability_keyboard_disable(GWL_Seat *seat)
|
|||
if (!seat->wl_keyboard) {
|
||||
return;
|
||||
}
|
||||
if (seat->key_repeat.timer) {
|
||||
keyboard_handle_key_repeat_cancel(seat);
|
||||
|
||||
{
|
||||
#ifdef USE_EVENT_BACKGROUND_THREAD
|
||||
std::lock_guard lock_timer_guard{*seat->system->timer_mutex};
|
||||
#endif
|
||||
if (seat->key_repeat.timer) {
|
||||
keyboard_handle_key_repeat_cancel(seat);
|
||||
}
|
||||
}
|
||||
wl_keyboard_destroy(seat->wl_keyboard);
|
||||
seat->wl_keyboard = nullptr;
|
||||
|
|
Loading…
Reference in New Issue