Skip to content

Commit d6051b5

Browse files
committed
patch 7.4.1447
Problem: Memory leak when using ch_read(). (Dominique Pelle) No log message when stopping a job and a few other situations. Too many "Nothing to read" messages. Channels are not freed. Solution: Free the listtv. Add more log messages. Remove "Nothing to read" message. Remove the channel from the job when its refcount becomes zero.
1 parent 80e7884 commit d6051b5

File tree

3 files changed

+45
-9
lines changed

3 files changed

+45
-9
lines changed

src/channel.c

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -311,8 +311,11 @@ add_channel(void)
311311
}
312312

313313
/*
314+
* Called when the refcount of a channel is zero.
314315
* Return TRUE if "channel" has a callback and the associated job wasn't
315316
* killed.
317+
* If the job was killed the channel is not expected to work anymore.
318+
* If there is no callback then nobody can get readahead.
316319
*/
317320
static int
318321
channel_still_useful(channel_T *channel)
@@ -347,6 +350,7 @@ channel_free(channel_T *channel)
347350
{
348351
channel_close(channel, TRUE);
349352
channel_clear(channel);
353+
ch_log(channel, "Freeing channel");
350354
if (channel->ch_next != NULL)
351355
channel->ch_next->ch_prev = channel->ch_prev;
352356
if (channel->ch_prev == NULL)
@@ -775,6 +779,10 @@ channel_set_pipes(channel_T *channel, sock_T in, sock_T out, sock_T err)
775779
}
776780
#endif
777781

782+
/*
783+
* Sets the job the channel is associated with.
784+
* This does not keep a refcount, when the job is freed ch_job is cleared.
785+
*/
778786
void
779787
channel_set_job(channel_T *channel, job_T *job)
780788
{
@@ -1421,7 +1429,10 @@ may_invoke_callback(channel_T *channel, int part)
14211429
if (callback == NULL && buffer == NULL)
14221430
{
14231431
while ((msg = channel_get(channel, part)) != NULL)
1432+
{
1433+
ch_logs(channel, "Dropping message '%s'", (char *)msg);
14241434
vim_free(msg);
1435+
}
14251436
return FALSE;
14261437
}
14271438

@@ -1475,7 +1486,8 @@ may_invoke_callback(channel_T *channel, int part)
14751486
{
14761487
if (item->cq_seq_nr == seq_nr)
14771488
{
1478-
ch_log(channel, "Invoking one-time callback");
1489+
ch_logs(channel, "Invoking one-time callback '%s'",
1490+
(char *)item->cq_callback);
14791491
/* Remove the item from the list first, if the callback
14801492
* invokes ch_close() the list will be cleared. */
14811493
remove_cb_node(head, item);
@@ -1488,7 +1500,7 @@ may_invoke_callback(channel_T *channel, int part)
14881500
item = item->cq_next;
14891501
}
14901502
if (!done)
1491-
ch_log(channel, "Dropping message without callback");
1503+
ch_logn(channel, "Dropping message %d without callback", seq_nr);
14921504
}
14931505
else if (callback != NULL || buffer != NULL)
14941506
{
@@ -1539,6 +1551,8 @@ may_invoke_callback(channel_T *channel, int part)
15391551
invoke_callback(channel, callback, argv);
15401552
}
15411553
}
1554+
else if (msg != NULL)
1555+
ch_logs(channel, "Dropping message '%s'", (char *)msg);
15421556
else
15431557
ch_log(channel, "Dropping message");
15441558

@@ -1637,6 +1651,8 @@ channel_close(channel_T *channel, int invoke_close_cb)
16371651

