Mercurial > vim
comparison src/channel.c @ 9011:72a597e9e36d v7.4.1791
commit https://github.com/vim/vim/commit/674127e1801fd02ff07dddf0dc3bf0d8cce68997
Author: Bram Moolenaar <Bram@vim.org>
Date: Tue Apr 26 20:30:07 2016 +0200
patch 7.4.1791
Problem: Channel could be garbage collected too early.
Solution: Don't free a channel or remove it from a job when it is still
useful.
author | Christian Brabandt <cb@256bit.org> |
---|---|
date | Tue, 26 Apr 2016 20:45:05 +0200 |
parents | d5c6f1c5cd28 |
children | 5abf6f38cbbb |
comparison
equal
deleted
inserted
replaced
9010:4e61ef434a1e | 9011:72a597e9e36d |
---|---|
437 { | 437 { |
438 int did_free = FALSE; | 438 int did_free = FALSE; |
439 channel_T *ch; | 439 channel_T *ch; |
440 | 440 |
441 for (ch = first_channel; ch != NULL; ch = ch->ch_next) | 441 for (ch = first_channel; ch != NULL; ch = ch->ch_next) |
442 if ((ch->ch_copyID & mask) != (copyID & mask)) | 442 if (!channel_still_useful(ch) |
443 && (ch->ch_copyID & mask) != (copyID & mask)) | |
443 { | 444 { |
444 /* Free the channel and ordinary items it contains, but don't | 445 /* Free the channel and ordinary items it contains, but don't |
445 * recurse into Lists, Dictionaries etc. */ | 446 * recurse into Lists, Dictionaries etc. */ |
446 channel_free_contents(ch); | 447 channel_free_contents(ch); |
447 did_free = TRUE; | 448 did_free = TRUE; |
456 channel_T *ch_next; | 457 channel_T *ch_next; |
457 | 458 |
458 for (ch = first_channel; ch != NULL; ch = ch_next) | 459 for (ch = first_channel; ch != NULL; ch = ch_next) |
459 { | 460 { |
460 ch_next = ch->ch_next; | 461 ch_next = ch->ch_next; |
461 if ((ch->ch_copyID & mask) != (copyID & mask)) | 462 if (!channel_still_useful(ch) |
463 && (ch->ch_copyID & mask) != (copyID & mask)) | |
462 { | 464 { |
463 /* Free the channel struct itself. */ | 465 /* Free the channel struct itself. */ |
464 channel_free_channel(ch); | 466 channel_free_channel(ch); |
465 } | 467 } |
466 } | 468 } |
4077 } | 4079 } |
4078 } | 4080 } |
4079 | 4081 |
4080 /* | 4082 /* |
4081 * Return TRUE if the job should not be freed yet. Do not free the job when | 4083 * Return TRUE if the job should not be freed yet. Do not free the job when |
4082 * it has not ended yet and there is a "stoponexit" flag or an exit callback. | 4084 * it has not ended yet and there is a "stoponexit" flag, an exit callback |
4085 * or when the associated channel will do something with the job output. | |
4083 */ | 4086 */ |
4084 static int | 4087 static int |
4085 job_still_useful(job_T *job) | 4088 job_still_useful(job_T *job) |
4086 { | 4089 { |
4087 return job->jv_status == JOB_STARTED | 4090 return job->jv_status == JOB_STARTED |
4088 && (job->jv_stoponexit != NULL || job->jv_exit_cb != NULL); | 4091 && (job->jv_stoponexit != NULL || job->jv_exit_cb != NULL |
4092 || (job->jv_channel != NULL | |
4093 && channel_still_useful(job->jv_channel))); | |
4089 } | 4094 } |
4090 | 4095 |
4091 void | 4096 void |
4092 job_unref(job_T *job) | 4097 job_unref(job_T *job) |
4093 { | 4098 { |
4097 * "stoponexit" flag or an exit callback. */ | 4102 * "stoponexit" flag or an exit callback. */ |
4098 if (!job_still_useful(job)) | 4103 if (!job_still_useful(job)) |
4099 { | 4104 { |
4100 job_free(job); | 4105 job_free(job); |
4101 } | 4106 } |
4102 else if (job->jv_channel != NULL) | 4107 else if (job->jv_channel != NULL |
4108 && !channel_still_useful(job->jv_channel)) | |
4103 { | 4109 { |
4104 /* Do remove the link to the channel, otherwise it hangs | 4110 /* Do remove the link to the channel, otherwise it hangs |
4105 * around until Vim exits. See job_free() for refcount. */ | 4111 * around until Vim exits. See job_free() for refcount. */ |
4112 ch_log(job->jv_channel, "detaching channel from job"); | |
4106 job->jv_channel->ch_job = NULL; | 4113 job->jv_channel->ch_job = NULL; |
4107 channel_unref(job->jv_channel); | 4114 channel_unref(job->jv_channel); |
4108 job->jv_channel = NULL; | 4115 job->jv_channel = NULL; |
4109 } | 4116 } |
4110 } | 4117 } |