comparison src/channel.c @ 10386:d3f0946b4a80 v8.0.0087

commit https://github.com/vim/vim/commit/7df915d113ac1981792c50e8b000c9f5f784b78b Author: Bram Moolenaar <Bram@vim.org> Date: Thu Nov 17 17:25:32 2016 +0100 patch 8.0.0087 Problem: When the channel callback gets job info the job may already have been deleted. (lifepillar) Solution: Do not delete the job when the channel is still useful. (ichizok, closes #1242, closes #1245)
author Christian Brabandt <cb@256bit.org>
date Thu, 17 Nov 2016 17:30:04 +0100
parents 2055d3722c5b
children 42911b233245
comparison
equal deleted inserted replaced
10385:368468ef35cf 10386:d3f0946b4a80
4431 job_free_contents(job); 4431 job_free_contents(job);
4432 job_free_job(job); 4432 job_free_job(job);
4433 } 4433 }
4434 } 4434 }
4435 4435
4436 #if defined(EXITFREE) || defined(PROTO)
4437 void
4438 job_free_all(void)
4439 {
4440 while (first_job != NULL)
4441 job_free(first_job);
4442 }
4443 #endif
4444
4445 /*
4446 * Return TRUE if we need to check if the process of "job" has ended.
4447 */
4448 static int
4449 job_need_end_check(job_T *job)
4450 {
4451 return job->jv_status == JOB_STARTED
4452 && (job->jv_stoponexit != NULL || job->jv_exit_cb != NULL);
4453 }
4454
4455 /*
4456 * Return TRUE if the channel of "job" is still useful.
4457 */
4458 static int
4459 job_channel_still_useful(job_T *job)
4460 {
4461 return job->jv_channel != NULL && channel_still_useful(job->jv_channel);
4462 }
4463
4464 /*
4465 * Return TRUE if the job should not be freed yet. Do not free the job when
4466 * it has not ended yet and there is a "stoponexit" flag, an exit callback
4467 * or when the associated channel will do something with the job output.
4468 */
4469 static int
4470 job_still_useful(job_T *job)
4471 {
4472 return job_need_end_check(job) || job_channel_still_useful(job);
4473 }
4474
4475 /*
4476 * NOTE: Must call job_cleanup() only once right after the status of "job"
4477 * changed to JOB_ENDED (i.e. after job_status() returned "dead" first or
4478 * mch_detect_ended_job() returned non-NULL).
4479 */
4436 static void 4480 static void
4437 job_cleanup(job_T *job) 4481 job_cleanup(job_T *job)
4438 { 4482 {
4439 if (job->jv_status != JOB_ENDED) 4483 if (job->jv_status != JOB_ENDED)
4440 return; 4484 return;
4485
4486 /* Ready to cleanup the job. */
4487 job->jv_status = JOB_FINISHED;
4441 4488
4442 if (job->jv_exit_cb != NULL) 4489 if (job->jv_exit_cb != NULL)
4443 { 4490 {
4444 typval_T argv[3]; 4491 typval_T argv[3];
4445 typval_T rettv; 4492 typval_T rettv;
4446 int dummy; 4493 int dummy;
4447 4494
4448 /* invoke the exit callback; make sure the refcount is > 0 */ 4495 /* Invoke the exit callback. Make sure the refcount is > 0. */
4449 ++job->jv_refcount; 4496 ++job->jv_refcount;
4450 argv[0].v_type = VAR_JOB; 4497 argv[0].v_type = VAR_JOB;
4451 argv[0].vval.v_job = job; 4498 argv[0].vval.v_job = job;
4452 argv[1].v_type = VAR_NUMBER; 4499 argv[1].v_type = VAR_NUMBER;
4453 argv[1].vval.v_number = job->jv_exitval; 4500 argv[1].vval.v_number = job->jv_exitval;
4456 job->jv_exit_partial, NULL); 4503 job->jv_exit_partial, NULL);
4457 clear_tv(&rettv); 4504 clear_tv(&rettv);
4458 --job->jv_refcount; 4505 --job->jv_refcount;
4459 channel_need_redraw = TRUE; 4506 channel_need_redraw = TRUE;
4460 } 4507 }
4461 if (job->jv_refcount == 0) 4508
4462 { 4509 /* Do not free the job in case the close callback of the associated channel
4463 /* The job was already unreferenced, now that it ended it can be 4510 * isn't invoked yet and may get information by job_info(). */
4464 * freed. Careful: caller must not use "job" after this! */ 4511 if (job->jv_refcount == 0 && !job_channel_still_useful(job))
4512 {
4513 /* The job was already unreferenced and the associated channel was
4514 * detached, now that it ended it can be freed. Careful: caller must
4515 * not use "job" after this! */
4465 job_free(job); 4516 job_free(job);
4466 } 4517 }
4467 }
4468
4469 #if defined(EXITFREE) || defined(PROTO)
4470 void
4471 job_free_all(void)
4472 {
4473 while (first_job != NULL)
4474 job_free(first_job);
4475 }
4476 #endif
4477
4478 /*
4479 * Return TRUE if the job should not be freed yet. Do not free the job when
4480 * it has not ended yet and there is a "stoponexit" flag, an exit callback
4481 * or when the associated channel will do something with the job output.
4482 */
4483 static int
4484 job_still_useful(job_T *job)
4485 {
4486 return (job->jv_stoponexit != NULL || job->jv_exit_cb != NULL
4487 || (job->jv_channel != NULL
4488 && channel_still_useful(job->jv_channel)));
4489 }
4490
4491 static int
4492 job_still_alive(job_T *job)
4493 {
4494 return (job->jv_status == JOB_STARTED) && job_still_useful(job);
4495 } 4518 }
4496 4519
4497 /* 4520 /*
4498 * Mark references in jobs that are still useful. 4521 * Mark references in jobs that are still useful.
4499 */ 4522 */
4503 int abort = FALSE; 4526 int abort = FALSE;
4504 job_T *job; 4527 job_T *job;
4505 typval_T tv; 4528 typval_T tv;
4506 4529
4507 for (job = first_job; job != NULL; job = job->jv_next) 4530 for (job = first_job; job != NULL; job = job->jv_next)
4508 if (job_still_alive(job)) 4531 if (job_still_useful(job))
4509 { 4532 {
4510 tv.v_type = VAR_JOB; 4533 tv.v_type = VAR_JOB;
4511 tv.vval.v_job = job; 4534 tv.vval.v_job = job;
4512 abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL); 4535 abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL);
4513 } 4536 }
4514 return abort; 4537 return abort;
4515 } 4538 }
4516 4539
4540 /*
4541 * Dereference "job". Note that after this "job" may have been freed.
4542 */
4517 void 4543 void
4518 job_unref(job_T *job) 4544 job_unref(job_T *job)
4519 { 4545 {
4520 if (job != NULL && --job->jv_refcount <= 0) 4546 if (job != NULL && --job->jv_refcount <= 0)
4521 { 4547 {
4522 /* Do not free the job when it has not ended yet and there is a 4548 /* Do not free the job if there is a channel where the close callback
4523 * "stoponexit" flag or an exit callback. */ 4549 * may get the job info. */
4524 if (!job_still_alive(job)) 4550 if (!job_channel_still_useful(job))
4525 { 4551 {
4526 job_free(job); 4552 /* Do not free the job when it has not ended yet and there is a
4527 } 4553 * "stoponexit" flag or an exit callback. */
4528 else if (job->jv_channel != NULL 4554 if (!job_need_end_check(job))
4529 && !channel_still_useful(job->jv_channel)) 4555 {
4530 { 4556 job_free(job);
4531 /* Do remove the link to the channel, otherwise it hangs 4557 }
4532 * around until Vim exits. See job_free() for refcount. */ 4558 else if (job->jv_channel != NULL)
4533 ch_log(job->jv_channel, "detaching channel from job"); 4559 {
4534 job->jv_channel->ch_job = NULL; 4560 /* Do remove the link to the channel, otherwise it hangs
4535 channel_unref(job->jv_channel); 4561 * around until Vim exits. See job_free() for refcount. */
4536 job->jv_channel = NULL; 4562 ch_log(job->jv_channel, "detaching channel from job");
4563 job->jv_channel->ch_job = NULL;
4564 channel_unref(job->jv_channel);
4565 job->jv_channel = NULL;
4566 }
4537 } 4567 }
4538 } 4568 }
4539 } 4569 }
4540 4570
4541 int 4571 int
4544 int did_free = FALSE; 4574 int did_free = FALSE;
4545 job_T *job; 4575 job_T *job;
4546 4576
4547 for (job = first_job; job != NULL; job = job->jv_next) 4577 for (job = first_job; job != NULL; job = job->jv_next)
4548 if ((job->jv_copyID & mask) != (copyID & mask) 4578 if ((job->jv_copyID & mask) != (copyID & mask)
4549 && !job_still_alive(job)) 4579 && !job_still_useful(job))
4550 { 4580 {
4551 /* Free the channel and ordinary items it contains, but don't 4581 /* Free the channel and ordinary items it contains, but don't
4552 * recurse into Lists, Dictionaries etc. */ 4582 * recurse into Lists, Dictionaries etc. */
4553 job_free_contents(job); 4583 job_free_contents(job);
4554 did_free = TRUE; 4584 did_free = TRUE;
4564 4594
4565 for (job = first_job; job != NULL; job = job_next) 4595 for (job = first_job; job != NULL; job = job_next)
4566 { 4596 {
4567 job_next = job->jv_next; 4597 job_next = job->jv_next;
4568 if ((job->jv_copyID & mask) != (copyID & mask) 4598 if ((job->jv_copyID & mask) != (copyID & mask)
4569 && !job_still_alive(job)) 4599 && !job_still_useful(job))
4570 { 4600 {
4571 /* Free the job struct itself. */ 4601 /* Free the job struct itself. */
4572 job_free_job(job); 4602 job_free_job(job);
4573 } 4603 }
4574 } 4604 }
4658 4688
4659 for (job = first_job; job != NULL; job = job->jv_next) 4689 for (job = first_job; job != NULL; job = job->jv_next)
4660 /* Only should check if the channel has been closed, if the channel is 4690 /* Only should check if the channel has been closed, if the channel is
4661 * open the job won't exit. */ 4691 * open the job won't exit. */
4662 if (job->jv_status == JOB_STARTED && job->jv_exit_cb != NULL 4692 if (job->jv_status == JOB_STARTED && job->jv_exit_cb != NULL
4663 && (job->jv_channel == NULL 4693 && !job_channel_still_useful(job))
4664 || !channel_still_useful(job->jv_channel)))
4665 return TRUE; 4694 return TRUE;
4666 return FALSE; 4695 return FALSE;
4667 } 4696 }
4668 4697
4669 #define MAX_CHECK_ENDED 8 4698 #define MAX_CHECK_ENDED 8
4674 void 4703 void
4675 job_check_ended(void) 4704 job_check_ended(void)
4676 { 4705 {
4677 int i; 4706 int i;
4678 4707
4708 if (first_job == NULL)
4709 return;
4710
4679 for (i = 0; i < MAX_CHECK_ENDED; ++i) 4711 for (i = 0; i < MAX_CHECK_ENDED; ++i)
4680 { 4712 {
4713 /* NOTE: mch_detect_ended_job() must only return a job of which the
4714 * status was just set to JOB_ENDED. */
4681 job_T *job = mch_detect_ended_job(first_job); 4715 job_T *job = mch_detect_ended_job(first_job);
4682 4716
4683 if (job == NULL) 4717 if (job == NULL)
4684 break; 4718 break;
4685 if (job_still_useful(job)) 4719 job_cleanup(job); /* may free "job" */
4686 job_cleanup(job); /* may free "job" */
4687 } 4720 }
4688 4721
4689 if (channel_need_redraw) 4722 if (channel_need_redraw)
4690 { 4723 {
4691 channel_need_redraw = FALSE; 4724 channel_need_redraw = FALSE;
4895 char * 4928 char *
4896 job_status(job_T *job) 4929 job_status(job_T *job)
4897 { 4930 {
4898 char *result; 4931 char *result;
4899 4932
4900 if (job->jv_status == JOB_ENDED) 4933 if (job->jv_status >= JOB_ENDED)
4901 /* No need to check, dead is dead. */ 4934 /* No need to check, dead is dead. */
4902 result = "dead"; 4935 result = "dead";
4903 else if (job->jv_status == JOB_FAILED) 4936 else if (job->jv_status == JOB_FAILED)
4904 result = "fail"; 4937 result = "fail";
4905 else 4938 else