16381652
/* invoke the close callback; increment the refcount to avoid it
16391653
* being freed halfway */
1654+
ch_logs(channel, "Invoking close callback %s",
1655+
(char *)channel->ch_close_cb);
16401656
argv[0].v_type = VAR_CHANNEL;
16411657
argv[0].vval.v_channel = channel;
16421658
++channel->ch_refcount;
@@ -1704,6 +1720,7 @@ channel_clear_one(channel_T *channel, int part)
17041720
void
17051721
channel_clear(channel_T *channel)
17061722
{
1723+
ch_log(channel, "Clearing channel");
17071724
channel_clear_one(channel, PART_SOCK);
17081725
#ifdef CHANNEL_PIPES
17091726
channel_clear_one(channel, PART_OUT);
@@ -1721,6 +1738,7 @@ channel_free_all(void)
17211738
{
17221739
channel_T *channel;
17231740

1741+
ch_log(NULL, "channel_free_all()");
17241742
for (channel = first_channel; channel != NULL; channel = channel->ch_next)
17251743
channel_clear(channel);
17261744
}
@@ -1798,7 +1816,6 @@ channel_wait(channel_T *channel, sock_T fd, int timeout)
17981816
return OK;
17991817
#endif
18001818
}
1801-
ch_log(channel, "Nothing to read");
18021819
return FAIL;
18031820
}
18041821

@@ -1970,11 +1987,11 @@ channel_read_block(channel_T *channel, int part, int timeout)
19701987
*/
19711988
int
19721989
channel_read_json_block(
1973-
channel_T *channel,
1974-
int part,
1975-
int timeout,
1976-
int id,
1977-
typval_T **rettv)
1990+
channel_T *channel,
1991+
int part,
1992+
int timeout,
1993+
int id,
1994+
typval_T **rettv)
19781995
{
19791996
int more;
19801997
sock_T fd;

src/eval.c

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7741,7 +7741,7 @@ get_dict_tv(char_u **arg, typval_T *rettv, int evaluate)
77417741
/*
77427742
* Decrement the reference count on "channel" and maybe free it when it goes
77437743
* down to zero. Don't free it if there is a pending action.
7744-
* Returns TRUE when the channel was freed.
7744+
* Returns TRUE when the channel is no longer referenced.
77457745
*/
77467746
int
77477747
channel_unref(channel_T *channel)
@@ -7762,6 +7762,7 @@ static job_T *first_job = NULL;
77627762
job_free(job_T *job)
77637763
{
77647764
# ifdef FEAT_CHANNEL
7765+
ch_log(job->jv_channel, "Freeing job");
77657766
if (job->jv_channel != NULL)
77667767
{
77677768
/* The link from the channel to the job doesn't count as a reference,
@@ -7796,7 +7797,17 @@ job_unref(job_T *job)
77967797
* "stoponexit" flag or an exit callback. */
77977798
if (job->jv_status != JOB_STARTED
77987799
|| (job->jv_stoponexit == NULL && job->jv_exit_cb == NULL))
7800+
{
77997801
job_free(job);
7802+
}
7803+
else if (job->jv_channel != NULL)
7804+
{
7805+
/* Do remove the link to the channel, otherwise it hangs
7806+
* around until Vim exits. See job_free() for refcount. */
7807+
job->jv_channel->ch_job = NULL;
7808+
channel_unref(job->jv_channel);
7809+
job->jv_channel = NULL;
7810+
}
78007811
}
78017812
}
78027813

@@ -10467,7 +10478,10 @@ common_channel_read(typval_T *argvars, typval_T *rettv, int raw)
1046710478
id = opt.jo_id;
1046810479
channel_read_json_block(channel, part, timeout, id, &listtv);
1046910480
if (listtv != NULL)
10481+
{
1047010482
*rettv = *listtv;
10483+
vim_free(listtv);
10484+
}
1047110485
else
1047210486
{
1047310487
rettv->v_type = VAR_SPECIAL;
@@ -15292,6 +15306,9 @@ f_job_stop(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1529215306
return;
1529315307
}
1529415308
}
15309+
# ifdef FEAT_CHANNEL
15310+
ch_logs(job->jv_channel, "Stopping job with '%s'", (char *)arg);
15311+
# endif
1529515312
if (mch_stop_job(job, arg) == FAIL)
1529615313
rettv->vval.v_number = 0;
1529715314
else

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,8 @@ static char *(features[]) =
743743

744744
static int included_patches[] =
745745
{ /* Add new patch number below this line */
746+
/**/
747+
1447,
746748
/**/
747749
1446,
748750
/**/

0 commit comments

Comments
 (0)