diff options
| author | Björn Linse <bjorn.linse@gmail.com> | 2017-08-27 11:59:33 +0200 |
|---|---|---|
| committer | Björn Linse <bjorn.linse@gmail.com> | 2017-11-24 14:50:00 +0100 |
| commit | 5215e3205a07b85e4e4cf1f8a8ca6be2b9556459 (patch) | |
| tree | 515ccc2d01935465edd71f1d66f526be7dc66cfc | |
| parent | 3717e2157f2d45ce23dbe4ac03085fea2d956dc4 (diff) | |
| download | rneovim-5215e3205a07b85e4e4cf1f8a8ca6be2b9556459.tar.gz rneovim-5215e3205a07b85e4e4cf1f8a8ca6be2b9556459.tar.bz2 rneovim-5215e3205a07b85e4e4cf1f8a8ca6be2b9556459.zip | |
channels: refactor
| -rw-r--r-- | src/nvim/api/ui.c | 2 | ||||
| -rw-r--r-- | src/nvim/api/vim.c | 4 | ||||
| -rw-r--r-- | src/nvim/channel.c | 61 | ||||
| -rw-r--r-- | src/nvim/channel.h | 120 | ||||
| -rw-r--r-- | src/nvim/eval.c | 314 | ||||
| -rw-r--r-- | src/nvim/eval/typval.c | 24 | ||||
| -rw-r--r-- | src/nvim/event/libuv_process.c | 12 | ||||
| -rw-r--r-- | src/nvim/event/process.c | 77 | ||||
| -rw-r--r-- | src/nvim/event/process.h | 15 | ||||
| -rw-r--r-- | src/nvim/event/stream.h | 2 | ||||
| -rw-r--r-- | src/nvim/globals.h | 7 | ||||
| -rw-r--r-- | src/nvim/msgpack_rpc/channel.c | 388 | ||||
| -rw-r--r-- | src/nvim/msgpack_rpc/channel.h | 2 | ||||
| -rw-r--r-- | src/nvim/msgpack_rpc/channel_defs.h | 36 | ||||
| -rw-r--r-- | src/nvim/os/pty_process_unix.c | 10 | ||||
| -rw-r--r-- | src/nvim/os/pty_process_win.c | 12 | ||||
| -rw-r--r-- | src/nvim/os/shell.c | 26 |
17 files changed, 580 insertions, 532 deletions
diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index afbee09c1c..a9eaccfac5 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -252,7 +252,7 @@ static void remote_ui_flush(UI *ui) { UIData *data = ui->data; if (data->buffer.size > 0) { - channel_send_event(data->channel_id, "redraw", data->buffer); + rpc_send_event(data->channel_id, "redraw", data->buffer); data->buffer = (Array)ARRAY_DICT_INIT; } } diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index d2b0e329c9..f4ccf07bec 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -721,7 +721,7 @@ void nvim_subscribe(uint64_t channel_id, String event) char e[METHOD_MAXLEN + 1]; memcpy(e, event.data, length); e[length] = NUL; - channel_subscribe(channel_id, e); + rpc_subscribe(channel_id, e); } /// Unsubscribes to event broadcasts @@ -737,7 +737,7 @@ void nvim_unsubscribe(uint64_t channel_id, String event) char e[METHOD_MAXLEN + 1]; memcpy(e, event.data, length); e[length] = NUL; - channel_unsubscribe(channel_id, e); + rpc_unsubscribe(channel_id, e); } Integer nvim_get_color_by_name(String name) diff --git a/src/nvim/channel.c b/src/nvim/channel.c new file mode 100644 index 0000000000..e61ec9c19b --- /dev/null +++ b/src/nvim/channel.c @@ -0,0 +1,61 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +#include "nvim/api/ui.h" +#include "nvim/channel.h" +#include "nvim/msgpack_rpc/channel.h" + +PMap(uint64_t) *channels = NULL; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "channel.c.generated.h" +#endif +/// Teardown the module +void channel_teardown(void) +{ + if (!channels) { + return; + } + + Channel *channel; + + map_foreach_value(channels, channel, { + (void)channel; // close_channel(channel); + }); +} + +/// Initializes the module +void channel_init(void) +{ + channels = pmap_new(uint64_t)(); + rpc_init(); + remote_ui_init(); +} + +void channel_incref(Channel *channel) +{ + channel->refcount++; +} + +void channel_decref(Channel *channel) +{ + if (!(--channel->refcount)) { + multiqueue_put(main_loop.fast_events, free_channel_event, 1, channel); + } +} + +static void free_channel_event(void **argv) +{ + Channel *channel = argv[0]; + if (channel->is_rpc) { + rpc_free(channel); + } + + callback_free(&channel->on_stdout); + callback_free(&channel->on_stderr); + callback_free(&channel->on_exit); + + pmap_del(uint64_t)(channels, channel->id); + multiqueue_free(channel->events); + xfree(channel); +} diff --git a/src/nvim/channel.h b/src/nvim/channel.h new file mode 100644 index 0000000000..b48f508722 --- /dev/null +++ b/src/nvim/channel.h @@ -0,0 +1,120 @@ +#ifndef NVIM_CHANNEL_H +#define NVIM_CHANNEL_H + +#include "nvim/main.h" +#include "nvim/event/socket.h" +#include "nvim/event/process.h" +#include "nvim/os/pty_process.h" +#include "nvim/event/libuv_process.h" +#include "nvim/eval/typval.h" +#include "nvim/msgpack_rpc/channel_defs.h" + +typedef enum { + kChannelStreamProc, + kChannelStreamSocket, + kChannelStreamStdio, + kChannelStreamInternal +} ChannelStreamType; + +typedef struct { + Stream in; + Stream out; +} StdioPair; + +// typedef struct { +// Callback on_out; +// Callback on_close; +// Garray buffer; +// bool buffering; +// } CallbackReader + +#define CallbackReader Callback + +struct Channel { + uint64_t id; + size_t refcount; + MultiQueue *events; + + ChannelStreamType streamtype; + union { + Process proc; + LibuvProcess uv; + PtyProcess pty; + Stream socket; + StdioPair stdio; + } stream; + + bool is_rpc; + RpcState rpc; + Terminal *term; + + CallbackReader on_stdout; + CallbackReader on_stderr; + Callback on_exit; + + varnumber_T *status_ptr; // TODO: refactor? +}; + +EXTERN PMap(uint64_t) *channels; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "channel.h.generated.h" +#endif + +static inline Channel *channel_alloc(ChannelStreamType type) +{ + Channel *chan = xcalloc(1, sizeof(*chan)); + chan->id = next_chan_id++; + chan->events = multiqueue_new_child(main_loop.events); + chan->refcount = 1; + chan->streamtype = type; + pmap_put(uint64_t)(channels, chan->id, chan); + return chan; +} + +/// @returns Channel with the id or NULL if not found +static inline Channel *find_channel(uint64_t id) +{ + return pmap_get(uint64_t)(channels, id); +} + +static inline Stream *channel_instream(Channel *chan) + FUNC_ATTR_NONNULL_ALL +{ + switch (chan->streamtype) { + case kChannelStreamProc: + return &chan->stream.proc.in; + + case kChannelStreamSocket: + return &chan->stream.socket; + + case kChannelStreamStdio: + return &chan->stream.stdio.in; + + case kChannelStreamInternal: + abort(); + } + abort(); +} + +static inline Stream *channel_outstream(Channel *chan) + FUNC_ATTR_NONNULL_ALL +{ + switch (chan->streamtype) { + case kChannelStreamProc: + return &chan->stream.proc.out; + + case kChannelStreamSocket: + return &chan->stream.socket; + + case kChannelStreamStdio: + return &chan->stream.stdio.out; + + case kChannelStreamInternal: + abort(); + } + abort(); +} + + +#endif // NVIM_CHANNEL_H diff --git a/src/nvim/eval.c b/src/nvim/eval.c index b8bae3e293..013cfce78d 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -24,6 +24,7 @@ #endif #include "nvim/eval.h" #include "nvim/buffer.h" +#include "nvim/channel.h" #include "nvim/charset.h" #include "nvim/cursor.h" #include "nvim/diff.h" @@ -437,29 +438,12 @@ static ScopeDictDictItem vimvars_var; #define vimvarht vimvardict.dv_hashtab typedef struct { - union { - LibuvProcess uv; - PtyProcess pty; - } proc; - Stream in, out, err; // Initialized in common_job_start(). - Terminal *term; - bool stopped; - bool exited; - bool rpc; - int refcount; - Callback on_stdout, on_stderr, on_exit; - varnumber_T *status_ptr; - uint64_t id; - MultiQueue *events; -} TerminalJobData; - -typedef struct { - TerminalJobData *data; + Channel *data; Callback *callback; const char *type; list_T *received; int status; -} JobEvent; +} ChannelEvent; typedef struct { TimeWatcher tw; @@ -513,7 +497,6 @@ typedef enum { #define FNE_INCL_BR 1 /* find_name_end(): include [] in name */ #define FNE_CHECK_START 2 /* find_name_end(): check name starts with valid character */ -static PMap(uint64_t) *jobs = NULL; static uint64_t last_timer_id = 0; static PMap(uint64_t) *timers = NULL; @@ -556,7 +539,6 @@ void eval_init(void) { vimvars[VV_VERSION].vv_nr = VIM_VERSION_100; - jobs = pmap_new(uint64_t)(); timers = pmap_new(uint64_t)(); struct vimvar *p; @@ -5141,8 +5123,8 @@ bool garbage_collect(bool testing) // Jobs { - TerminalJobData *data; - map_foreach_value(jobs, data, { + Channel *data; + map_foreach_value(channels, data, { set_ref_in_callback(&data->on_stdout, copyID, NULL, NULL); set_ref_in_callback(&data->on_stderr, copyID, NULL, NULL); set_ref_in_callback(&data->on_exit, copyID, NULL, NULL); @@ -11433,24 +11415,23 @@ static void f_jobclose(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - TerminalJobData *data = find_job(argvars[0].vval.v_number); + Channel *data = find_job(argvars[0].vval.v_number, true); if (!data) { - EMSG(_(e_invjob)); return; } - Process *proc = (Process *)&data->proc; + Process *proc = (Process *)&data->stream.proc; if (argvars[1].v_type == VAR_STRING) { char *stream = (char *)argvars[1].vval.v_string; if (!strcmp(stream, "stdin")) { - if (data->rpc) { + if (data->is_rpc) { EMSG(_("Invalid stream on rpc job, use jobclose(id, 'rpc')")); } else { process_close_in(proc); } } else if (!strcmp(stream, "stdout")) { - if (data->rpc) { + if (data->is_rpc) { EMSG(_("Invalid stream on rpc job, use jobclose(id, 'rpc')")); } else { process_close_out(proc); @@ -11458,7 +11439,7 @@ static void f_jobclose(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else if (!strcmp(stream, "stderr")) { process_close_err(proc); } else if (!strcmp(stream, "rpc")) { - if (data->rpc) { + if (data->is_rpc) { channel_close(data->id); } else { EMSG(_("Invalid job stream: Not an rpc job")); @@ -11467,13 +11448,13 @@ static void f_jobclose(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_("Invalid job stream \"%s\""), stream); } } else { - if (data->rpc) { + if (data->is_rpc) { channel_close(data->id); process_close_err(proc); } else { process_close_streams(proc); if (proc->type == kProcessTypePty) { - pty_process_close_master(&data->proc.pty); + pty_process_close_master(&data->stream.pty); } } } @@ -11494,13 +11475,12 @@ static void f_jobpid(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - TerminalJobData *data = find_job(argvars[0].vval.v_number); + Channel *data = find_job(argvars[0].vval.v_number, true); if (!data) { - EMSG(_(e_invjob)); return; } - Process *proc = (Process *)&data->proc; + Process *proc = (Process *)&data->stream.proc; rettv->vval.v_number = proc->pid; } @@ -11521,18 +11501,19 @@ static void f_jobsend(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - TerminalJobData *data = find_job(argvars[0].vval.v_number); + Channel *data = find_channel(argvars[0].vval.v_number); if (!data) { - EMSG(_(e_invjob)); + EMSG(_(e_invchan)); return; } - if (((Process *)&data->proc)->in->closed) { + Stream *in = channel_instream(data); + if (in->closed) { EMSG(_("Can't send data to the job: stdin is closed")); return; } - if (data->rpc) { + if (data->is_rpc) { EMSG(_("Can't send raw data to rpc channel")); return; } @@ -11546,7 +11527,7 @@ static void f_jobsend(typval_T *argvars, typval_T *rettv, FunPtr fptr) } WBuffer *buf = wstream_new_buffer(input, input_len, 1, xfree); - rettv->vval.v_number = wstream_write(data->proc.uv.process.in, buf); + rettv->vval.v_number = wstream_write(in, buf); } // "jobresize(job, width, height)" function @@ -11567,19 +11548,17 @@ static void f_jobresize(typval_T *argvars, typval_T *rettv, FunPtr fptr) } - TerminalJobData *data = find_job(argvars[0].vval.v_number); + Channel *data = find_job(argvars[0].vval.v_number, true); if (!data) { - EMSG(_(e_invjob)); return; } - if (data->proc.uv.process.type != kProcessTypePty) { - EMSG(_(e_jobnotpty)); + if (data->stream.proc.type != kProcessTypePty) { + EMSG(_(e_channotpty)); return; } - pty_process_resize(&data->proc.pty, argvars[1].vval.v_number, - argvars[2].vval.v_number); + pty_process_resize(&data->stream.pty, argvars[1].vval.v_number, argvars[2].vval.v_number); rettv->vval.v_number = 1; } @@ -11697,31 +11676,25 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - TerminalJobData *data = common_job_init(argv, on_stdout, on_stderr, on_exit, + Channel *data = common_job_init(argv, on_stdout, on_stderr, on_exit, pty, rpc, detach, cwd); - Process *proc = (Process *)&data->proc; if (pty) { + PtyProcess *pty = &data->stream.pty; uint16_t width = (uint16_t)tv_dict_get_number(job_opts, "width"); if (width > 0) { - data->proc.pty.width = width; + pty->width = width; } uint16_t height = (uint16_t)tv_dict_get_number(job_opts, "height"); if (height > 0) { - data->proc.pty.height = height; + pty->height = height; } char *term = tv_dict_get_string(job_opts, "TERM", true); if (term) { - data->proc.pty.term_name = term; + pty->term_name = term; } } - if (!rpc && on_stdout.type == kCallbackNone) { - proc->out = NULL; - } - if (on_stderr.type == kCallbackNone) { - proc->err = NULL; - } common_job_start(data, rettv); } @@ -11742,14 +11715,12 @@ static void f_jobstop(typval_T *argvars, typval_T *rettv, FunPtr fptr) } - TerminalJobData *data = find_job(argvars[0].vval.v_number); + Channel *data = find_job(argvars[0].vval.v_number, true); if (!data) { - EMSG(_(e_invjob)); return; } - process_stop((Process *)&data->proc); - data->stopped = true; + process_stop((Process *)&data->stream.proc); rettv->vval.v_number = 1; } @@ -11778,9 +11749,9 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) // is used to represent an invalid job id, -2 is for a interrupted job and // -1 for jobs that were skipped or timed out. for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) { - TerminalJobData *data = NULL; + Channel *data = NULL; if (arg->li_tv.v_type != VAR_NUMBER - || !(data = find_job(arg->li_tv.vval.v_number))) { + || !(data = find_job(arg->li_tv.vval.v_number, false))) { tv_list_append_number(rv, -3); } else { // append the list item and set the status pointer so we'll collect the @@ -11802,16 +11773,16 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) } for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) { - TerminalJobData *data = NULL; + Channel *data = NULL; if (remaining == 0) { // timed out break; } if (arg->li_tv.v_type != VAR_NUMBER - || !(data = find_job(arg->li_tv.vval.v_number))) { + || !(data = find_job(arg->li_tv.vval.v_number, false))) { continue; } - int status = process_wait((Process *)&data->proc, remaining, waiting_jobs); + int status = process_wait((Process *)&data->stream.proc, remaining, waiting_jobs); if (status < 0) { // interrupted or timed out, skip remaining jobs. if (status == -2) { @@ -11832,9 +11803,9 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) } for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) { - TerminalJobData *data = NULL; + Channel *data = NULL; if (arg->li_tv.v_type != VAR_NUMBER - || !(data = find_job(arg->li_tv.vval.v_number))) { + || !(data = find_job(arg->li_tv.vval.v_number, false))) { continue; } // remove the status pointer because the list may be freed before the @@ -11844,9 +11815,9 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) // restore the parent queue for any jobs still alive for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) { - TerminalJobData *data = NULL; + Channel *data = NULL; if (arg->li_tv.v_type != VAR_NUMBER - || !(data = pmap_get(uint64_t)(jobs, arg->li_tv.vval.v_number))) { + || !(data = find_job(arg->li_tv.vval.v_number, false))) { continue; } // restore the parent queue for the job @@ -13803,9 +13774,8 @@ static void f_rpcnotify(typval_T *argvars, typval_T *rettv, FunPtr fptr) ADD(args, vim_to_object(tv)); } - if (!channel_send_event((uint64_t)argvars[0].vval.v_number, - tv_get_string(&argvars[1]), - args)) { + if (!rpc_send_event((uint64_t)argvars[0].vval.v_number, + tv_get_string(&argvars[1]), args)) { EMSG2(_(e_invarg2), "Channel doesn't exist"); return; } @@ -13870,10 +13840,8 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr) Error err = ERROR_INIT; - Object result = channel_send_call((uint64_t)argvars[0].vval.v_number, - tv_get_string(&argvars[1]), - args, - &err); + Object result = rpc_send_call((uint64_t)argvars[0].vval.v_number, + tv_get_string(&argvars[1]), args, &err); if (l_provider_call_nesting) { current_SID = save_current_SID; @@ -13954,7 +13922,7 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) // The last item of argv must be NULL argv[i] = NULL; - TerminalJobData *data = common_job_init(argv, CALLBACK_NONE, CALLBACK_NONE, + Channel *data = common_job_init(argv, CALLBACK_NONE, CALLBACK_NONE, CALLBACK_NONE, false, true, false, NULL); common_job_start(data, rettv); @@ -13977,10 +13945,11 @@ static void f_rpcstop(typval_T *argvars, typval_T *rettv, FunPtr fptr) } // if called with a job, stop it, else closes the channel - if (pmap_get(uint64_t)(jobs, argvars[0].vval.v_number)) { + uint64_t id = argvars[0].vval.v_number; + if (find_job(id, false)) { // FIXME f_jobstop(argvars, rettv, NULL); } else { - rettv->vval.v_number = channel_close(argvars[0].vval.v_number); + rettv->vval.v_number = channel_close(id); } } @@ -16689,11 +16658,11 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) } uint16_t term_width = MAX(0, curwin->w_width - win_col_off(curwin)); - TerminalJobData *data = common_job_init(argv, on_stdout, on_stderr, on_exit, + Channel *data = common_job_init(argv, on_stdout, on_stderr, on_exit, true, false, false, cwd); - data->proc.pty.width = term_width; - data->proc.pty.height = curwin->w_height; - data->proc.pty.term_name = xstrdup("xterm-256color"); + data->stream.pty.width = term_width; + data->stream.pty.height = curwin->w_height; + data->stream.pty.term_name = xstrdup("xterm-256color"); if (!common_job_start(data, rettv)) { return; } @@ -16705,7 +16674,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) topts.resize_cb = term_resize; topts.close_cb = term_close; - int pid = data->proc.pty.process.pid; + int pid = data->stream.pty.process.pid; char buf[1024]; // format the title with the pid to conform with the term:// URI @@ -16725,7 +16694,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) Terminal *term = terminal_open(topts); data->term = term; - data->refcount++; + channel_incref(data); return; } @@ -16760,30 +16729,6 @@ bool callback_from_typval(Callback *const callback, typval_T *const arg) return true; } -/// Unref/free callback -void callback_free(Callback *const callback) - FUNC_ATTR_NONNULL_ALL -{ - switch (callback->type) { - case kCallbackFuncref: { - func_unref(callback->data.funcref); - xfree(callback->data.funcref); - break; - } - case kCallbackPartial: { - partial_unref(callback->data.partial); - break; - } - case kCallbackNone: { - break; - } - default: { - abort(); - } - } - callback->type = kCallbackNone; -} - bool callback_call(Callback *const callback, const int argcount_in, typval_T *const argvars_in, typval_T *const rettv) FUNC_ATTR_NONNULL_ALL @@ -22402,7 +22347,7 @@ char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub, return ret; } -static inline TerminalJobData *common_job_init(char **argv, +static inline Channel *common_job_init(char **argv, Callback on_stdout, Callback on_stderr, Callback on_exit, @@ -22411,25 +22356,18 @@ static inline TerminalJobData *common_job_init(char **argv, bool detach, const char *cwd) { - TerminalJobData *data = xcalloc(1, sizeof(TerminalJobData)); - data->stopped = false; + Channel *data = channel_alloc(kChannelStreamProc); data->on_stdout = on_stdout; data->on_stderr = on_stderr; data->on_exit = on_exit; - data->events = multiqueue_new_child(main_loop.events); - data->rpc = rpc; + data->is_rpc = rpc; if (pty) { - data->proc.pty = pty_process_init(&main_loop, data); + data->stream.pty = pty_process_init(&main_loop, data); } else { - data->proc.uv = libuv_process_init(&main_loop, data); + data->stream.uv = libuv_process_init(&main_loop, data); } - Process *proc = (Process *)&data->proc; + Process *proc = (Process *)&data->stream.proc; proc->argv = argv; - proc->in = &data->in; - proc->out = &data->out; - if (!pty) { - proc->err = &data->err; - } proc->cb = eval_job_process_exit_cb; proc->events = data->events; proc->detach = detach; @@ -22456,80 +22394,66 @@ static inline bool common_job_callbacks(dict_T *vopts, Callback *on_stdout, return false; } -static inline bool common_job_start(TerminalJobData *data, typval_T *rettv) +static inline bool common_job_start(Channel *data, typval_T *rettv) { - Process *proc = (Process *)&data->proc; + Process *proc = (Process *)&data->stream.proc; if (proc->type == kProcessTypePty && proc->detach) { EMSG2(_(e_invarg2), "terminal/pty job cannot be detached"); - xfree(data->proc.pty.term_name); + xfree(data->stream.pty.term_name); shell_free_argv(proc->argv); - free_term_job_data_event((void **)&data); + channel_decref(data); return false; } - data->id = next_chan_id++; - pmap_put(uint64_t)(jobs, data->id, data); - data->refcount++; char *cmd = xstrdup(proc->argv[0]); - int status = process_spawn(proc); + bool has_out, has_err; + if (proc->type == kProcessTypePty) { + has_out = true; + has_err = false; + } else { + has_out = data->is_rpc || data->on_stdout.type != kCallbackNone; + has_err = data->on_stderr.type != kCallbackNone; + } + int status = process_spawn(proc, true, has_out, has_err); if (status) { EMSG3(_(e_jobspawn), os_strerror(status), cmd); xfree(cmd); if (proc->type == kProcessTypePty) { - xfree(data->proc.pty.term_name); + xfree(data->stream.pty.term_name); } rettv->vval.v_number = proc->status; - term_job_data_decref(data); + channel_decref(data); return false; } xfree(cmd); - if (data->rpc) { - // the rpc channel takes over the in and out streams - channel_from_process(proc, data->id); + if (data->is_rpc) { + // the rpc takes over the in and out streams + rpc_start(data); } else { - wstream_init(proc->in, 0); - if (proc->out) { - rstream_init(proc->out, 0); - rstream_start(proc->out, on_job_stdout, data); + wstream_init(&proc->in, 0); + if (has_out) { + rstream_init(&proc->out, 0); + rstream_start(&proc->out, on_job_stdout, data); } } - if (proc->err) { - rstream_init(proc->err, 0); - rstream_start(proc->err, on_job_stderr, data); + if (has_err) { + rstream_init(&proc->err, 0); + rstream_start(&proc->err, on_job_stderr, data); } rettv->vval.v_number = data->id; return true; } -static inline void free_term_job_data_event(void **argv) -{ - TerminalJobData *data = argv[0]; - callback_free(&data->on_stdout); - callback_free(&data->on_stderr); - callback_free(&data->on_exit); - - multiqueue_free(data->events); - pmap_del(uint64_t)(jobs, data->id); - xfree(data); -} - -static inline void free_term_job_data(TerminalJobData *data) -{ - // data->queue may still be used after this function returns(process_wait), so - // only free in the next event loop iteration - multiqueue_put(main_loop.fast_events, free_term_job_data_event, 1, data); -} - // vimscript job callbacks must be executed on Nvim main loop -static inline void process_job_event(TerminalJobData *data, Callback *callback, +static inline void process_job_event(Channel *data, Callback *callback, const char *type, char *buf, size_t count, int status) { - JobEvent event_data; + ChannelEvent event_data; event_data.received = NULL; if (buf) { event_data.received = tv_list_alloc(); @@ -22566,18 +22490,18 @@ static inline void process_job_event(TerminalJobData *data, Callback *callback, static void on_job_stdout(Stream *stream, RBuffer *buf, size_t count, void *job, bool eof) { - TerminalJobData *data = job; + Channel *data = job; on_job_output(stream, job, buf, count, eof, &data->on_stdout, "stdout"); } static void on_job_stderr(Stream *stream, RBuffer *buf, size_t count, void *job, bool eof) { - TerminalJobData *data = job; + Channel *data = job; on_job_output(stream, job, buf, count, eof, &data->on_stderr, "stderr"); } -static void on_job_output(Stream *stream, TerminalJobData *data, RBuffer *buf, +static void on_job_output(Stream *stream, Channel *data, RBuffer *buf, size_t count, bool eof, Callback *callback, const char *type) { @@ -22604,14 +22528,14 @@ static void on_job_output(Stream *stream, TerminalJobData *data, RBuffer *buf, static void eval_job_process_exit_cb(Process *proc, int status, void *d) { - TerminalJobData *data = d; - if (data->term && !data->exited) { - data->exited = true; + Channel *data = d; + if (data->term && !data->stream.proc.exited) { + data->stream.proc.exited = true; char msg[sizeof("\r\n[Process exited ]") + NUMBUFLEN]; snprintf(msg, sizeof msg, "\r\n[Process exited %d]", proc->status); terminal_close(data->term, msg); } - if (data->rpc) { + if (data->is_rpc) { channel_process_exit(data->id, status); } @@ -22621,58 +22545,51 @@ static void eval_job_process_exit_cb(Process *proc, int status, void *d) process_job_event(data, &data->on_exit, "exit", NULL, 0, status); - term_job_data_decref(data); + channel_decref(data); } static void term_write(char *buf, size_t size, void *d) { - TerminalJobData *job = d; - if (job->in.closed) { + Channel *job = d; + if (job->stream.proc.in.closed) { // If the backing stream was closed abruptly, there may be write events // ahead of the terminal close event. Just ignore the writes. ILOG("write failed: stream is closed"); return; } WBuffer *wbuf = wstream_new_buffer(xmemdup(buf, size), size, 1, xfree); - wstream_write(&job->in, wbuf); + wstream_write(&job->stream.proc.in, wbuf); } static void term_resize(uint16_t width, uint16_t height, void *d) { - TerminalJobData *data = d; - pty_process_resize(&data->proc.pty, width, height); + Channel *data = d; + pty_process_resize(&data->stream.pty, width, height); } static inline void term_delayed_free(void **argv) { - TerminalJobData *j = argv[0]; - if (j->in.pending_reqs || j->out.pending_reqs || j->err.pending_reqs) { + Channel *j = argv[0]; + if (j->stream.proc.in.pending_reqs || j->stream.proc.out.pending_reqs) { multiqueue_put(j->events, term_delayed_free, 1, j); return; } terminal_destroy(j->term); - term_job_data_decref(j); + channel_decref(j); } static void term_close(void *d) { - TerminalJobData *data = d; - if (!data->exited) { - data->exited = true; - process_stop((Process *)&data->proc); + Channel *data = d; + if (!data->stream.proc.exited) { + data->stream.proc.exited = true; + process_stop((Process *)&data->stream.proc); } multiqueue_put(data->events, term_delayed_free, 1, data); } -static void term_job_data_decref(TerminalJobData *data) -{ - if (!(--data->refcount)) { - free_term_job_data(data); - } -} - -static void on_job_event(JobEvent *ev) +static void on_job_event(ChannelEvent *ev) { if (!ev->callback) { return; @@ -22704,15 +22621,24 @@ static void on_job_event(JobEvent *ev) tv_clear(&rettv); } -static TerminalJobData *find_job(uint64_t id) +static Channel *find_job(uint64_t id, bool show_error) { - TerminalJobData *data = pmap_get(uint64_t)(jobs, id); - if (!data || data->stopped) { + Channel *data = find_channel(id); + if (!data || data->streamtype != kChannelStreamProc + || process_is_stopped(&data->stream.proc)) { + if (show_error) { + if (data && data->streamtype != kChannelStreamProc) { + EMSG(_(e_invchanjob)); + } else { + EMSG(_(e_invchan)); + } + } return NULL; } return data; } + static void script_host_eval(char *name, typval_T *argvars, typval_T *rettv) { if (check_restricted() || check_secure()) { diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 262ea922ef..99382d2a24 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -847,6 +847,30 @@ bool tv_callback_equal(const Callback *const cb1, const Callback *const cb2) return false; } +/// Unref/free callback +void callback_free(Callback *const callback) + FUNC_ATTR_NONNULL_ALL +{ + switch (callback->type) { + case kCallbackFuncref: { + func_unref(callback->data.funcref); + xfree(callback->data.funcref); + break; + } + case kCallbackPar |