diff --git a/cmd-choose-client.c b/cmd-choose-client.c index dd09fe51..0aa66622 100644 --- a/cmd-choose-client.c +++ b/cmd-choose-client.c @@ -43,7 +43,7 @@ const struct cmd_entry cmd_choose_client_entry = { }; struct cmd_choose_client_data { - u_int client; + struct client *client; char *template; }; @@ -87,7 +87,8 @@ cmd_choose_client_exec(struct cmd *self, struct cmd_ctx *ctx) cdata->template = xstrdup(data->arg); else cdata->template = xstrdup("detach-client -t '%%'"); - cdata->client = server_client_index(ctx->curclient); + cdata->client = ctx->curclient; + cdata->client->references++; window_choose_ready(wl->window->active, cur, cmd_choose_client_callback, cmd_choose_client_free, cdata); @@ -99,23 +100,22 @@ void cmd_choose_client_callback(void *data, int idx) { struct cmd_choose_client_data *cdata = data; - struct client *c, *c2; + struct client *c; struct cmd_list *cmdlist; struct cmd_ctx ctx; char *template, *cause; if (idx == -1) return; - if (cdata->client > ARRAY_LENGTH(&clients) - 1) + if (cdata->client->flags & CLIENT_DEAD) return; - c = ARRAY_ITEM(&clients, cdata->client); if ((u_int) idx > ARRAY_LENGTH(&clients) - 1) return; - c2 = ARRAY_ITEM(&clients, idx); - if (c2 == NULL || c2->session == NULL) + c = ARRAY_ITEM(&clients, idx); + if (c == NULL || c->session == NULL) return; - template = cmd_template_replace(cdata->template, c2->tty.path, 1); + template = cmd_template_replace(cdata->template, c->tty.path, 1); if (cmd_string_parse(template, &cmdlist, &cause) != 0) { if (cause != NULL) { @@ -129,7 +129,7 @@ cmd_choose_client_callback(void *data, int idx) xfree(template); ctx.msgdata = NULL; - ctx.curclient = c; + ctx.curclient = cdata->client; ctx.error = key_bindings_error; ctx.print = key_bindings_print; @@ -146,6 +146,7 @@ cmd_choose_client_free(void *data) { struct cmd_choose_client_data *cdata = data; + cdata->client->references--; xfree(cdata->template); xfree(cdata); } diff --git a/cmd-choose-session.c b/cmd-choose-session.c index 87b3437b..7bc34853 100644 --- a/cmd-choose-session.c +++ b/cmd-choose-session.c @@ -43,7 +43,7 @@ const struct cmd_entry cmd_choose_session_entry = { }; struct cmd_choose_session_data { - u_int client; + struct client *client; char *template; }; @@ -87,7 +87,8 @@ cmd_choose_session_exec(struct cmd *self, struct cmd_ctx *ctx) cdata->template = xstrdup(data->arg); else cdata->template = xstrdup("switch-client -t '%%'"); - cdata->client = server_client_index(ctx->curclient); + cdata->client = ctx->curclient; + cdata->client->references++; window_choose_ready(wl->window->active, cur, cmd_choose_session_callback, cmd_choose_session_free, cdata); @@ -99,7 +100,6 @@ void cmd_choose_session_callback(void *data, int idx) { struct cmd_choose_session_data *cdata = data; - struct client *c; struct session *s; struct cmd_list *cmdlist; struct cmd_ctx ctx; @@ -107,9 +107,8 @@ cmd_choose_session_callback(void *data, int idx) if (idx == -1) return; - if (cdata->client > ARRAY_LENGTH(&clients) - 1) + if (cdata->client->flags & CLIENT_DEAD) return; - c = ARRAY_ITEM(&clients, cdata->client); if ((u_int) idx > ARRAY_LENGTH(&sessions) - 1) return; @@ -121,7 +120,7 @@ cmd_choose_session_callback(void *data, int idx) if (cmd_string_parse(template, &cmdlist, &cause) != 0) { if (cause != NULL) { *cause = toupper((u_char) *cause); - status_message_set(c, "%s", cause); + status_message_set(cdata->client, "%s", cause); xfree(cause); } xfree(template); @@ -130,7 +129,7 @@ cmd_choose_session_callback(void *data, int idx) xfree(template); ctx.msgdata = NULL; - ctx.curclient = c; + ctx.curclient = cdata->client; ctx.error = key_bindings_error; ctx.print = key_bindings_print; @@ -147,6 +146,7 @@ cmd_choose_session_free(void *data) { struct cmd_choose_session_data *cdata = data; + cdata->client->references--; xfree(cdata->template); xfree(cdata); } diff --git a/cmd-choose-window.c b/cmd-choose-window.c index ddc8ecb6..43a24585 100644 --- a/cmd-choose-window.c +++ b/cmd-choose-window.c @@ -43,8 +43,8 @@ const struct cmd_entry cmd_choose_window_entry = { }; struct cmd_choose_window_data { - u_int client; - u_int session; + struct client *client; + struct session *session; char *template; }; @@ -107,13 +107,14 @@ cmd_choose_window_exec(struct cmd *self, struct cmd_ctx *ctx) } cdata = xmalloc(sizeof *cdata); - if (session_index(s, &cdata->session) != 0) - fatalx("session not found"); if (data->arg != NULL) cdata->template = xstrdup(data->arg); else cdata->template = xstrdup("select-window -t '%%'"); - cdata->client = server_client_index(ctx->curclient); + cdata->session = s; + cdata->session->references++; + cdata->client = ctx->curclient; + cdata->client->references++; window_choose_ready(wl->window->active, cur, cmd_choose_window_callback, cmd_choose_window_free, cdata); @@ -125,31 +126,27 @@ void cmd_choose_window_callback(void *data, int idx) { struct cmd_choose_window_data *cdata = data; - struct client *c; - struct session *s; struct cmd_list *cmdlist; struct cmd_ctx ctx; char *target, *template, *cause; if (idx == -1) return; - if (cdata->client > ARRAY_LENGTH(&clients) - 1) + if (cdata->client->flags & CLIENT_DEAD) + return; + if (cdata->session->flags & SESSION_DEAD) return; - c = ARRAY_ITEM(&clients, cdata->client); - if (cdata->session > ARRAY_LENGTH(&sessions) - 1) - return; - s = ARRAY_ITEM(&sessions, cdata->session); - if (c->session != s) + if (cdata->client->session != cdata->session) return; - xasprintf(&target, "%s:%d", s->name, idx); + xasprintf(&target, "%s:%d", cdata->session->name, idx); template = cmd_template_replace(cdata->template, target, 1); xfree(target); if (cmd_string_parse(template, &cmdlist, &cause) != 0) { if (cause != NULL) { *cause = toupper((u_char) *cause); - status_message_set(c, "%s", cause); + status_message_set(cdata->client, "%s", cause); xfree(cause); } xfree(template); @@ -158,7 +155,7 @@ cmd_choose_window_callback(void *data, int idx) xfree(template); ctx.msgdata = NULL; - ctx.curclient = c; + ctx.curclient = cdata->client; ctx.error = key_bindings_error; ctx.print = key_bindings_print; @@ -175,6 +172,8 @@ cmd_choose_window_free(void *data) { struct cmd_choose_window_data *cdata = data; + cdata->session->references--; + cdata->client->references--; xfree(cdata->template); xfree(cdata); } diff --git a/cmd-server-info.c b/cmd-server-info.c index 14212478..48ea3707 100644 --- a/cmd-server-info.c +++ b/cmd-server-info.c @@ -91,9 +91,10 @@ cmd_server_info_exec(unused struct cmd *self, struct cmd_ctx *ctx) continue; ctx->print(ctx, "%2d: %s (%d, %d): %s [%ux%u %s] " - "[flags=0x%x/0x%x]", i, c->tty.path, c->ibuf.fd, c->tty.fd, - c->session->name, c->tty.sx, c->tty.sy, c->tty.termname, - c->flags, c->tty.flags); + "[flags=0x%x/0x%x, references=%u]", i, c->tty.path, + c->ibuf.fd, c->tty.fd, c->session->name, + c->tty.sx, c->tty.sy, c->tty.termname, c->flags, + c->tty.flags, c->references); } ctx->print(ctx, "%s", ""); @@ -109,8 +110,9 @@ cmd_server_info_exec(unused struct cmd *self, struct cmd_ctx *ctx) *strchr(tim, '\n') = '\0'; ctx->print(ctx, "%2u: %s: %u windows (created %s) [%ux%u] " - "[flags=0x%x]", i, s->name, winlink_count(&s->windows), - tim, s->sx, s->sy, s->flags); + "[flags=0x%x, references=%u]", i, s->name, + winlink_count(&s->windows), tim, s->sx, s->sy, s->flags, + s->references); RB_FOREACH(wl, winlinks, &s->windows) { w = wl->window; ctx->print(ctx, "%4u: %s [%ux%u] [flags=0x%x, " diff --git a/server.c b/server.c index 09c412d5..c8b96368 100644 --- a/server.c +++ b/server.c @@ -43,6 +43,7 @@ /* Client list. */ struct clients clients; +struct clients dead_clients; void server_create_client(int); int server_create_socket(void); @@ -61,6 +62,7 @@ int server_check_window_activity(struct session *, struct window *); int server_check_window_content(struct session *, struct window *, struct window_pane *); +void server_clean_dead(void); void server_lost_client(struct client *); void server_check_window(struct window *); void server_check_redraw(struct client *); @@ -85,6 +87,7 @@ server_create_client(int fd) fatal("fcntl failed"); c = xcalloc(1, sizeof *c); + c->references = 0; imsg_init(&c->ibuf, fd); ARRAY_INIT(&c->prompt_hdata); @@ -162,7 +165,9 @@ server_start(char *path) ARRAY_INIT(&windows); ARRAY_INIT(&clients); + ARRAY_INIT(&dead_clients); ARRAY_INIT(&sessions); + ARRAY_INIT(&dead_sessions); mode_key_init_trees(); key_bindings_init(); utf8_build(); @@ -344,6 +349,9 @@ server_main(int srv_fd) /* Collect any unset key bindings. */ key_bindings_clean(); + + /* Collect dead clients and sessions. */ + server_clean_dead(); /* * If we have no sessions and clients left, let's get out @@ -948,11 +956,45 @@ server_lost_client(struct client *c) close(c->ibuf.fd); imsg_clear(&c->ibuf); - xfree(c); + + for (i = 0; i < ARRAY_LENGTH(&dead_clients); i++) { + if (ARRAY_ITEM(&dead_clients, i) == NULL) { + ARRAY_SET(&dead_clients, i, c); + break; + } + } + if (i == ARRAY_LENGTH(&dead_clients)) + ARRAY_ADD(&dead_clients, c); + c->flags |= CLIENT_DEAD; recalculate_sizes(); } +/* Free dead, unreferenced clients and sessions. */ +void +server_clean_dead(void) +{ + struct session *s; + struct client *c; + u_int i; + + for (i = 0; i < ARRAY_LENGTH(&dead_sessions); i++) { + s = ARRAY_ITEM(&dead_sessions, i); + if (s == NULL || s->references != 0) + continue; + ARRAY_SET(&dead_sessions, i, NULL); + xfree(s); + } + + for (i = 0; i < ARRAY_LENGTH(&dead_clients); i++) { + c = ARRAY_ITEM(&dead_clients, i); + if (c == NULL || c->references != 0) + continue; + ARRAY_SET(&dead_clients, i, NULL); + xfree(c); + } +} + /* Handle window data. */ void server_handle_window(struct window *w, struct window_pane *wp) diff --git a/session.c b/session.c index 55d38c80..4e5ac83e 100644 --- a/session.c +++ b/session.c @@ -28,6 +28,7 @@ /* Global session list. */ struct sessions sessions; +struct sessions dead_sessions; struct winlink *session_next_activity(struct session *, struct winlink *); struct winlink *session_previous_activity(struct session *, struct winlink *); @@ -121,14 +122,19 @@ session_create(const char *name, const char *cmd, const char *cwd, u_int i; s = xmalloc(sizeof *s); + s->references = 0; s->flags = 0; + if (gettimeofday(&s->tv, NULL) != 0) fatal("gettimeofday"); + s->curw = NULL; SLIST_INIT(&s->lastw); RB_INIT(&s->windows); SLIST_INIT(&s->alerts); + paste_init_stack(&s->buffers); + options_init(&s->options, &global_s_options); environ_init(&s->environ); if (env != NULL) @@ -187,7 +193,16 @@ session_destroy(struct session *s) winlink_remove(&s->windows, RB_ROOT(&s->windows)); xfree(s->name); - xfree(s); + + for (i = 0; i < ARRAY_LENGTH(&dead_sessions); i++) { + if (ARRAY_ITEM(&dead_sessions, i) == NULL) { + ARRAY_SET(&dead_sessions, i, s); + break; + } + } + if (i == ARRAY_LENGTH(&dead_sessions)) + ARRAY_ADD(&dead_sessions, s); + s->flags |= SESSION_DEAD; } /* Find session index. */ diff --git a/tmux.h b/tmux.h index 4057fe8b..7b725b00 100644 --- a/tmux.h +++ b/tmux.h @@ -812,11 +812,14 @@ struct session { SLIST_HEAD(, session_alert) alerts; #define SESSION_UNATTACHED 0x1 /* not attached to any clients */ +#define SESSION_DEAD 0x2 int flags; struct termios tio; struct environ environ; + + int references; }; ARRAY_DECL(sessions, struct session *); @@ -939,6 +942,7 @@ struct client { #define CLIENT_SUSPENDED 0x40 #define CLIENT_BAD 0x80 #define CLIENT_IDENTIFY 0x100 +#define CLIENT_DEAD 0x200 int flags; struct timeval identify_timer; @@ -963,6 +967,8 @@ struct client { struct mode_key_data prompt_mdata; struct session *session; + + int references; }; ARRAY_DECL(clients, struct client *); @@ -1428,6 +1434,7 @@ const char *key_string_lookup_key(int); /* server.c */ extern struct clients clients; +extern struct clients dead_clients; int server_client_index(struct client *); int server_start(char *); @@ -1702,6 +1709,7 @@ char *default_window_name(struct window *); /* session.c */ extern struct sessions sessions; +extern struct sessions dead_sessions; void session_alert_add(struct session *, struct window *, int); void session_alert_cancel(struct session *, struct winlink *); int session_alert_has(struct session *, struct winlink *, int);