@@ -4433,19 +4433,66 @@ job_free(job_T *job)
44334433 }
44344434}
44354435
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+ */
44364480 static void
44374481job_cleanup (job_T * job )
44384482{
44394483 if (job -> jv_status != JOB_ENDED )
44404484 return ;
44414485
4486+ /* Ready to cleanup the job. */
4487+ job -> jv_status = JOB_FINISHED ;
4488+
44424489 if (job -> jv_exit_cb != NULL )
44434490 {
44444491 typval_T argv [3 ];
44454492 typval_T rettv ;
44464493 int dummy ;
44474494
4448- /* invoke the exit callback; make sure the refcount is > 0 */
4495+ /* Invoke the exit callback. Make sure the refcount is > 0. */
44494496 ++ job -> jv_refcount ;
44504497 argv [0 ].v_type = VAR_JOB ;
44514498 argv [0 ].vval .v_job = job ;
@@ -4458,42 +4505,18 @@ job_cleanup(job_T *job)
44584505 -- job -> jv_refcount ;
44594506 channel_need_redraw = TRUE;
44604507 }
4461- if (job -> jv_refcount == 0 )
4508+
4509+ /* Do not free the job in case the close callback of the associated channel
4510+ * isn't invoked yet and may get information by job_info(). */
4511+ if (job -> jv_refcount == 0 && !job_channel_still_useful (job ))
44624512 {
4463- /* The job was already unreferenced, now that it ended it can be
4464- * freed. Careful: caller must not use "job" after this! */
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! */
44654516 job_free (job );
44664517 }
44674518}
44684519
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- }
4496-
44974520/*
44984521 * Mark references in jobs that are still useful.
44994522 */
@@ -4505,7 +4528,7 @@ set_ref_in_job(int copyID)
45054528 typval_T tv ;
45064529
45074530 for (job = first_job ; job != NULL ; job = job -> jv_next )
4508- if (job_still_alive (job ))
4531+ if (job_still_useful (job ))
45094532 {
45104533 tv .v_type = VAR_JOB ;
45114534 tv .vval .v_job = job ;
@@ -4514,26 +4537,33 @@ set_ref_in_job(int copyID)
45144537 return abort ;
45154538}
45164539
4540+ /*
4541+ * Dereference "job". Note that after this "job" may have been freed.
4542+ */
45174543 void
45184544job_unref (job_T * job )
45194545{
45204546 if (job != NULL && -- job -> jv_refcount <= 0 )
45214547 {
4522- /* Do not free the job when it has not ended yet and there is a
4523- * "stoponexit" flag or an exit callback . */
4524- if (!job_still_alive (job ))
4548+ /* Do not free the job if there is a channel where the close callback
4549+ * may get the job info . */
4550+ if (!job_channel_still_useful (job ))
45254551 {
4526- job_free (job );
4527- }
4528- else if (job -> jv_channel != NULL
4529- && !channel_still_useful (job -> jv_channel ))
4530- {
4531- /* Do remove the link to the channel, otherwise it hangs
4532- * around until Vim exits. See job_free() for refcount. */
4533- ch_log (job -> jv_channel , "detaching channel from job" );
4534- job -> jv_channel -> ch_job = NULL ;
4535- channel_unref (job -> jv_channel );
4536- job -> jv_channel = NULL ;
4552+ /* Do not free the job when it has not ended yet and there is a
4553+ * "stoponexit" flag or an exit callback. */
4554+ if (!job_need_end_check (job ))
4555+ {
4556+ job_free (job );
4557+ }
4558+ else if (job -> jv_channel != NULL )
4559+ {
4560+ /* Do remove the link to the channel, otherwise it hangs
4561+ * around until Vim exits. See job_free() for refcount. */
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+ }
45374567 }
45384568 }
45394569}
@@ -4546,7 +4576,7 @@ free_unused_jobs_contents(int copyID, int mask)
45464576
45474577 for (job = first_job ; job != NULL ; job = job -> jv_next )
45484578 if ((job -> jv_copyID & mask ) != (copyID & mask )
4549- && !job_still_alive (job ))
4579+ && !job_still_useful (job ))
45504580 {
45514581 /* Free the channel and ordinary items it contains, but don't
45524582 * recurse into Lists, Dictionaries etc. */
@@ -4566,7 +4596,7 @@ free_unused_jobs(int copyID, int mask)
45664596 {
45674597 job_next = job -> jv_next ;
45684598 if ((job -> jv_copyID & mask ) != (copyID & mask )
4569- && !job_still_alive (job ))
4599+ && !job_still_useful (job ))
45704600 {
45714601 /* Free the job struct itself. */
45724602 job_free_job (job );
@@ -4660,8 +4690,7 @@ has_pending_job(void)
46604690 /* Only should check if the channel has been closed, if the channel is
46614691 * open the job won't exit. */
46624692 if (job -> jv_status == JOB_STARTED && job -> jv_exit_cb != NULL
4663- && (job -> jv_channel == NULL
4664- || !channel_still_useful (job -> jv_channel )))
4693+ && !job_channel_still_useful (job ))
46654694 return TRUE;
46664695 return FALSE;
46674696}
@@ -4676,14 +4705,18 @@ job_check_ended(void)
46764705{
46774706 int i ;
46784707
4708+ if (first_job == NULL )
4709+ return ;
4710+
46794711 for (i = 0 ; i < MAX_CHECK_ENDED ; ++ i )
46804712 {
4713+ /* NOTE: mch_detect_ended_job() must only return a job of which the
4714+ * status was just set to JOB_ENDED. */
46814715 job_T * job = mch_detect_ended_job (first_job );
46824716
46834717 if (job == NULL )
46844718 break ;
4685- if (job_still_useful (job ))
4686- job_cleanup (job ); /* may free "job" */
4719+ job_cleanup (job ); /* may free "job" */
46874720 }
46884721
46894722 if (channel_need_redraw )
@@ -4897,7 +4930,7 @@ job_status(job_T *job)
48974930{
48984931 char * result ;
48994932
4900- if (job -> jv_status = = JOB_ENDED )
4933+ if (job -> jv_status > = JOB_ENDED )
49014934 /* No need to check, dead is dead. */
49024935 result = "dead" ;
49034936 else if (job -> jv_status == JOB_FAILED )
0 commit comments