Avoid mallocs when writing small amounts of data to the child

This commit is contained in:
Kovid Goyal
2017-09-14 17:08:56 +05:30
parent 43c4c71abd
commit 32a11d9d21
3 changed files with 40 additions and 25 deletions

View File

@@ -55,7 +55,8 @@ static unsigned long remove_notify[MAX_CHILDREN] = {0};
static size_t add_queue_count = 0, remove_queue_count = 0;
static struct pollfd fds[MAX_CHILDREN + EXTRA_FDS] = {{0}};
static pthread_mutex_t children_lock;
static bool created = false, signal_received = false;
static bool signal_received = false;
static ChildMonitor *the_monitor = NULL;
static uint8_t drain_buf[1024];
static int signal_fds[2], wakeup_fds[2];
static void *glfw_window_id = NULL;
@@ -124,10 +125,9 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
int ret;
double repaint_delay;
if (created) { PyErr_SetString(PyExc_RuntimeError, "Can have only a single ChildMonitor instance"); return NULL; }
if (the_monitor) { PyErr_SetString(PyExc_RuntimeError, "Can have only a single ChildMonitor instance"); return NULL; }
if (!PyArg_ParseTuple(args, "dOOO", &repaint_delay, &wid, &death_notify, &dump_callback)) return NULL;
glfw_window_id = PyLong_AsVoidPtr(wid);
created = true;
if ((ret = pthread_mutex_init(&children_lock, NULL)) != 0) {
PyErr_Format(PyExc_RuntimeError, "Failed to create children_lock mutex: %s", strerror(ret));
return NULL;
@@ -149,6 +149,7 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
fds[0].fd = wakeup_fds[0]; fds[1].fd = signal_fds[0];
fds[0].events = POLLIN; fds[1].events = POLLIN;
self->repaint_delay = repaint_delay;
the_monitor = self;
return (PyObject*) self;
}
@@ -229,36 +230,47 @@ add_child(ChildMonitor *self, PyObject *args) {
Py_RETURN_NONE;
}
static PyObject *
needs_write(ChildMonitor *self, PyObject *args) {
#define needs_write_doc "needs_write(id, data) -> Queue data to be written to child."
unsigned long id, sz;
const char *data;
if (!PyArg_ParseTuple(args, "ks#", &id, &data, &sz)) return NULL;
PyObject *found = Py_False;
bool
schedule_write_to_child(unsigned long id, const char *data, size_t sz) {
ChildMonitor *self = the_monitor;
bool found = false;
children_mutex(lock);
for (size_t i = 0; i < self->count; i++) {
if (children[i].id == id) {
found = Py_True;
found = true;
Screen *screen = children[i].screen;
screen_mutex(lock, write);
uint8_t *buf = PyMem_RawRealloc(screen->write_buf, screen->write_buf_sz + sz);
if (buf == NULL) PyErr_NoMemory();
else {
memcpy(buf + screen->write_buf_sz, data, sz);
screen->write_buf = buf;
screen->write_buf_sz += sz;
size_t space_left = screen->write_buf_sz - screen->write_buf_used;
if (space_left < sz) {
if (screen->write_buf_used + sz > 100 * 1024 * 1024) {
fprintf(stderr, "Too much data being sent to child with id: %lu, ignoring it\n", id);
screen_mutex(unlock, write);
break;
}
screen->write_buf_sz = screen->write_buf_used + sz;
screen->write_buf = PyMem_RawRealloc(screen->write_buf, screen->write_buf_sz);
if (screen->write_buf == NULL) { fatal("Out of memory."); }
}
memcpy(screen->write_buf + screen->write_buf_used, data, sz);
screen->write_buf_used += sz;
screen_mutex(unlock, write);
break;
}
}
children_mutex(unlock);
if (PyErr_Occurred()) return NULL;
Py_INCREF(found);
return found;
}
static PyObject *
needs_write(ChildMonitor UNUSED *self, PyObject *args) {
#define needs_write_doc "needs_write(id, data) -> Queue data to be written to child."
unsigned long id, sz;
const char *data;
if (!PyArg_ParseTuple(args, "ks#", &id, &data, &sz)) return NULL;
if (schedule_write_to_child(id, data, sz)) { Py_RETURN_TRUE; }
Py_RETURN_FALSE;
}
static PyObject *
shutdown(ChildMonitor *self) {
#define shutdown_doc "shutdown() -> Shutdown the monitor loop."
@@ -727,8 +739,8 @@ write_to_child(int fd, Screen *screen) {
size_t written = 0;
ssize_t ret = 0;
screen_mutex(lock, write);
while (written < screen->write_buf_sz) {
ret = write(fd, screen->write_buf + written, screen->write_buf_sz - written);
while (written < screen->write_buf_used) {
ret = write(fd, screen->write_buf + written, screen->write_buf_used - written);
if (ret > 0) { written += ret; }
else if (ret == 0) {
// could mean anything, ignore
@@ -737,10 +749,10 @@ write_to_child(int fd, Screen *screen) {
if (errno == EINTR) continue;
if (errno == EWOULDBLOCK || errno == EAGAIN) break;
perror("Call to write() to child fd failed, discarding data.");
written = screen->write_buf_sz;
written = screen->write_buf_used;
}
}
screen->write_buf_sz -= written;
screen->write_buf_used -= written;
screen_mutex(unlock, write);
}

View File

@@ -250,7 +250,7 @@ typedef struct {
unsigned int parser_state, parser_text_start, parser_buf_pos;
bool parser_has_pending_text;
uint8_t read_buf[READ_BUF_SZ], *write_buf;
size_t read_buf_sz, write_buf_sz;
size_t read_buf_sz, write_buf_sz, write_buf_used;
pthread_mutex_t read_buf_lock, write_buf_lock;
} Screen;
@@ -305,6 +305,7 @@ void set_sprite_position(Cell *cell, Cell *previous_cell);
double monotonic();
PyObject* cm_thread_write(PyObject *self, PyObject *args);
bool schedule_write_to_child(unsigned long id, const char *data, size_t sz);
bool set_iutf8(int, bool);
color_type colorprofile_to_color(ColorProfile *self, color_type entry, color_type defval);

View File

@@ -64,7 +64,9 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
return NULL;
}
self->columns = columns; self->lines = lines;
self->write_buf = NULL;
self->write_buf = PyMem_RawMalloc(BUFSIZ);
if (self->write_buf == NULL) { Py_CLEAR(self); return PyErr_NoMemory(); }
self->write_buf_sz = BUFSIZ;
self->modes = empty_modes;
self->is_dirty = true;
self->scroll_changed = false;