patch 7.4.1315
Problem: Using a channel handle does not allow for freeing it when unused.
Solution: Add the Channel variable type.
diff --git a/src/eval.c b/src/eval.c
index 4a939a7..b79bb02 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -509,6 +509,7 @@
static void f_ch_readraw(typval_T *argvars, typval_T *rettv);
static void f_ch_sendexpr(typval_T *argvars, typval_T *rettv);
static void f_ch_sendraw(typval_T *argvars, typval_T *rettv);
+static void f_ch_status(typval_T *argvars, typval_T *rettv);
#endif
static void f_changenr(typval_T *argvars, typval_T *rettv);
static void f_char2nr(typval_T *argvars, typval_T *rettv);
@@ -3084,6 +3085,7 @@
case VAR_FUNC:
case VAR_SPECIAL:
case VAR_JOB:
+ case VAR_CHANNEL:
break;
case VAR_LIST:
@@ -3863,6 +3865,7 @@
case VAR_FLOAT:
case VAR_SPECIAL:
case VAR_JOB:
+ case VAR_CHANNEL:
break;
case VAR_LIST:
@@ -5359,6 +5362,7 @@
#endif
case VAR_SPECIAL:
case VAR_JOB:
+ case VAR_CHANNEL:
if (verbose)
EMSG(_("E909: Cannot index a special variable"));
return FAIL;
@@ -5471,6 +5475,7 @@
case VAR_FLOAT:
case VAR_SPECIAL:
case VAR_JOB:
+ case VAR_CHANNEL:
break; /* not evaluating, skipping over subscript */
case VAR_NUMBER:
@@ -6224,6 +6229,10 @@
#ifdef FEAT_JOB
return tv1->vval.v_job == tv2->vval.v_job;
#endif
+ case VAR_CHANNEL:
+#ifdef FEAT_CHANNEL
+ return tv1->vval.v_channel == tv2->vval.v_channel;
+#endif
case VAR_UNKNOWN:
break;
}
@@ -7719,12 +7728,26 @@
return OK;
}
+#ifdef FEAT_CHANNEL
+ static void
+channel_unref(channel_T *channel)
+{
+ if (channel != NULL && --channel->ch_refcount <= 0)
+ channel_free(channel);
+}
+#endif
+
#ifdef FEAT_JOB
static void
job_free(job_T *job)
{
- if (job->jv_channel >= 0)
- channel_close(job->jv_channel);
+ if (job->jv_channel != NULL)
+ {
+ /* The channel doesn't count as a references for the job, we need to
+ * NULL the reference when the job is freed. */
+ job->jv_channel->ch_job = NULL;
+ channel_unref(job->jv_channel);
+ }
mch_clear_job(job);
vim_free(job);
}
@@ -7746,9 +7769,7 @@
job = (job_T *)alloc_clear(sizeof(job_T));
if (job != NULL)
- {
job->jv_refcount = 1;
- }
return job;
}
@@ -7850,6 +7871,7 @@
case VAR_NUMBER:
case VAR_UNKNOWN:
case VAR_JOB:
+ case VAR_CHANNEL:
*tofree = NULL;
r = get_tv_string_buf(tv, numbuf);
break;
@@ -7906,6 +7928,7 @@
case VAR_DICT:
case VAR_SPECIAL:
case VAR_JOB:
+ case VAR_CHANNEL:
case VAR_UNKNOWN:
break;
}
@@ -8093,6 +8116,7 @@
{"ch_readraw", 1, 2, f_ch_readraw},
{"ch_sendexpr", 2, 3, f_ch_sendexpr},
{"ch_sendraw", 2, 3, f_ch_sendraw},
+ {"ch_status", 1, 1, f_ch_status},
#endif
{"changenr", 0, 0, f_changenr},
{"char2nr", 1, 2, f_char2nr},
@@ -9781,27 +9805,27 @@
#ifdef FEAT_CHANNEL
/*
- * Get the channel index from the handle argument.
- * Returns -1 if the handle is invalid or the channel is closed.
+ * Get the channel from the argument.
+ * Returns NULL if the handle is invalid.
*/
- static int
+ static channel_T *
get_channel_arg(typval_T *tv)
{
- int ch_idx;
+ channel_T *channel;
- if (tv->v_type != VAR_NUMBER)
+ if (tv->v_type != VAR_CHANNEL)
{
EMSG2(_(e_invarg2), get_tv_string(tv));
- return -1;
+ return NULL;
}
- ch_idx = tv->vval.v_number;
+ channel = tv->vval.v_channel;
- if (!channel_can_write_to(ch_idx))
+ if (channel == NULL || !channel_is_open(channel))
{
- EMSGN(_("E906: not an open channel"), ch_idx);
- return -1;
+ EMSG(_("E906: not an open channel"));
+ return NULL;
}
- return ch_idx;
+ return channel;
}
/*
@@ -9810,10 +9834,10 @@
static void
f_ch_close(typval_T *argvars, typval_T *rettv UNUSED)
{
- int ch_idx = get_channel_arg(&argvars[0]);
+ channel_T *channel = get_channel_arg(&argvars[0]);
- if (ch_idx >= 0)
- channel_close(ch_idx);
+ if (channel != NULL)
+ channel_close(channel);
}
/*
@@ -9873,10 +9897,11 @@
int waittime = 0;
int timeout = 2000;
ch_mode_T ch_mode = MODE_JSON;
- int ch_idx;
+ channel_T *channel;
/* default: fail */
- rettv->vval.v_number = -1;
+ rettv->v_type = VAR_CHANNEL;
+ rettv->vval.v_channel = NULL;
address = get_tv_string(&argvars[0]);
if (argvars[1].v_type != VAR_UNKNOWN
@@ -9936,15 +9961,15 @@
return;
}
- ch_idx = channel_open((char *)address, port, waittime, NULL);
- if (ch_idx >= 0)
+ channel = channel_open((char *)address, port, waittime, NULL);
+ if (channel != NULL)
{
- channel_set_json_mode(ch_idx, ch_mode);
- channel_set_timeout(ch_idx, timeout);
+ channel_set_json_mode(channel, ch_mode);
+ channel_set_timeout(channel, timeout);
if (callback != NULL && *callback != NUL)
- channel_set_callback(ch_idx, callback);
+ channel_set_callback(channel, callback);
}
- rettv->vval.v_number = ch_idx;
+ rettv->vval.v_channel = channel;
}
/*
@@ -9953,53 +9978,65 @@
static void
f_ch_readraw(typval_T *argvars, typval_T *rettv)
{
- int ch_idx;
+ channel_T *channel;
/* return an empty string by default */
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
- ch_idx = get_channel_arg(&argvars[0]);
- if (ch_idx < 0)
+ channel = get_channel_arg(&argvars[0]);
+ if (channel != NULL)
+ rettv->vval.v_string = channel_read_block(channel);
+}
+
+/*
+ * "ch_status()" function
+ */
+ static void
+f_ch_status(typval_T *argvars, typval_T *rettv)
+{
+ /* return an empty string by default */
+ rettv->v_type = VAR_STRING;
+
+ if (argvars[0].v_type != VAR_CHANNEL)
{
- EMSG(_(e_invarg));
- return;
+ EMSG2(_(e_invarg2), get_tv_string(&argvars[0]));
+ rettv->vval.v_string = NULL;
}
- rettv->vval.v_string = channel_read_block(ch_idx);
+ else
+ rettv->vval.v_string = vim_strsave(
+ (char_u *)channel_status(argvars[0].vval.v_channel));
}
/*
* common for "sendexpr()" and "sendraw()"
- * Returns the channel index if the caller should read the response.
- * Otherwise returns -1.
+ * Returns the channel if the caller should read the response.
+ * Otherwise returns NULL.
*/
- static int
+ static channel_T *
send_common(typval_T *argvars, char_u *text, int id, char *fun)
{
- int ch_idx;
+ channel_T *channel;
char_u *callback = NULL;
- ch_idx = get_channel_arg(&argvars[0]);
- if (ch_idx < 0)
- {
- EMSG(_(e_invarg));
- return -1;
- }
+ channel = get_channel_arg(&argvars[0]);
+ if (channel == NULL)
+ return NULL;
if (argvars[2].v_type != VAR_UNKNOWN)
{
callback = get_callback(&argvars[2]);
if (callback == NULL)
- return -1;
+ return NULL;
}
/* Set the callback. An empty callback means no callback and not reading
* the response. */
if (callback != NULL && *callback != NUL)
- channel_set_req_callback(ch_idx, callback, id);
+ channel_set_req_callback(channel, callback, id);
- if (channel_send(ch_idx, text, fun) == OK && callback == NULL)
- return ch_idx;
- return -1;
+ if (channel_send(channel, text, fun) == OK && callback == NULL)
+ return channel;
+ return NULL;
}
/*
@@ -10010,7 +10047,7 @@
{
char_u *text;
typval_T *listtv;
- int ch_idx;
+ channel_T *channel;
int id;
ch_mode_T ch_mode;
@@ -10018,14 +10055,11 @@
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
- ch_idx = get_channel_arg(&argvars[0]);
- if (ch_idx < 0)
- {
- EMSG(_(e_invarg));
+ channel = get_channel_arg(&argvars[0]);
+ if (channel == NULL)
return;
- }
- ch_mode = channel_get_mode(ch_idx);
+ ch_mode = channel_get_mode(channel);
if (ch_mode == MODE_RAW)
{
EMSG(_("E912: cannot use ch_sendexpr() with a raw channel"));
@@ -10038,11 +10072,11 @@
if (text == NULL)
return;
- ch_idx = send_common(argvars, text, id, "sendexpr");
+ channel = send_common(argvars, text, id, "sendexpr");
vim_free(text);
- if (ch_idx >= 0)
+ if (channel != NULL)
{
- if (channel_read_json_block(ch_idx, id, &listtv) == OK)
+ if (channel_read_json_block(channel, id, &listtv) == OK)
{
list_T *list = listtv->vval.v_list;
@@ -10050,7 +10084,7 @@
* avoid the value being freed. */
*rettv = list->lv_last->li_tv;
list->lv_last->li_tv.v_type = VAR_NUMBER;
- clear_tv(listtv);
+ free_tv(listtv);
}
}
}
@@ -10063,16 +10097,16 @@
{
char_u buf[NUMBUFLEN];
char_u *text;
- int ch_idx;
+ channel_T *channel;
/* return an empty string by default */
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
text = get_tv_string_buf(&argvars[1], buf);
- ch_idx = send_common(argvars, text, 0, "sendraw");
- if (ch_idx >= 0)
- rettv->vval.v_string = channel_read_block(ch_idx);
+ channel = send_common(argvars, text, 0, "sendraw");
+ if (channel != NULL)
+ rettv->vval.v_string = channel_read_block(channel);
}
#endif
@@ -10708,7 +10742,14 @@
case VAR_JOB:
#ifdef FEAT_JOB
- n = argvars[0].vval.v_job->jv_status != JOB_STARTED;
+ n = argvars[0].vval.v_job == NULL
+ || argvars[0].vval.v_job->jv_status != JOB_STARTED;
+ break;
+#endif
+ case VAR_CHANNEL:
+#ifdef FEAT_CMDWIN
+ n = argvars[0].vval.v_channel == NULL
+ || !channel_is_open(argvars[0].vval.v_channel);
break;
#endif
case VAR_UNKNOWN:
@@ -14366,8 +14407,10 @@
{
job_T *job = argvars[0].vval.v_job;
- rettv->v_type = VAR_NUMBER;
- rettv->vval.v_number = job->jv_channel;
+ rettv->v_type = VAR_CHANNEL;
+ rettv->vval.v_channel = job->jv_channel;
+ if (job->jv_channel != NULL)
+ ++job->jv_channel->ch_refcount;
}
}
@@ -14659,6 +14702,7 @@
case VAR_FLOAT:
case VAR_FUNC:
case VAR_JOB:
+ case VAR_CHANNEL:
EMSG(_("E701: Invalid type for len()"));
break;
}
@@ -20040,7 +20084,8 @@
else
n = 7;
break;
- case VAR_JOB: n = 8; break;
+ case VAR_JOB: n = 8; break;
+ case VAR_CHANNEL: n = 9; break;
case VAR_UNKNOWN:
EMSG2(_(e_intern2), "f_type(UNKNOWN)");
n = -1;
@@ -21412,6 +21457,11 @@
job_unref(varp->vval.v_job);
break;
#endif
+ case VAR_CHANNEL:
+#ifdef FEAT_CHANNEL
+ channel_unref(varp->vval.v_channel);
+ break;
+#endif
case VAR_NUMBER:
case VAR_FLOAT:
case VAR_UNKNOWN:
@@ -21462,6 +21512,11 @@
varp->vval.v_job = NULL;
#endif
break;
+ case VAR_CHANNEL:
+#ifdef FEAT_CHANNEL
+ channel_unref(varp->vval.v_channel);
+ varp->vval.v_channel = NULL;
+#endif
case VAR_UNKNOWN:
break;
}
@@ -21531,6 +21586,11 @@
EMSG(_("E910: Using a Job as a Number"));
break;
#endif
+ case VAR_CHANNEL:
+#ifdef FEAT_CHANNEL
+ EMSG(_("E913: Using a Channel as a Number"));
+ break;
+#endif
case VAR_UNKNOWN:
EMSG2(_(e_intern2), "get_tv_number(UNKNOWN)");
break;
@@ -21572,6 +21632,11 @@
EMSG(_("E911: Using a Job as a Float"));
break;
# endif
+ case VAR_CHANNEL:
+# ifdef FEAT_CHANNEL
+ EMSG(_("E914: Using a Channel as a Float"));
+ break;
+# endif
case VAR_UNKNOWN:
EMSG2(_(e_intern2), "get_tv_float(UNKNOWN)");
break;
@@ -21707,6 +21772,18 @@
}
#endif
break;
+ case VAR_CHANNEL:
+#ifdef FEAT_CHANNEL
+ {
+ channel_T *channel = varp->vval.v_channel;
+ char *status = channel_status(channel);
+
+ vim_snprintf((char *)buf, NUMBUFLEN,
+ "channel %d %s", channel->ch_id, status);
+ return buf;
+ }
+#endif
+ break;
case VAR_UNKNOWN:
EMSG(_("E908: using an invalid value as a String"));
break;
@@ -22337,6 +22414,12 @@
++to->vval.v_job->jv_refcount;
break;
#endif
+ case VAR_CHANNEL:
+#ifdef FEAT_CHANNEL
+ to->vval.v_channel = from->vval.v_channel;
+ ++to->vval.v_channel->ch_refcount;
+ break;
+#endif
case VAR_STRING:
case VAR_FUNC:
if (from->vval.v_string == NULL)
@@ -22404,6 +22487,7 @@
case VAR_FUNC:
case VAR_SPECIAL:
case VAR_JOB:
+ case VAR_CHANNEL:
copy_tv(from, to);
break;
case VAR_LIST:
@@ -25076,6 +25160,7 @@
case VAR_UNKNOWN:
case VAR_FUNC:
case VAR_JOB:
+ case VAR_CHANNEL:
continue;
}
fprintf(fp, "!%s\t%s\t", this_var->di_key, s);