From 4d6091379b4486b624070492d45ad390e7442382 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 4 Nov 2009 21:04:43 +0000 Subject: [PATCH] Switch jobs over to use a bufferevent. --- Makefile | 2 +- cmd-run-shell.c | 44 ++++++++++++++-------------- job.c | 51 +++++++++++++++++++++++++------- server-job.c | 77 ------------------------------------------------- server.c | 6 ++-- status.c | 21 ++++++++------ tmux.h | 12 ++------ 7 files changed, 81 insertions(+), 132 deletions(-) delete mode 100644 server-job.c diff --git a/Makefile b/Makefile index 04102479..e1b5b6c1 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,7 @@ SRCS= attributes.c buffer-poll.c buffer.c cfg.c \ layout-set.c layout.c log.c job.c \ mode-key.c names.c options-cmd.c options.c paste.c procname.c \ resize.c screen-redraw.c screen-write.c screen.c session.c status.c \ - server-fn.c server.c server-client.c server-window.c server-job.c \ + server-fn.c server.c server-client.c server-window.c \ tmux.c tty-keys.c tty-term.c tty.c utf8.c \ window-choose.c window-clock.c window-copy.c window-more.c window.c \ xterm-keys.c xmalloc.c diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 1a8e28f4..605b5dca 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -77,31 +77,33 @@ cmd_run_shell_callback(struct job *job) { struct cmd_run_shell_data *cdata = job->data; struct cmd_ctx *ctx = &cdata->ctx; - char *cmd, *msg, *line, *buf; - size_t off, len, llen; + char *cmd, *msg, *line; + size_t size; int retcode; + u_int lines; - buf = BUFFER_OUT(job->out); - len = BUFFER_USED(job->out); + lines = 0; + do { + if ((line = evbuffer_readline(job->event->input)) != NULL) { + ctx->print(ctx, "%s", line); + lines++; + } + } while (line != NULL); + + size = EVBUFFER_LENGTH(job->event->input); + if (size != 0) { + line = xmalloc(size + 1); + memcpy(line, EVBUFFER_DATA(job->event->input), size); + line[size] = '\0'; + + ctx->print(ctx, "%s", line); + lines++; + + xfree(line); + } cmd = cdata->cmd; - if (len != 0) { - line = buf; - for (off = 0; off < len; off++) { - if (buf[off] == '\n') { - llen = buf + off - line; - if (llen > INT_MAX) - break; - ctx->print(ctx, "%.*s", (int) llen, line); - line = buf + off + 1; - } - } - llen = buf + len - line; - if (llen > 0 && llen < INT_MAX) - ctx->print(ctx, "%.*s", (int) llen, line); - } - msg = NULL; if (WIFEXITED(job->status)) { if ((retcode = WEXITSTATUS(job->status)) != 0) @@ -111,7 +113,7 @@ cmd_run_shell_callback(struct job *job) xasprintf(&msg, "'%s' terminated by signal %d", cmd, retcode); } if (msg != NULL) { - if (len != 0) + if (lines != 0) ctx->print(ctx, "%s", msg); else ctx->info(ctx, "%s", msg); diff --git a/job.c b/job.c index fd91913f..15f93013 100644 --- a/job.c +++ b/job.c @@ -17,6 +17,7 @@ */ #include +#include #include #include @@ -35,6 +36,8 @@ struct joblist all_jobs = SLIST_HEAD_INITIALIZER(&all_jobs); RB_GENERATE(jobs, job, entry, job_cmp); +void job_callback(struct bufferevent *, short, void *); + int job_cmp(struct job *job1, struct job *job2) { @@ -86,14 +89,13 @@ job_add(struct jobs *jobs, int flags, struct client *c, const char *cmd, job->client = c; job->fd = -1; - job->out = buffer_create(BUFSIZ); - memset(&job->event, 0, sizeof job->event); + job->event = NULL; job->callbackfn = callbackfn; job->freefn = freefn; job->data = data; - job->flags = flags|JOB_DONE; + job->flags = flags; if (jobs != NULL) RB_INSERT(jobs, jobs, job); @@ -125,9 +127,9 @@ job_free(struct job *job) if (job->fd != -1) close(job->fd); - if (job->out != NULL) - buffer_destroy(job->out); - event_del(&job->event); + + if (job->event != NULL) + bufferevent_free(job->event); xfree(job); } @@ -138,11 +140,10 @@ job_run(struct job *job) { int nullfd, out[2], mode; - if (!(job->flags & JOB_DONE)) + if (job->fd != -1 || job->pid != -1) return (0); - job->flags &= ~JOB_DONE; - if (pipe(out) != 0) + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, out) != 0) return (-1); switch (job->pid = fork()) { @@ -181,13 +182,41 @@ job_run(struct job *job) if (fcntl(job->fd, F_SETFD, FD_CLOEXEC) == -1) fatal("fcntl failed"); - if (BUFFER_USED(job->out) != 0) - buffer_remove(job->out, BUFFER_USED(job->out)); + if (job->event != NULL) + bufferevent_free(job->event); + job->event = + bufferevent_new(job->fd, NULL, NULL, job_callback, job); + bufferevent_enable(job->event, EV_READ); return (0); } } +/* Job buffer error callback. */ +void +job_callback(unused struct bufferevent *bufev, unused short events, void *data) +{ + struct job *job = data; + + bufferevent_disable(job->event, EV_READ); + close(job->fd); + job->fd = -1; + + if (job->pid == -1 && job->callbackfn != NULL) + job->callbackfn(job); +} + +/* Job died (waitpid() returned its pid). */ +void +job_died(struct job *job, int status) +{ + job->status = status; + job->pid = -1; + + if (job->fd == -1 && job->callbackfn != NULL) + job->callbackfn(job); +} + /* Kill a job. */ void job_kill(struct job *job) diff --git a/server-job.c b/server-job.c deleted file mode 100644 index 5d83ff92..00000000 --- a/server-job.c +++ /dev/null @@ -1,77 +0,0 @@ -/* $OpenBSD$ */ - -/* - * Copyright (c) 2009 Nicholas Marriott - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER - * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING - * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include - -#include -#include - -#include "tmux.h" - -/* Register jobs for poll. */ -void -server_job_prepare(void) -{ - struct job *job; - - SLIST_FOREACH(job, &all_jobs, lentry) { - if (job->fd == -1) - continue; - event_del(&job->event); - event_set( - &job->event, job->fd, EV_READ, server_job_callback, job); - event_add(&job->event, NULL); - } -} - -/* Process a single job event. */ -void -server_job_callback(int fd, short events, void *data) -{ - struct job *job = data; - - if (job->fd == -1) - return; - - if (buffer_poll(fd, events, job->out, NULL) != 0) { - close(job->fd); - job->fd = -1; - } -} - -/* Job functions that happen once a loop. */ -void -server_job_loop(void) -{ - struct job *job; - -restart: - SLIST_FOREACH(job, &all_jobs, lentry) { - if (job->flags & JOB_DONE || job->fd != -1 || job->pid != -1) - continue; - job->flags |= JOB_DONE; - - if (job->callbackfn != NULL) { - job->callbackfn(job); - if ((!job->flags & JOB_PERSIST)) { - job_free(job); - goto restart; - } - } - } -} diff --git a/server.c b/server.c index 236cd201..680ac93f 100644 --- a/server.c +++ b/server.c @@ -214,14 +214,12 @@ server_loop(void) while (!server_should_shutdown()) { server_update_socket(); - server_job_prepare(); server_window_prepare(); server_client_prepare(); event_loopexit(&tv); event_loop(EVLOOP_ONCE); - server_job_loop(); server_window_loop(); server_client_loop(); @@ -470,8 +468,8 @@ server_child_exited(pid_t pid, int status) SLIST_FOREACH(job, &all_jobs, lentry) { if (pid == job->pid) { - job->pid = -1; - job->status = status; + job_died(job, status); /* might free job */ + break; } } } diff --git a/status.c b/status.c index 21ca31a2..4b0c81fb 100644 --- a/status.c +++ b/status.c @@ -486,23 +486,26 @@ status_job(struct client *c, char **iptr) void status_job_callback(struct job *job) { - char *buf; + char *line, *buf; size_t len; - len = BUFFER_USED(job->out); - buf = xmalloc(len + 1); - if (len != 0) - buffer_read(job->out, buf, len); - buf[len] = '\0'; - buf[strcspn(buf, "\n")] = '\0'; + buf = NULL; + if ((line = evbuffer_readline(job->event->input)) == NULL) { + len = EVBUFFER_LENGTH(job->event->input); + buf = xmalloc(len + 1); + if (len != 0) + memcpy(buf, EVBUFFER_DATA(job->event->input), len); + buf[len] = '\0'; + } if (job->data != NULL) xfree(job->data); else server_redraw_client(job->client); - job->data = xstrdup(buf); + job->data = xstrdup(line); - xfree(buf); + if (buf != NULL) + xfree(buf); } size_t diff --git a/tmux.h b/tmux.h index b72c0d8c..64a7d3bd 100644 --- a/tmux.h +++ b/tmux.h @@ -661,16 +661,14 @@ struct job { struct client *client; int fd; - struct event event; - struct buffer *out; + struct bufferevent *event; void (*callbackfn)(struct job *); void (*freefn)(void *); void *data; int flags; -#define JOB_DONE 0x1 -#define JOB_PERSIST 0x2 /* don't free after callback */ +#define JOB_PERSIST 0x1 /* don't free after callback */ RB_ENTRY(job) entry; SLIST_ENTRY(job) lentry; @@ -1306,6 +1304,7 @@ struct job *job_add(struct jobs *, int, struct client *, void job_remove(struct jobs *, struct job *); void job_free(struct job *); int job_run(struct job *); +void job_died(struct job *, int); void job_kill(struct job *); /* environ.c */ @@ -1589,11 +1588,6 @@ void server_client_prepare(void); void server_client_callback(int, short, void *); void server_client_loop(void); -/* server-job.c */ -void server_job_prepare(void); -void server_job_callback(int, short, void *); -void server_job_loop(void); - /* server-window.c */ void server_window_prepare(void); void server_window_callback(int, short, void *);