Mercurial > vim
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 |