@@ -7774,14 +7774,21 @@ job_free(job_T *job)
77747774 job->jv_prev->jv_next = job->jv_next;
77757775
77767776 vim_free(job->jv_stoponexit);
7777+ vim_free(job->jv_exit_cb);
77777778 vim_free(job);
77787779}
77797780
77807781 static void
77817782job_unref(job_T *job)
77827783{
77837784 if (job != NULL && --job->jv_refcount <= 0)
7784- job_free(job);
7785+ {
7786+ /* Do not free the job when it has not ended yet and there is a
7787+ * "stoponexit" flag or an exit callback. */
7788+ if (job->jv_status != JOB_STARTED
7789+ || (job->jv_stoponexit == NULL && job->jv_exit_cb == NULL))
7790+ job_free(job);
7791+ }
77857792}
77867793
77877794/*
@@ -7819,6 +7826,14 @@ job_set_options(job_T *job, jobopt_T *opt)
78197826 else
78207827 job->jv_stoponexit = vim_strsave(opt->jo_stoponexit);
78217828 }
7829+ if (opt->jo_set & JO_EXIT_CB)
7830+ {
7831+ vim_free(job->jv_exit_cb);
7832+ if (opt->jo_exit_cb == NULL || *opt->jo_exit_cb == NUL)
7833+ job->jv_exit_cb = NULL;
7834+ else
7835+ job->jv_exit_cb = vim_strsave(opt->jo_exit_cb);
7836+ }
78227837}
78237838
78247839/*
@@ -7830,7 +7845,7 @@ job_stop_on_exit()
78307845 job_T *job;
78317846
78327847 for (job = first_job; job != NULL; job = job->jv_next)
7833- if (job->jv_stoponexit != NULL && * job->jv_stoponexit != NUL )
7848+ if (job->jv_status == JOB_STARTED && job->jv_stoponexit != NULL )
78347849 mch_stop_job(job, job->jv_stoponexit);
78357850}
78367851#endif
@@ -10030,7 +10045,7 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported)
1003010045 opt->jo_out_cb = get_callback(item);
1003110046 if (opt->jo_out_cb == NULL)
1003210047 {
10033- EMSG2(_(e_invarg2), "out-db ");
10048+ EMSG2(_(e_invarg2), "out-cb ");
1003410049 return FAIL;
1003510050 }
1003610051 }
@@ -10108,6 +10123,18 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported)
1010810123 return FAIL;
1010910124 }
1011010125 }
10126+ else if (STRCMP(hi->hi_key, "exit-cb") == 0)
10127+ {
10128+ if (!(supported & JO_EXIT_CB))
10129+ break;
10130+ opt->jo_set |= JO_EXIT_CB;
10131+ opt->jo_exit_cb = get_tv_string_buf_chk(item, opt->jo_ecb_buf);
10132+ if (opt->jo_ecb_buf == NULL)
10133+ {
10134+ EMSG2(_(e_invarg2), "exit-cb");
10135+ return FAIL;
10136+ }
10137+ }
1011110138 else
1011210139 break;
1011310140 --todo;
@@ -14771,7 +14798,7 @@ f_items(typval_T *argvars, typval_T *rettv)
1477114798 dict_list(argvars, rettv, 2);
1477214799}
1477314800
14774- #ifdef FEAT_JOB
14801+ #if defined( FEAT_JOB) || defined(PROTO)
1477514802/*
1477614803 * Get the job from the argument.
1477714804 * Returns NULL if the job is invalid.
@@ -14824,7 +14851,7 @@ f_job_setoptions(typval_T *argvars, typval_T *rettv UNUSED)
1482414851 if (job == NULL)
1482514852 return;
1482614853 clear_job_options(&opt);
14827- if (get_job_options(&argvars[1], &opt, JO_STOPONEXIT) == FAIL)
14854+ if (get_job_options(&argvars[1], &opt, JO_STOPONEXIT + JO_EXIT_CB ) == FAIL)
1482814855 return;
1482914856 job_set_options(job, &opt);
1483014857}
@@ -14858,7 +14885,8 @@ f_job_start(typval_T *argvars UNUSED, typval_T *rettv)
1485814885 clear_job_options(&opt);
1485914886 opt.jo_mode = MODE_NL;
1486014887 if (get_job_options(&argvars[1], &opt,
14861- JO_MODE_ALL + JO_CB_ALL + JO_TIMEOUT_ALL + JO_STOPONEXIT) == FAIL)
14888+ JO_MODE_ALL + JO_CB_ALL + JO_TIMEOUT_ALL
14889+ + JO_STOPONEXIT + JO_EXIT_CB) == FAIL)
1486214890 return;
1486314891 job_set_options(job, &opt);
1486414892
@@ -14958,6 +14986,77 @@ f_job_start(typval_T *argvars UNUSED, typval_T *rettv)
1495814986#endif
1495914987}
1496014988
14989+ /*
14990+ * Get the status of "job" and invoke the exit callback when needed.
14991+ * The returned string is not allocated.
14992+ */
14993+ static char *
14994+ job_status(job_T *job)
14995+ {
14996+ char *result;
14997+
14998+ if (job->jv_status == JOB_ENDED)
14999+ /* No need to check, dead is dead. */
15000+ result = "dead";
15001+ else if (job->jv_status == JOB_FAILED)
15002+ result = "fail";
15003+ else
15004+ {
15005+ result = mch_job_status(job);
15006+ # ifdef FEAT_CHANNEL
15007+ if (job->jv_status == JOB_ENDED)
15008+ ch_log(job->jv_channel, "Job ended");
15009+ # endif
15010+ if (job->jv_status == JOB_ENDED && job->jv_exit_cb != NULL)
15011+ {
15012+ typval_T argv[3];
15013+ typval_T rettv;
15014+ int dummy;
15015+
15016+ /* invoke the exit callback */
15017+ argv[0].v_type = VAR_JOB;
15018+ argv[0].vval.v_job = job;
15019+ argv[1].v_type = VAR_NUMBER;
15020+ argv[1].vval.v_number = job->jv_exitval;
15021+ call_func(job->jv_exit_cb, (int)STRLEN(job->jv_exit_cb),
15022+ &rettv, 2, argv, 0L, 0L, &dummy, TRUE, NULL);
15023+ clear_tv(&rettv);
15024+ }
15025+ if (job->jv_status == JOB_ENDED && job->jv_refcount == 0)
15026+ {
15027+ /* The job already was unreferenced, now that it ended it can be
15028+ * freed. Careful: caller must not use "job" after this! */
15029+ job_free(job);
15030+ }
15031+ }
15032+ return result;
15033+ }
15034+
15035+ /*
15036+ * Called once in a while: check if any jobs with an "exit-cb" have ended.
15037+ */
15038+ void
15039+ job_check_ended()
15040+ {
15041+ static time_t last_check = 0;
15042+ time_t now;
15043+ job_T *job;
15044+ job_T *next;
15045+
15046+ /* Only do this once in 10 seconds. */
15047+ now = time(NULL);
15048+ if (last_check + 10 < now)
15049+ {
15050+ last_check = now;
15051+ for (job = first_job; job != NULL; job = next)
15052+ {
15053+ next = job->jv_next;
15054+ if (job->jv_status == JOB_STARTED && job->jv_exit_cb != NULL)
15055+ job_status(job); /* may free "job" */
15056+ }
15057+ }
15058+ }
15059+
1496115060/*
1496215061 * "job_status()" function
1496315062 */
@@ -14969,13 +15068,7 @@ f_job_status(typval_T *argvars, typval_T *rettv)
1496915068
1497015069 if (job != NULL)
1497115070 {
14972- if (job->jv_status == JOB_ENDED)
14973- /* No need to check, dead is dead. */
14974- result = "dead";
14975- else if (job->jv_status == JOB_FAILED)
14976- result = "fail";
14977- else
14978- result = mch_job_status(job);
15071+ result = job_status(job);
1497915072 rettv->v_type = VAR_STRING;
1498015073 rettv->vval.v_string = vim_strsave((char_u *)result);
1498115074 }
@@ -22857,7 +22950,8 @@ copy_tv(typval_T *from, typval_T *to)
2285722950 case VAR_JOB:
2285822951#ifdef FEAT_JOB
2285922952 to->vval.v_job = from->vval.v_job;
22860- ++to->vval.v_job->jv_refcount;
22953+ if (to->vval.v_job != NULL)
22954+ ++to->vval.v_job->jv_refcount;
2286122955 break;
2286222956#endif
2286322957 case VAR_CHANNEL:
0 commit comments