render: don’t call term_arm_blink_timer() from multiple threads

We call term_arm_blink_timer() from render_cell(), which runs in
multiple threads.

This caused multiple blink timer FDs to be created and registered with
the FDM, later causing read failures after one of those FDs had been
closed again.

This rarely happened under normal circumstances, but was easy to
trigger when the whole screen was full of blinking text.

As a small optimization, we don’t bother taking the lock if the timer
FD already is valid.

This is safe, because:

1) If the timer FD isn’t valid, we take the lock and then call
    term_arm_blink_timer(), which again checks if the FD is already
    valid.

2) The blink timer FD cannot be closed while we’re rendering cells. It
   is only disabled in the FDM callback, which cannot execute while
   we’re rendering.
This commit is contained in:
Daniel Eklöf 2020-11-23 19:26:00 +01:00
parent 275f97381d
commit 90edc09697
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F

View file

@ -487,8 +487,12 @@ render_cell(struct terminal *term, pixman_image_t *pix,
PIXMAN_OP_SRC, pix, &bg, 1,
&(pixman_rectangle16_t){x, y, cell_cols * width, height});
if (cell->attrs.blink)
if (cell->attrs.blink && term->blink.fd < 0) {
/* TODO: use a custom lock for this? */
mtx_lock(&term->render.workers.lock);
term_arm_blink_timer(term);
mtx_unlock(&term->render.workers.lock);
}
if (has_cursor && term->cursor_style == CURSOR_BLOCK && term->kbd_focus)
draw_cursor(term, cell, font, pix, &fg, &bg, x, y, cell_cols);
@ -499,7 +503,6 @@ render_cell(struct terminal *term, pixman_image_t *pix,
pixman_image_t *clr_pix = pixman_image_create_solid_fill(&fg);
if (glyph != NULL) {
/* Clip to cell */
if (unlikely(pixman_image_get_format(glyph->pix) == PIXMAN_a8r8g8b8)) {
/* Glyph surface is a pre-rendered image (typically a color emoji...) */
if (!(cell->attrs.blink && term->blink.state == BLINK_OFF)) {