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 }