comparison src/channel.c @ 10279:c5c15c818bda v8.0.0036

commit https://github.com/vim/vim/commit/97792de2762cc79cc365a8a0b858f27753179577 Author: Bram Moolenaar <Bram@vim.org> Date: Sat Oct 15 18:36:49 2016 +0200 patch 8.0.0036 Problem: Detecting that a job has finished may take a while. Solution: Check for a finished job more often (Ozaki Kiichi)
author Christian Brabandt <cb@256bit.org>
date Sat, 15 Oct 2016 18:45:04 +0200
parents a09db7a4afe0
children 88331ee68367
comparison
equal deleted inserted replaced
10278:abb9c83e2b7c 10279:c5c15c818bda
4426 job_free_contents(job); 4426 job_free_contents(job);
4427 job_free_job(job); 4427 job_free_job(job);
4428 } 4428 }
4429 } 4429 }
4430 4430
4431 static void
4432 job_cleanup(job_T *job)
4433 {
4434 if (job->jv_status != JOB_ENDED)
4435 return;
4436
4437 if (job->jv_exit_cb != NULL)
4438 {
4439 typval_T argv[3];
4440 typval_T rettv;
4441 int dummy;
4442
4443 /* invoke the exit callback; make sure the refcount is > 0 */
4444 ++job->jv_refcount;
4445 argv[0].v_type = VAR_JOB;
4446 argv[0].vval.v_job = job;
4447 argv[1].v_type = VAR_NUMBER;
4448 argv[1].vval.v_number = job->jv_exitval;
4449 call_func(job->jv_exit_cb, (int)STRLEN(job->jv_exit_cb),
4450 &rettv, 2, argv, NULL, 0L, 0L, &dummy, TRUE,
4451 job->jv_exit_partial, NULL);
4452 clear_tv(&rettv);
4453 --job->jv_refcount;
4454 channel_need_redraw = TRUE;
4455 }
4456 if (job->jv_refcount == 0)
4457 {
4458 /* The job was already unreferenced, now that it ended it can be
4459 * freed. Careful: caller must not use "job" after this! */
4460 job_free(job);
4461 }
4462 }
4463
4431 #if defined(EXITFREE) || defined(PROTO) 4464 #if defined(EXITFREE) || defined(PROTO)
4432 void 4465 void
4433 job_free_all(void) 4466 job_free_all(void)
4434 { 4467 {
4435 while (first_job != NULL) 4468 while (first_job != NULL)
4443 * or when the associated channel will do something with the job output. 4476 * or when the associated channel will do something with the job output.
4444 */ 4477 */
4445 static int 4478 static int
4446 job_still_useful(job_T *job) 4479 job_still_useful(job_T *job)
4447 { 4480 {
4448 return job->jv_status == JOB_STARTED 4481 return (job->jv_stoponexit != NULL || job->jv_exit_cb != NULL
4449 && (job->jv_stoponexit != NULL || job->jv_exit_cb != NULL 4482 || (job->jv_channel != NULL
4450 || (job->jv_channel != NULL 4483 && channel_still_useful(job->jv_channel)));
4451 && channel_still_useful(job->jv_channel))); 4484 }
4485
4486 static int
4487 job_still_alive(job_T *job)
4488 {
4489 return (job->jv_status == JOB_STARTED) && job_still_useful(job);
4452 } 4490 }
4453 4491
4454 /* 4492 /*
4455 * Mark references in jobs that are still useful. 4493 * Mark references in jobs that are still useful.
4456 */ 4494 */
4460 int abort = FALSE; 4498 int abort = FALSE;
4461 job_T *job; 4499 job_T *job;
4462 typval_T tv; 4500 typval_T tv;
4463 4501
4464 for (job = first_job; job != NULL; job = job->jv_next) 4502 for (job = first_job; job != NULL; job = job->jv_next)
4465 if (job_still_useful(job)) 4503 if (job_still_alive(job))
4466 { 4504 {
4467 tv.v_type = VAR_JOB; 4505 tv.v_type = VAR_JOB;
4468 tv.vval.v_job = job; 4506 tv.vval.v_job = job;
4469 abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL); 4507 abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL);
4470 } 4508 }
4476 { 4514 {
4477 if (job != NULL && --job->jv_refcount <= 0) 4515 if (job != NULL && --job->jv_refcount <= 0)
4478 { 4516 {
4479 /* Do not free the job when it has not ended yet and there is a 4517 /* Do not free the job when it has not ended yet and there is a
4480 * "stoponexit" flag or an exit callback. */ 4518 * "stoponexit" flag or an exit callback. */
4481 if (!job_still_useful(job)) 4519 if (!job_still_alive(job))
4482 { 4520 {
4483 job_free(job); 4521 job_free(job);
4484 } 4522 }
4485 else if (job->jv_channel != NULL 4523 else if (job->jv_channel != NULL
4486 && !channel_still_useful(job->jv_channel)) 4524 && !channel_still_useful(job->jv_channel))
4501 int did_free = FALSE; 4539 int did_free = FALSE;
4502 job_T *job; 4540 job_T *job;
4503 4541
4504 for (job = first_job; job != NULL; job = job->jv_next) 4542 for (job = first_job; job != NULL; job = job->jv_next)
4505 if ((job->jv_copyID & mask) != (copyID & mask) 4543 if ((job->jv_copyID & mask) != (copyID & mask)
4506 && !job_still_useful(job)) 4544 && !job_still_alive(job))
4507 { 4545 {
4508 /* Free the channel and ordinary items it contains, but don't 4546 /* Free the channel and ordinary items it contains, but don't
4509 * recurse into Lists, Dictionaries etc. */ 4547 * recurse into Lists, Dictionaries etc. */
4510 job_free_contents(job); 4548 job_free_contents(job);
4511 did_free = TRUE; 4549 did_free = TRUE;
4521 4559
4522 for (job = first_job; job != NULL; job = job_next) 4560 for (job = first_job; job != NULL; job = job_next)
4523 { 4561 {
4524 job_next = job->jv_next; 4562 job_next = job->jv_next;
4525 if ((job->jv_copyID & mask) != (copyID & mask) 4563 if ((job->jv_copyID & mask) != (copyID & mask)
4526 && !job_still_useful(job)) 4564 && !job_still_alive(job))
4527 { 4565 {
4528 /* Free the job struct itself. */ 4566 /* Free the job struct itself. */
4529 job_free_job(job); 4567 job_free_job(job);
4530 } 4568 }
4531 } 4569 }
4612 has_pending_job(void) 4650 has_pending_job(void)
4613 { 4651 {
4614 job_T *job; 4652 job_T *job;
4615 4653
4616 for (job = first_job; job != NULL; job = job->jv_next) 4654 for (job = first_job; job != NULL; job = job->jv_next)
4617 if (job->jv_status == JOB_STARTED && job_still_useful(job)) 4655 if (job_still_alive(job))
4618 return TRUE; 4656 return TRUE;
4619 return FALSE; 4657 return FALSE;
4620 } 4658 }
4621 4659
4660 #define MAX_CHECK_ENDED 8
4661
4622 /* 4662 /*
4623 * Called once in a while: check if any jobs that seem useful have ended. 4663 * Called once in a while: check if any jobs that seem useful have ended.
4624 */ 4664 */
4625 void 4665 void
4626 job_check_ended(void) 4666 job_check_ended(void)
4627 { 4667 {
4628 static time_t last_check = 0; 4668 int i;
4629 time_t now; 4669
4630 job_T *job; 4670 for (i = 0; i < MAX_CHECK_ENDED; ++i)
4631 job_T *next; 4671 {
4632 4672 job_T *job = mch_detect_ended_job(first_job);
4633 /* Only do this once in 10 seconds. */ 4673
4634 now = time(NULL); 4674 if (job == NULL)
4635 if (last_check + 10 < now) 4675 break;
4636 { 4676 if (job_still_useful(job))
4637 last_check = now; 4677 job_cleanup(job); /* may free "job" */
4638 for (job = first_job; job != NULL; job = next) 4678 }
4639 { 4679
4640 next = job->jv_next;
4641 if (job->jv_status == JOB_STARTED && job_still_useful(job))
4642 job_status(job); /* may free "job" */
4643 }
4644 }
4645 if (channel_need_redraw) 4680 if (channel_need_redraw)
4646 { 4681 {
4647 channel_need_redraw = FALSE; 4682 channel_need_redraw = FALSE;
4648 redraw_after_callback(); 4683 redraw_after_callback();
4649 } 4684 }
4860 result = "fail"; 4895 result = "fail";
4861 else 4896 else
4862 { 4897 {
4863 result = mch_job_status(job); 4898 result = mch_job_status(job);
4864 if (job->jv_status == JOB_ENDED) 4899 if (job->jv_status == JOB_ENDED)
4865 ch_log(job->jv_channel, "Job ended"); 4900 job_cleanup(job);
4866 if (job->jv_status == JOB_ENDED && job->jv_exit_cb != NULL)
4867 {
4868 typval_T argv[3];
4869 typval_T rettv;
4870 int dummy;
4871
4872 /* invoke the exit callback; make sure the refcount is > 0 */
4873 ++job->jv_refcount;
4874 argv[0].v_type = VAR_JOB;
4875 argv[0].vval.v_job = job;
4876 argv[1].v_type = VAR_NUMBER;
4877 argv[1].vval.v_number = job->jv_exitval;
4878 call_func(job->jv_exit_cb, (int)STRLEN(job->jv_exit_cb),
4879 &rettv, 2, argv, NULL, 0L, 0L, &dummy, TRUE,
4880 job->jv_exit_partial, NULL);
4881 clear_tv(&rettv);
4882 --job->jv_refcount;
4883 channel_need_redraw = TRUE;
4884 }
4885 if (job->jv_status == JOB_ENDED && job->jv_refcount == 0)
4886 {
4887 /* The job was already unreferenced, now that it ended it can be
4888 * freed. Careful: caller must not use "job" after this! */
4889 job_free(job);
4890 }
4891 } 4901 }
4892 return result; 4902 return result;
4893 } 4903 }
4894 4904
4895 /* 4905 /*