mirror of
https://github.com/freebsd/freebsd-src
synced 2024-09-06 17:18:32 +00:00
teken, vt(4): New callbacks to lock the terminal once
... to process input, instead of inside each smaller operations such as appending a character or moving the cursor forward. In other words, before we were doing (oversimplified): teken_input() <for each input character> vtterm_putchar() VTBUF_LOCK() VTBUF_UNLOCK() vtterm_cursor_position() VTBUF_LOCK() VTBUF_UNLOCK() Now, we are doing: vtterm_pre_input() VTBUF_LOCK() teken_input() <for each input character> vtterm_putchar() vtterm_cursor_position() vtterm_post_input() VTBUF_UNLOCK() The situation was even worse when the vtterm_copy() and vtterm_fill() callbacks were involved. The new callbacks are: * struct terminal_class->tc_pre_input() * struct terminal_class->tc_post_input() They are called in teken_input(), surrounding the while() loop. The goal is to improve input processing speed of vt(4). As a benchmark, here is the time taken to write a text file of 360 000 lines (26 MiB) on `ttyv0`: * vt(4), unmodified: 1500 ms * vt(4), with this patch: 1200 ms * syscons(4): 700 ms This is on a Haswell laptop with a GENERIC-NODEBUG kernel. At the same time, the locking is changed in the vt_flush() function which is responsible to draw the text on screen. So instead of (indirectly) using VTBUF_LOCK() just to read and reset the dirty area of the internal buffer, the lock is held for about the entire function, including the drawing part. The change is mostly visible while content is scrolling fast: before, lines could appear garbled while scrolling because the internal buffer was accessed without locks (once the scrolling was finished, the output was correct). Now, the scrolling appears correct. In the end, the locking model is closer to what syscons(4) does. Differential Revision: https://reviews.freebsd.org/D15302
This commit is contained in:
parent
c9c4d38aa8
commit
547e74a8be
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=333669
|
@ -213,8 +213,10 @@ struct vt_buf {
|
|||
#define VBF_DEFAULT_HISTORY_SIZE 500
|
||||
#endif
|
||||
|
||||
void vtbuf_lock(struct vt_buf *);
|
||||
void vtbuf_unlock(struct vt_buf *);
|
||||
void vtbuf_copy(struct vt_buf *, const term_rect_t *, const term_pos_t *);
|
||||
void vtbuf_fill_locked(struct vt_buf *, const term_rect_t *, term_char_t);
|
||||
void vtbuf_fill(struct vt_buf *, const term_rect_t *, term_char_t);
|
||||
void vtbuf_init_early(struct vt_buf *);
|
||||
void vtbuf_init(struct vt_buf *, const term_pos_t *);
|
||||
void vtbuf_grow(struct vt_buf *, const term_pos_t *, unsigned int);
|
||||
|
|
|
@ -253,8 +253,22 @@ vtbuf_iscursor(const struct vt_buf *vb, int row, int col)
|
|||
return (0);
|
||||
}
|
||||
|
||||
static inline void
|
||||
vtbuf_dirty_locked(struct vt_buf *vb, const term_rect_t *area)
|
||||
void
|
||||
vtbuf_lock(struct vt_buf *vb)
|
||||
{
|
||||
|
||||
VTBUF_LOCK(vb);
|
||||
}
|
||||
|
||||
void
|
||||
vtbuf_unlock(struct vt_buf *vb)
|
||||
{
|
||||
|
||||
VTBUF_UNLOCK(vb);
|
||||
}
|
||||
|
||||
void
|
||||
vtbuf_dirty(struct vt_buf *vb, const term_rect_t *area)
|
||||
{
|
||||
|
||||
if (vb->vb_dirtyrect.tr_begin.tp_row > area->tr_begin.tp_row)
|
||||
|
@ -267,24 +281,15 @@ vtbuf_dirty_locked(struct vt_buf *vb, const term_rect_t *area)
|
|||
vb->vb_dirtyrect.tr_end.tp_col = area->tr_end.tp_col;
|
||||
}
|
||||
|
||||
void
|
||||
vtbuf_dirty(struct vt_buf *vb, const term_rect_t *area)
|
||||
{
|
||||
|
||||
VTBUF_LOCK(vb);
|
||||
vtbuf_dirty_locked(vb, area);
|
||||
VTBUF_UNLOCK(vb);
|
||||
}
|
||||
|
||||
static inline void
|
||||
vtbuf_dirty_cell_locked(struct vt_buf *vb, const term_pos_t *p)
|
||||
vtbuf_dirty_cell(struct vt_buf *vb, const term_pos_t *p)
|
||||
{
|
||||
term_rect_t area;
|
||||
|
||||
area.tr_begin = *p;
|
||||
area.tr_end.tp_row = p->tp_row + 1;
|
||||
area.tr_end.tp_col = p->tp_col + 1;
|
||||
vtbuf_dirty_locked(vb, &area);
|
||||
vtbuf_dirty(vb, &area);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -299,10 +304,8 @@ void
|
|||
vtbuf_undirty(struct vt_buf *vb, term_rect_t *r)
|
||||
{
|
||||
|
||||
VTBUF_LOCK(vb);
|
||||
*r = vb->vb_dirtyrect;
|
||||
vtbuf_make_undirty(vb);
|
||||
VTBUF_UNLOCK(vb);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -366,7 +369,7 @@ vtbuf_copy(struct vt_buf *vb, const term_rect_t *r, const term_pos_t *p2)
|
|||
}
|
||||
|
||||
static void
|
||||
vtbuf_fill(struct vt_buf *vb, const term_rect_t *r, term_char_t c)
|
||||
vtbuf_do_fill(struct vt_buf *vb, const term_rect_t *r, term_char_t c)
|
||||
{
|
||||
unsigned int pr, pc;
|
||||
term_char_t *row;
|
||||
|
@ -381,26 +384,25 @@ vtbuf_fill(struct vt_buf *vb, const term_rect_t *r, term_char_t c)
|
|||
}
|
||||
|
||||
void
|
||||
vtbuf_fill_locked(struct vt_buf *vb, const term_rect_t *r, term_char_t c)
|
||||
vtbuf_fill(struct vt_buf *vb, const term_rect_t *r, term_char_t c)
|
||||
{
|
||||
|
||||
KASSERT(r->tr_begin.tp_row < vb->vb_scr_size.tp_row,
|
||||
("vtbuf_fill_locked begin.tp_row %d must be < screen height %d",
|
||||
("vtbuf_fill begin.tp_row %d must be < screen height %d",
|
||||
r->tr_begin.tp_row, vb->vb_scr_size.tp_row));
|
||||
KASSERT(r->tr_begin.tp_col < vb->vb_scr_size.tp_col,
|
||||
("vtbuf_fill_locked begin.tp_col %d must be < screen width %d",
|
||||
("vtbuf_fill begin.tp_col %d must be < screen width %d",
|
||||
r->tr_begin.tp_col, vb->vb_scr_size.tp_col));
|
||||
|
||||
KASSERT(r->tr_end.tp_row <= vb->vb_scr_size.tp_row,
|
||||
("vtbuf_fill_locked end.tp_row %d must be <= screen height %d",
|
||||
("vtbuf_fill end.tp_row %d must be <= screen height %d",
|
||||
r->tr_end.tp_row, vb->vb_scr_size.tp_row));
|
||||
KASSERT(r->tr_end.tp_col <= vb->vb_scr_size.tp_col,
|
||||
("vtbuf_fill_locked end.tp_col %d must be <= screen width %d",
|
||||
("vtbuf_fill end.tp_col %d must be <= screen width %d",
|
||||
r->tr_end.tp_col, vb->vb_scr_size.tp_col));
|
||||
|
||||
VTBUF_LOCK(vb);
|
||||
vtbuf_fill(vb, r, c);
|
||||
vtbuf_dirty_locked(vb, r);
|
||||
VTBUF_UNLOCK(vb);
|
||||
vtbuf_do_fill(vb, r, c);
|
||||
vtbuf_dirty(vb, r);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -431,7 +433,7 @@ vtbuf_init_early(struct vt_buf *vb)
|
|||
rect.tr_begin.tp_row = rect.tr_begin.tp_col = 0;
|
||||
rect.tr_end.tp_col = vb->vb_scr_size.tp_col;
|
||||
rect.tr_end.tp_row = vb->vb_history_size;
|
||||
vtbuf_fill(vb, &rect, VTBUF_SPACE_CHAR(TERMINAL_NORM_ATTR));
|
||||
vtbuf_do_fill(vb, &rect, VTBUF_SPACE_CHAR(TERMINAL_NORM_ATTR));
|
||||
vtbuf_make_undirty(vb);
|
||||
if ((vb->vb_flags & VBF_MTX_INIT) == 0) {
|
||||
mtx_init(&vb->vb_lock, "vtbuf", NULL, MTX_SPIN);
|
||||
|
@ -645,23 +647,18 @@ vtbuf_putchar(struct vt_buf *vb, const term_pos_t *p, term_char_t c)
|
|||
row = vb->vb_rows[(vb->vb_curroffset + p->tp_row) %
|
||||
VTBUF_MAX_HEIGHT(vb)];
|
||||
if (row[p->tp_col] != c) {
|
||||
VTBUF_LOCK(vb);
|
||||
row[p->tp_col] = c;
|
||||
vtbuf_dirty_cell_locked(vb, p);
|
||||
VTBUF_UNLOCK(vb);
|
||||
vtbuf_dirty_cell(vb, p);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
vtbuf_cursor_position(struct vt_buf *vb, const term_pos_t *p)
|
||||
{
|
||||
|
||||
if (vb->vb_flags & VBF_CURSOR) {
|
||||
VTBUF_LOCK(vb);
|
||||
vtbuf_dirty_cell_locked(vb, &vb->vb_cursor);
|
||||
vtbuf_dirty_cell(vb, &vb->vb_cursor);
|
||||
vb->vb_cursor = *p;
|
||||
vtbuf_dirty_cell_locked(vb, &vb->vb_cursor);
|
||||
VTBUF_UNLOCK(vb);
|
||||
vtbuf_dirty_cell(vb, &vb->vb_cursor);
|
||||
} else {
|
||||
vb->vb_cursor = *p;
|
||||
}
|
||||
|
@ -687,7 +684,9 @@ vtbuf_flush_mark(struct vt_buf *vb)
|
|||
area.tr_end.tp_col = vb->vb_scr_size.tp_col;
|
||||
area.tr_end.tp_row = MAX(s, e) + 1;
|
||||
|
||||
VTBUF_LOCK(vb);
|
||||
vtbuf_dirty(vb, &area);
|
||||
VTBUF_UNLOCK(vb);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -823,7 +822,6 @@ vtbuf_cursor_visibility(struct vt_buf *vb, int yes)
|
|||
{
|
||||
int oflags, nflags;
|
||||
|
||||
VTBUF_LOCK(vb);
|
||||
oflags = vb->vb_flags;
|
||||
if (yes)
|
||||
vb->vb_flags |= VBF_CURSOR;
|
||||
|
@ -832,8 +830,7 @@ vtbuf_cursor_visibility(struct vt_buf *vb, int yes)
|
|||
nflags = vb->vb_flags;
|
||||
|
||||
if (oflags != nflags)
|
||||
vtbuf_dirty_cell_locked(vb, &vb->vb_cursor);
|
||||
VTBUF_UNLOCK(vb);
|
||||
vtbuf_dirty_cell(vb, &vb->vb_cursor);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -850,7 +847,6 @@ vtbuf_scroll_mode(struct vt_buf *vb, int yes)
|
|||
nflags = vb->vb_flags;
|
||||
|
||||
if (oflags != nflags)
|
||||
vtbuf_dirty_cell_locked(vb, &vb->vb_cursor);
|
||||
vtbuf_dirty_cell(vb, &vb->vb_cursor);
|
||||
VTBUF_UNLOCK(vb);
|
||||
}
|
||||
|
||||
|
|
|
@ -66,6 +66,8 @@ static tc_cursor_t vtterm_cursor;
|
|||
static tc_putchar_t vtterm_putchar;
|
||||
static tc_fill_t vtterm_fill;
|
||||
static tc_copy_t vtterm_copy;
|
||||
static tc_pre_input_t vtterm_pre_input;
|
||||
static tc_post_input_t vtterm_post_input;
|
||||
static tc_param_t vtterm_param;
|
||||
static tc_done_t vtterm_done;
|
||||
|
||||
|
@ -85,6 +87,8 @@ const struct terminal_class vt_termclass = {
|
|||
.tc_putchar = vtterm_putchar,
|
||||
.tc_fill = vtterm_fill,
|
||||
.tc_copy = vtterm_copy,
|
||||
.tc_pre_input = vtterm_pre_input,
|
||||
.tc_post_input = vtterm_post_input,
|
||||
.tc_param = vtterm_param,
|
||||
.tc_done = vtterm_done,
|
||||
|
||||
|
@ -1053,7 +1057,7 @@ vtterm_fill(struct terminal *tm, const term_rect_t *r, term_char_t c)
|
|||
{
|
||||
struct vt_window *vw = tm->tm_softc;
|
||||
|
||||
vtbuf_fill_locked(&vw->vw_buf, r, c);
|
||||
vtbuf_fill(&vw->vw_buf, r, c);
|
||||
vt_resume_flush_timer(vw->vw_device, 0);
|
||||
}
|
||||
|
||||
|
@ -1142,7 +1146,7 @@ vt_is_cursor_in_area(const struct vt_device *vd, const term_rect_t *area)
|
|||
}
|
||||
|
||||
static void
|
||||
vt_mark_mouse_position_as_dirty(struct vt_device *vd)
|
||||
vt_mark_mouse_position_as_dirty(struct vt_device *vd, int locked)
|
||||
{
|
||||
term_rect_t area;
|
||||
struct vt_window *vw;
|
||||
|
@ -1175,7 +1179,11 @@ vt_mark_mouse_position_as_dirty(struct vt_device *vd)
|
|||
area.tr_end.tp_row = y + 2;
|
||||
}
|
||||
|
||||
if (!locked)
|
||||
vtbuf_lock(&vw->vw_buf);
|
||||
vtbuf_dirty(&vw->vw_buf, &area);
|
||||
if (!locked)
|
||||
vtbuf_unlock(&vw->vw_buf);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -1230,6 +1238,8 @@ vt_flush(struct vt_device *vd)
|
|||
if (((vd->vd_flags & VDF_TEXTMODE) == 0) && (vf == NULL))
|
||||
return (0);
|
||||
|
||||
vtbuf_lock(&vw->vw_buf);
|
||||
|
||||
#ifndef SC_NO_CUTPASTE
|
||||
cursor_was_shown = vd->vd_mshown;
|
||||
cursor_moved = (vd->vd_mx != vd->vd_mx_drawn ||
|
||||
|
@ -1250,7 +1260,7 @@ vt_flush(struct vt_device *vd)
|
|||
*/
|
||||
if (cursor_was_shown != vd->vd_mshown ||
|
||||
(vd->vd_mshown && cursor_moved))
|
||||
vt_mark_mouse_position_as_dirty(vd);
|
||||
vt_mark_mouse_position_as_dirty(vd, true);
|
||||
|
||||
/*
|
||||
* Save position of the mouse cursor. It's used by backends to
|
||||
|
@ -1265,7 +1275,7 @@ vt_flush(struct vt_device *vd)
|
|||
* mark the new position as dirty.
|
||||
*/
|
||||
if (vd->vd_mshown && cursor_moved)
|
||||
vt_mark_mouse_position_as_dirty(vd);
|
||||
vt_mark_mouse_position_as_dirty(vd, true);
|
||||
#endif
|
||||
|
||||
vtbuf_undirty(&vw->vw_buf, &tarea);
|
||||
|
@ -1282,9 +1292,11 @@ vt_flush(struct vt_device *vd)
|
|||
|
||||
if (tarea.tr_begin.tp_col < tarea.tr_end.tp_col) {
|
||||
vd->vd_driver->vd_bitblt_text(vd, vw, &tarea);
|
||||
vtbuf_unlock(&vw->vw_buf);
|
||||
return (1);
|
||||
}
|
||||
|
||||
vtbuf_unlock(&vw->vw_buf);
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
@ -1305,6 +1317,23 @@ vt_timer(void *arg)
|
|||
vd->vd_timer_armed = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
vtterm_pre_input(struct terminal *tm)
|
||||
{
|
||||
struct vt_window *vw = tm->tm_softc;
|
||||
|
||||
vtbuf_lock(&vw->vw_buf);
|
||||
}
|
||||
|
||||
static void
|
||||
vtterm_post_input(struct terminal *tm)
|
||||
{
|
||||
struct vt_window *vw = tm->tm_softc;
|
||||
|
||||
vtbuf_unlock(&vw->vw_buf);
|
||||
vt_resume_flush_timer(vw->vw_device, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
vtterm_done(struct terminal *tm)
|
||||
{
|
||||
|
@ -2000,7 +2029,7 @@ vt_mouse_state(int show)
|
|||
}
|
||||
|
||||
/* Mark mouse position as dirty. */
|
||||
vt_mark_mouse_position_as_dirty(vd);
|
||||
vt_mark_mouse_position_as_dirty(vd, false);
|
||||
vt_resume_flush_timer(vw->vw_device, 0);
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -106,6 +106,8 @@ static tf_cursor_t termteken_cursor;
|
|||
static tf_putchar_t termteken_putchar;
|
||||
static tf_fill_t termteken_fill;
|
||||
static tf_copy_t termteken_copy;
|
||||
static tf_pre_input_t termteken_pre_input;
|
||||
static tf_post_input_t termteken_post_input;
|
||||
static tf_param_t termteken_param;
|
||||
static tf_respond_t termteken_respond;
|
||||
|
||||
|
@ -115,6 +117,8 @@ static teken_funcs_t terminal_drawmethods = {
|
|||
.tf_putchar = termteken_putchar,
|
||||
.tf_fill = termteken_fill,
|
||||
.tf_copy = termteken_copy,
|
||||
.tf_pre_input = termteken_pre_input,
|
||||
.tf_post_input = termteken_post_input,
|
||||
.tf_param = termteken_param,
|
||||
.tf_respond = termteken_respond,
|
||||
};
|
||||
|
@ -626,6 +630,22 @@ termteken_copy(void *softc, const teken_rect_t *r, const teken_pos_t *p)
|
|||
tm->tm_class->tc_copy(tm, r, p);
|
||||
}
|
||||
|
||||
static void
|
||||
termteken_pre_input(void *softc)
|
||||
{
|
||||
struct terminal *tm = softc;
|
||||
|
||||
tm->tm_class->tc_pre_input(tm);
|
||||
}
|
||||
|
||||
static void
|
||||
termteken_post_input(void *softc)
|
||||
{
|
||||
struct terminal *tm = softc;
|
||||
|
||||
tm->tm_class->tc_post_input(tm);
|
||||
}
|
||||
|
||||
static void
|
||||
termteken_param(void *softc, int cmd, unsigned int arg)
|
||||
{
|
||||
|
|
|
@ -151,6 +151,8 @@ typedef void tc_fill_t(struct terminal *tm, const term_rect_t *r,
|
|||
term_char_t c);
|
||||
typedef void tc_copy_t(struct terminal *tm, const term_rect_t *r,
|
||||
const term_pos_t *p);
|
||||
typedef void tc_pre_input_t(struct terminal *tm);
|
||||
typedef void tc_post_input_t(struct terminal *tm);
|
||||
typedef void tc_param_t(struct terminal *tm, int cmd, unsigned int arg);
|
||||
typedef void tc_done_t(struct terminal *tm);
|
||||
|
||||
|
@ -173,6 +175,8 @@ struct terminal_class {
|
|||
tc_putchar_t *tc_putchar;
|
||||
tc_fill_t *tc_fill;
|
||||
tc_copy_t *tc_copy;
|
||||
tc_pre_input_t *tc_pre_input;
|
||||
tc_post_input_t *tc_post_input;
|
||||
tc_param_t *tc_param;
|
||||
tc_done_t *tc_done;
|
||||
|
||||
|
|
|
@ -132,6 +132,22 @@ teken_funcs_copy(const teken_t *t, const teken_rect_t *r, const teken_pos_t *p)
|
|||
t->t_funcs->tf_copy(t->t_softc, r, p);
|
||||
}
|
||||
|
||||
static inline void
|
||||
teken_funcs_pre_input(const teken_t *t)
|
||||
{
|
||||
|
||||
teken_assert(t->t_funcs->tf_pre_input != NULL);
|
||||
t->t_funcs->tf_pre_input(t->t_softc);
|
||||
}
|
||||
|
||||
static inline void
|
||||
teken_funcs_post_input(const teken_t *t)
|
||||
{
|
||||
|
||||
teken_assert(t->t_funcs->tf_post_input != NULL);
|
||||
t->t_funcs->tf_post_input(t->t_softc);
|
||||
}
|
||||
|
||||
static inline void
|
||||
teken_funcs_param(const teken_t *t, int cmd, unsigned int value)
|
||||
{
|
||||
|
@ -292,8 +308,10 @@ teken_input(teken_t *t, const void *buf, size_t len)
|
|||
{
|
||||
const char *c = buf;
|
||||
|
||||
teken_funcs_pre_input(t);
|
||||
while (len-- > 0)
|
||||
teken_input_byte(t, *c++);
|
||||
teken_funcs_post_input(t);
|
||||
}
|
||||
|
||||
const teken_pos_t *
|
||||
|
|
|
@ -93,6 +93,8 @@ typedef void tf_putchar_t(void *, const teken_pos_t *, teken_char_t,
|
|||
typedef void tf_fill_t(void *, const teken_rect_t *, teken_char_t,
|
||||
const teken_attr_t *);
|
||||
typedef void tf_copy_t(void *, const teken_rect_t *, const teken_pos_t *);
|
||||
typedef void tf_pre_input_t(void *);
|
||||
typedef void tf_post_input_t(void *);
|
||||
typedef void tf_param_t(void *, int, unsigned int);
|
||||
#define TP_SHOWCURSOR 0
|
||||
#define TP_KEYPADAPP 1
|
||||
|
@ -114,6 +116,8 @@ typedef struct {
|
|||
tf_putchar_t *tf_putchar;
|
||||
tf_fill_t *tf_fill;
|
||||
tf_copy_t *tf_copy;
|
||||
tf_pre_input_t *tf_pre_input;
|
||||
tf_post_input_t *tf_post_input;
|
||||
tf_param_t *tf_param;
|
||||
tf_respond_t *tf_respond;
|
||||
} teken_funcs_t;
|
||||
|
|
Loading…
Reference in a new issue