To: vim_dev@googlegroups.com Subject: Patch 7.4.1380 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 7.4.1380 Problem: The job exit callback is not implemented. Solution: Add the "exit-cb" option. Files: src/structs.h, src/eval.c, src/channel.c, src/proto/eval.pro, src/misc2.c, src/macros.h, src/testdir/test_channel.vim *** ../vim-7.4.1379/src/structs.h 2016-02-21 16:40:07.084383820 +0100 --- src/structs.h 2016-02-21 18:13:31.002125745 +0100 *************** *** 1265,1270 **** --- 1265,1271 ---- #endif jobstatus_T jv_status; char_u *jv_stoponexit; /* allocated */ + char_u *jv_exit_cb; /* allocated */ int jv_refcount; /* reference count */ channel_T *jv_channel; /* channel for I/O, reference counted */ *************** *** 1390,1395 **** --- 1391,1397 ---- #define JO_PART 0x0800 /* "part" */ #define JO_ID 0x1000 /* "id" */ #define JO_STOPONEXIT 0x2000 /* "stoponexit" */ + #define JO_EXIT_CB 0x4000 /* "exit-cb" */ #define JO_ALL 0xffffff #define JO_MODE_ALL (JO_MODE + JO_IN_MODE + JO_OUT_MODE + JO_ERR_MODE) *************** *** 1418,1423 **** --- 1420,1427 ---- int jo_id; char_u jo_soe_buf[NUMBUFLEN]; char_u *jo_stoponexit; + char_u jo_ecb_buf[NUMBUFLEN]; + char_u *jo_exit_cb; } jobopt_T; *** ../vim-7.4.1379/src/eval.c 2016-02-21 16:40:07.084383820 +0100 --- src/eval.c 2016-02-21 19:07:47.668211225 +0100 *************** *** 7774,7779 **** --- 7774,7780 ---- job->jv_prev->jv_next = job->jv_next; vim_free(job->jv_stoponexit); + vim_free(job->jv_exit_cb); vim_free(job); } *************** *** 7781,7787 **** job_unref(job_T *job) { if (job != NULL && --job->jv_refcount <= 0) ! job_free(job); } /* --- 7782,7794 ---- job_unref(job_T *job) { if (job != NULL && --job->jv_refcount <= 0) ! { ! /* Do not free the job when it has not ended yet and there is a ! * "stoponexit" flag or an exit callback. */ ! if (job->jv_status != JOB_STARTED ! || (job->jv_stoponexit == NULL && job->jv_exit_cb == NULL)) ! job_free(job); ! } } /* *************** *** 7819,7824 **** --- 7826,7839 ---- else job->jv_stoponexit = vim_strsave(opt->jo_stoponexit); } + if (opt->jo_set & JO_EXIT_CB) + { + vim_free(job->jv_exit_cb); + if (opt->jo_exit_cb == NULL || *opt->jo_exit_cb == NUL) + job->jv_exit_cb = NULL; + else + job->jv_exit_cb = vim_strsave(opt->jo_exit_cb); + } } /* *************** *** 7830,7836 **** job_T *job; for (job = first_job; job != NULL; job = job->jv_next) ! if (job->jv_stoponexit != NULL && *job->jv_stoponexit != NUL) mch_stop_job(job, job->jv_stoponexit); } #endif --- 7845,7851 ---- job_T *job; for (job = first_job; job != NULL; job = job->jv_next) ! if (job->jv_status == JOB_STARTED && job->jv_stoponexit != NULL) mch_stop_job(job, job->jv_stoponexit); } #endif *************** *** 10030,10036 **** opt->jo_out_cb = get_callback(item); if (opt->jo_out_cb == NULL) { ! EMSG2(_(e_invarg2), "out-db"); return FAIL; } } --- 10045,10051 ---- opt->jo_out_cb = get_callback(item); if (opt->jo_out_cb == NULL) { ! EMSG2(_(e_invarg2), "out-cb"); return FAIL; } } *************** *** 10108,10113 **** --- 10123,10140 ---- return FAIL; } } + else if (STRCMP(hi->hi_key, "exit-cb") == 0) + { + if (!(supported & JO_EXIT_CB)) + break; + opt->jo_set |= JO_EXIT_CB; + opt->jo_exit_cb = get_tv_string_buf_chk(item, opt->jo_ecb_buf); + if (opt->jo_ecb_buf == NULL) + { + EMSG2(_(e_invarg2), "exit-cb"); + return FAIL; + } + } else break; --todo; *************** *** 14771,14777 **** dict_list(argvars, rettv, 2); } ! #ifdef FEAT_JOB /* * Get the job from the argument. * Returns NULL if the job is invalid. --- 14798,14804 ---- dict_list(argvars, rettv, 2); } ! #if defined(FEAT_JOB) || defined(PROTO) /* * Get the job from the argument. * Returns NULL if the job is invalid. *************** *** 14824,14830 **** if (job == NULL) return; clear_job_options(&opt); ! if (get_job_options(&argvars[1], &opt, JO_STOPONEXIT) == FAIL) return; job_set_options(job, &opt); } --- 14851,14857 ---- if (job == NULL) return; clear_job_options(&opt); ! if (get_job_options(&argvars[1], &opt, JO_STOPONEXIT + JO_EXIT_CB) == FAIL) return; job_set_options(job, &opt); } *************** *** 14858,14864 **** clear_job_options(&opt); opt.jo_mode = MODE_NL; if (get_job_options(&argvars[1], &opt, ! JO_MODE_ALL + JO_CB_ALL + JO_TIMEOUT_ALL + JO_STOPONEXIT) == FAIL) return; job_set_options(job, &opt); --- 14885,14892 ---- clear_job_options(&opt); opt.jo_mode = MODE_NL; if (get_job_options(&argvars[1], &opt, ! JO_MODE_ALL + JO_CB_ALL + JO_TIMEOUT_ALL ! + JO_STOPONEXIT + JO_EXIT_CB) == FAIL) return; job_set_options(job, &opt); *************** *** 14959,14964 **** --- 14987,15063 ---- } /* + * Get the status of "job" and invoke the exit callback when needed. + * The returned string is not allocated. + */ + static char * + job_status(job_T *job) + { + char *result; + + if (job->jv_status == JOB_ENDED) + /* No need to check, dead is dead. */ + result = "dead"; + else if (job->jv_status == JOB_FAILED) + result = "fail"; + else + { + result = mch_job_status(job); + # ifdef FEAT_CHANNEL + if (job->jv_status == JOB_ENDED) + ch_log(job->jv_channel, "Job ended"); + # endif + if (job->jv_status == JOB_ENDED && job->jv_exit_cb != NULL) + { + typval_T argv[3]; + typval_T rettv; + int dummy; + + /* invoke the exit callback */ + argv[0].v_type = VAR_JOB; + argv[0].vval.v_job = job; + argv[1].v_type = VAR_NUMBER; + argv[1].vval.v_number = job->jv_exitval; + call_func(job->jv_exit_cb, (int)STRLEN(job->jv_exit_cb), + &rettv, 2, argv, 0L, 0L, &dummy, TRUE, NULL); + clear_tv(&rettv); + } + if (job->jv_status == JOB_ENDED && job->jv_refcount == 0) + { + /* The job already was unreferenced, now that it ended it can be + * freed. Careful: caller must not use "job" after this! */ + job_free(job); + } + } + return result; + } + + /* + * Called once in a while: check if any jobs with an "exit-cb" have ended. + */ + void + job_check_ended() + { + static time_t last_check = 0; + time_t now; + job_T *job; + job_T *next; + + /* Only do this once in 10 seconds. */ + now = time(NULL); + if (last_check + 10 < now) + { + last_check = now; + for (job = first_job; job != NULL; job = next) + { + next = job->jv_next; + if (job->jv_status == JOB_STARTED && job->jv_exit_cb != NULL) + job_status(job); /* may free "job" */ + } + } + } + + /* * "job_status()" function */ static void *************** *** 14969,14981 **** if (job != NULL) { ! if (job->jv_status == JOB_ENDED) ! /* No need to check, dead is dead. */ ! result = "dead"; ! else if (job->jv_status == JOB_FAILED) ! result = "fail"; ! else ! result = mch_job_status(job); rettv->v_type = VAR_STRING; rettv->vval.v_string = vim_strsave((char_u *)result); } --- 15068,15074 ---- if (job != NULL) { ! result = job_status(job); rettv->v_type = VAR_STRING; rettv->vval.v_string = vim_strsave((char_u *)result); } *************** *** 22857,22863 **** case VAR_JOB: #ifdef FEAT_JOB to->vval.v_job = from->vval.v_job; ! ++to->vval.v_job->jv_refcount; break; #endif case VAR_CHANNEL: --- 22950,22957 ---- case VAR_JOB: #ifdef FEAT_JOB to->vval.v_job = from->vval.v_job; ! if (to->vval.v_job != NULL) ! ++to->vval.v_job->jv_refcount; break; #endif case VAR_CHANNEL: *** ../vim-7.4.1379/src/channel.c 2016-02-21 17:20:51.175001604 +0100 --- src/channel.c 2016-02-21 18:08:31.777248426 +0100 *************** *** 833,838 **** --- 833,840 ---- call_func(callback, (int)STRLEN(callback), &rettv, 2, argv, 0L, 0L, &dummy, TRUE, NULL); + clear_tv(&rettv); + /* If an echo command was used the cursor needs to be put back where * it belongs. */ setcursor(); *** ../vim-7.4.1379/src/proto/eval.pro 2016-02-21 16:40:07.088383778 +0100 --- src/proto/eval.pro 2016-02-21 18:40:11.457437953 +0100 *************** *** 87,92 **** --- 87,93 ---- int call_func(char_u *funcname, int len, typval_T *rettv, int argcount, typval_T *argvars, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, dict_T *selfdict); int func_call(char_u *name, typval_T *args, dict_T *selfdict, typval_T *rettv); void dict_extend(dict_T *d1, dict_T *d2, char_u *action); + void job_check_ended(void); void mzscheme_call_vim(char_u *name, typval_T *args, typval_T *rettv); float_T vim_round(float_T f); long do_searchpair(char_u *spat, char_u *mpat, char_u *epat, int dir, char_u *skip, int flags, pos_T *match_pos, linenr_T lnum_stop, long time_limit); *** ../vim-7.4.1379/src/misc2.c 2016-02-21 17:20:51.179001562 +0100 --- src/misc2.c 2016-02-21 18:39:22.401949173 +0100 *************** *** 6256,6260 **** --- 6256,6264 ---- /* Process the queued clientserver messages. */ server_parse_messages(); # endif + # ifdef FEAT_JOB + /* Check if any jobs have ended. */ + job_check_ended(); + # endif } #endif *** ../vim-7.4.1379/src/macros.h 2016-01-24 20:36:18.862082391 +0100 --- src/macros.h 2016-02-21 18:40:04.989505356 +0100 *************** *** 317,322 **** # define PLINES_NOFILL(x) plines(x) #endif ! #if defined(FEAT_CHANNEL) || defined(FEAT_CLIENTSERVER) # define MESSAGE_QUEUE #endif --- 317,322 ---- # define PLINES_NOFILL(x) plines(x) #endif ! #if defined(FEAT_CHANNEL) || defined(FEAT_JOB) || defined(FEAT_CLIENTSERVER) # define MESSAGE_QUEUE #endif *** ../vim-7.4.1379/src/testdir/test_channel.vim 2016-02-21 16:40:07.088383778 +0100 --- src/testdir/test_channel.vim 2016-02-21 19:02:15.463664885 +0100 *************** *** 468,470 **** --- 468,495 ---- call ch_log('Test_call()') call s:run_server('s:test_call') endfunc + + """"""""" + + let s:job_ret = 'not yet' + function MyExitCb(job, status) + let s:job_ret = 'done' + endfunc + + function s:test_exit_callback(port) + call job_setoptions(s:job, {'exit-cb': 'MyExitCb'}) + let s:exit_job = s:job + endfunc + + func Test_exit_callback() + if has('job') + call s:run_server('s:test_exit_callback') + + " the job may take a little while to exit + sleep 50m + + " calling job_status() triggers the callback + call job_status(s:exit_job) + call assert_equal('done', s:job_ret) + endif + endfunc *** ../vim-7.4.1379/src/version.c 2016-02-21 17:20:51.179001562 +0100 --- src/version.c 2016-02-21 18:10:41.599893524 +0100 *************** *** 749,750 **** --- 749,752 ---- { /* Add new patch number below this line */ + /**/ + 1380, /**/ -- WOMAN: King of the who? ARTHUR: The Britons. WOMAN: Who are the Britons? ARTHUR: Well, we all are. we're all Britons and I am your king. The Quest for the Holy Grail (Monty Python) /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///