0
0
mirror of https://github.com/vim/vim.git synced 2025-11-16 23:24:03 -05:00

patch 7.4.1719

Problem:    Leaking memory when there is a cycle involving a job and a
            partial.
Solution:   Add a copyID to job and channel.  Set references in items referred
            by them.  Go through all jobs and channels to find unreferenced
            items.  Also, decrement reference counts when garbage collecting.
This commit is contained in:
Bram Moolenaar
2016-04-08 17:07:19 +02:00
parent d56374e25d
commit 107e1eef1d
11 changed files with 379 additions and 135 deletions

View File

@@ -367,6 +367,39 @@ channel_still_useful(channel_T *channel)
|| (channel->ch_part[PART_ERR].ch_callback != NULL && has_err_msg);
}
/*
* Close a channel and free all its resources.
*/
static void
channel_free_contents(channel_T *channel)
{
channel_close(channel, TRUE);
channel_clear(channel);
ch_log(channel, "Freeing channel");
}
static void
channel_free_channel(channel_T *channel)
{
if (channel->ch_next != NULL)
channel->ch_next->ch_prev = channel->ch_prev;
if (channel->ch_prev == NULL)
first_channel = channel->ch_next;
else
channel->ch_prev->ch_next = channel->ch_next;
vim_free(channel);
}
static void
channel_free(channel_T *channel)
{
if (!in_free_unref_items)
{
channel_free_contents(channel);
channel_free_channel(channel);
}
}
/*
* Close a channel and free all its resources if there is no further action
* possible, there is no callback to be invoked or the associated job was
@@ -397,22 +430,39 @@ channel_unref(channel_T *channel)
return FALSE;
}
/*
* Close a channel and free all its resources.
*/
void
channel_free(channel_T *channel)
int
free_unused_channels_contents(int copyID, int mask)
{
channel_close(channel, TRUE);
channel_clear(channel);
ch_log(channel, "Freeing channel");
if (channel->ch_next != NULL)
channel->ch_next->ch_prev = channel->ch_prev;
if (channel->ch_prev == NULL)
first_channel = channel->ch_next;
else
channel->ch_prev->ch_next = channel->ch_next;
vim_free(channel);
int did_free = FALSE;
channel_T *ch;
for (ch = first_channel; ch != NULL; ch = ch->ch_next)
if ((ch->ch_copyID & mask) != (copyID & mask))
{
/* Free the channel and ordinary items it contains, but don't
* recurse into Lists, Dictionaries etc. */
channel_free_contents(ch);
did_free = TRUE;
}
return did_free;
}
void
free_unused_channels(int copyID, int mask)
{
channel_T *ch;
channel_T *ch_next;
for (ch = first_channel; ch != NULL; ch = ch_next)
{
ch_next = ch->ch_next;
if ((ch->ch_copyID & mask) != (copyID & mask))
{
/* Free the channel and ordinary items it contains, but don't
* recurse into Lists, Dictionaries etc. */
channel_free_channel(ch);
}
}
}
#if defined(FEAT_GUI) || defined(PROTO)
@@ -2457,6 +2507,7 @@ channel_clear(channel_T *channel)
channel_clear_one(channel, PART_SOCK);
channel_clear_one(channel, PART_OUT);
channel_clear_one(channel, PART_ERR);
/* there is no callback or queue for PART_IN */
vim_free(channel->ch_callback);
channel->ch_callback = NULL;
partial_unref(channel->ch_partial);
@@ -3913,7 +3964,7 @@ get_channel_arg(typval_T *tv, int check_open)
static job_T *first_job = NULL;
static void
job_free(job_T *job)
job_free_contents(job_T *job)
{
ch_log(job->jv_channel, "Freeing job");
if (job->jv_channel != NULL)
@@ -3928,19 +3979,33 @@ job_free(job_T *job)
}
mch_clear_job(job);
vim_free(job->jv_stoponexit);
vim_free(job->jv_exit_cb);
partial_unref(job->jv_exit_partial);
}
static void
job_free_job(job_T *job)
{
if (job->jv_next != NULL)
job->jv_next->jv_prev = job->jv_prev;
if (job->jv_prev == NULL)
first_job = job->jv_next;
else
job->jv_prev->jv_next = job->jv_next;
vim_free(job->jv_stoponexit);
vim_free(job->jv_exit_cb);
partial_unref(job->jv_exit_partial);
vim_free(job);
}
static void
job_free(job_T *job)
{
if (!in_free_unref_items)
{
job_free_contents(job);
job_free_job(job);
}
}
void
job_unref(job_T *job)
{
@@ -3964,6 +4029,41 @@ job_unref(job_T *job)
}
}
int
free_unused_jobs_contents(int copyID, int mask)
{
int did_free = FALSE;
job_T *job;
for (job = first_job; job != NULL; job = job->jv_next)
if ((job->jv_copyID & mask) != (copyID & mask))
{
/* Free the channel and ordinary items it contains, but don't
* recurse into Lists, Dictionaries etc. */
job_free_contents(job);
did_free = TRUE;
}
return did_free;
}
void
free_unused_jobs(int copyID, int mask)
{
job_T *job;
job_T *job_next;
for (job = first_job; job != NULL; job = job_next)
{
job_next = job->jv_next;
if ((job->jv_copyID & mask) != (copyID & mask))
{
/* Free the channel and ordinary items it contains, but don't
* recurse into Lists, Dictionaries etc. */
job_free_job(job);
}
}
}
/*
* Allocate a job. Sets the refcount to one and sets options default.
*/