term: asynchronous writes to slave

Make ptmx non-blocking. Then, when writing data to the slave would
have blocked, use the FDM to asynchronously write the remaining data.

This is done by enabling EPOLLOUT on ptmx, and enqueueing all outgoing
data. The FDM handler will go through the enqueued data, and once all
of it has been written, we turn off EPOLLOUT again (thus switching
back to synchronous writes)
This commit is contained in:
Daniel Eklöf 2019-11-03 01:03:52 +01:00
parent 777d851282
commit f00c5fdac6
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
2 changed files with 106 additions and 21 deletions

View file

@ -26,15 +26,99 @@
#define min(x, y) ((x) < (y) ? (x) : (y))
#define max(x, y) ((x) > (y) ? (x) : (y))
bool
term_to_slave(struct terminal *term, const void *_data, size_t len)
{
if (tll_length(term->ptmx_buffer) > 0)
goto enqueue_data;
const uint8_t *data = _data;
size_t left = len;
while (left > 0) {
ssize_t ret = write(term->ptmx, data, left);
if (ret < 0) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
LOG_ERRNO("failed to write to client");
return false;
}
if (!fdm_event_add(term->fdm, term->ptmx, EPOLLOUT))
return false;
goto enqueue_data;
}
data += ret;
left -= ret;
}
return true;
enqueue_data:
{
void *copy = malloc(len);
memcpy(copy, _data, len);
struct ptmx_buffer queued = {
.data = copy,
.len = len,
.idx = 0,
};
tll_push_back(term->ptmx_buffer, queued);
}
return true;
}
static bool
fdm_ptmx_out(struct fdm *fdm, int fd, int events, void *data)
{
struct terminal *term = data;
assert(tll_length(term->ptmx_buffer) > 0);
tll_foreach(term->ptmx_buffer, it) {
const uint8_t *const data = it->item.data;
size_t left = it->item.len - it->item.idx;
while (left > 0) {
ssize_t ret = write(term->ptmx, &data[it->item.idx], left);
if (ret < 0) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
LOG_ERRNO("failed to write to client");
return false;
}
return true;
}
it->item.idx += ret;
left -= ret;
}
free(it->item.data);
tll_remove(term->ptmx_buffer, it);
}
fdm_event_del(term->fdm, term->ptmx, EPOLLOUT);
return true;
}
static bool
fdm_ptmx(struct fdm *fdm, int fd, int events, void *data)
{
struct terminal *term = data;
if (events & EPOLLOUT) {
if (!fdm_ptmx_out(fdm, fd, events, data))
return false;
}
if ((events & EPOLLHUP) && !(events & EPOLLIN))
return term_shutdown(term);
assert(events & EPOLLIN);
if (!(events & EPOLLIN))
return true;
uint8_t buf[24 * 1024];
ssize_t count = read(term->ptmx, buf, sizeof(buf));
@ -328,6 +412,14 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl,
goto close_fds;
}
int ptmx_flags;
if ((ptmx_flags = fcntl(ptmx, F_GETFL)) < 0 ||
fcntl(ptmx, F_SETFL, ptmx_flags | O_NONBLOCK) < 0)
{
LOG_ERRNO("failed to configure ptmx as non-blocking");
goto err;
}
if (!fdm_add(fdm, ptmx, EPOLLIN, &fdm_ptmx, term) ||
!fdm_add(fdm, flash_fd, EPOLLIN, &fdm_flash, term) ||
!fdm_add(fdm, blink_fd, EPOLLIN, &fdm_blink, term) ||
@ -342,6 +434,7 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl,
.fdm = fdm,
.quit = false,
.ptmx = ptmx,
.ptmx_buffer = tll_init(),
.cursor_keys_mode = CURSOR_KEYS_NORMAL,
.keypad_keys_mode = KEYPAD_NUMERICAL,
.auto_margin = true,
@ -615,6 +708,10 @@ term_destroy(struct terminal *term)
assert(tll_length(term->render.workers.queue) == 0);
tll_free(term->render.workers.queue);
tll_foreach(term->ptmx_buffer, it)
free(it->item.data);
tll_free(term->ptmx_buffer);
int ret = EXIT_SUCCESS;
if (term->slave > 0) {
@ -768,26 +865,6 @@ term_reset(struct terminal *term, bool hard)
term_damage_all(term);
}
bool
term_to_slave(struct terminal *term, const void *_data, size_t len)
{
const uint8_t *data = _data;
size_t left = len;
while (left > 0) {
ssize_t ret = write(term->ptmx, data, left);
if (ret < 0) {
LOG_ERRNO("failed to write to client");
return false;
}
data += ret;
left -= ret;
}
return true;
}
void
term_damage_rows(struct terminal *term, int start, int end)
{

View file

@ -145,6 +145,12 @@ enum mouse_reporting {
enum cursor_style { CURSOR_BLOCK, CURSOR_UNDERLINE, CURSOR_BAR };
struct ptmx_buffer {
void *data;
size_t len;
size_t idx;
};
struct terminal {
struct fdm *fdm;
@ -152,6 +158,8 @@ struct terminal {
int ptmx;
bool quit;
tll(struct ptmx_buffer) ptmx_buffer;
enum cursor_keys cursor_keys_mode;
enum keypad_keys keypad_keys_mode;
bool reverse;