Patch 8.1.0845
Problem:    Having job_status() free the job causes problems.
Solution:   Do not actually free the job or terminal yet, put it in a list and
            free it a bit later. Do not use a terminal after checking the job
            status.  (closes #3873)
Files:      src/channel.c, src/terminal.c, src/proto/terminal.pro, src/misc2.c


*** ../vim-8.1.0844/src/channel.c       2019-01-28 20:04:20.324937137 +0100
--- src/channel.c       2019-01-29 22:08:49.222535249 +0100
***************
*** 5161,5168 ****
      }
  }
  
      static void
! job_free_job(job_T *job)
  {
      if (job->jv_next != NULL)
        job->jv_next->jv_prev = job->jv_prev;
--- 5161,5171 ----
      }
  }
  
+ /*
+  * Remove "job" from the list of jobs.
+  */
      static void
! job_unlink(job_T *job)
  {
      if (job->jv_next != NULL)
        job->jv_next->jv_prev = job->jv_prev;
***************
*** 5170,5175 ****
--- 5173,5184 ----
        first_job = job->jv_next;
      else
        job->jv_prev->jv_next = job->jv_next;
+ }
+ 
+     static void
+ job_free_job(job_T *job)
+ {
+     job_unlink(job);
      vim_free(job);
  }
  
***************
*** 5183,5194 ****
--- 5192,5235 ----
      }
  }
  
+ job_T *jobs_to_free = NULL;
+ 
+ /*
+  * Put "job" in a list to be freed later, when it's no longer referenced.
+  */
+     static void
+ job_free_later(job_T *job)
+ {
+     job_unlink(job);
+     job->jv_next = jobs_to_free;
+     jobs_to_free = job;
+ }
+ 
+     static void
+ free_jobs_to_free_later(void)
+ {
+     job_T *job;
+ 
+     while (jobs_to_free != NULL)
+     {
+       job = jobs_to_free;
+       jobs_to_free = job->jv_next;
+       job_free_contents(job);
+       vim_free(job);
+     }
+ }
+ 
  #if defined(EXITFREE) || defined(PROTO)
      void
  job_free_all(void)
  {
      while (first_job != NULL)
        job_free(first_job);
+     free_jobs_to_free_later();
+ 
+ # ifdef FEAT_TERMINAL
+     free_unused_terminals();
+ # endif
  }
  #endif
  
***************
*** 5359,5364 ****
--- 5400,5407 ----
   * NOTE: Must call job_cleanup() only once right after the status of "job"
   * changed to JOB_ENDED (i.e. after job_status() returned "dead" first or
   * mch_detect_ended_job() returned non-NULL).
+  * If the job is no longer used it will be removed from the list of jobs, and
+  * deleted a bit later.
   */
      void
  job_cleanup(job_T *job)
***************
*** 5394,5408 ****
        channel_need_redraw = TRUE;
      }
  
!     /* Do not free the job in case the close callback of the associated 
channel
!      * isn't invoked yet and may get information by job_info(). */
      if (job->jv_refcount == 0 && !job_channel_still_useful(job))
!     {
!       /* The job was already unreferenced and the associated channel was
!        * detached, now that it ended it can be freed. Careful: caller must
!        * not use "job" after this! */
!       job_free(job);
!     }
  }
  
  /*
--- 5437,5449 ----
        channel_need_redraw = TRUE;
      }
  
!     // Do not free the job in case the close callback of the associated 
channel
!     // isn't invoked yet and may get information by job_info().
      if (job->jv_refcount == 0 && !job_channel_still_useful(job))
!       // The job was already unreferenced and the associated channel was
!       // detached, now that it ended it can be freed. However, a caller might
!       // still use it, thus free it a bit later.
!       job_free_later(job);
  }
  
  /*
***************
*** 5609,5617 ****
        if (job == NULL)
            break;
        did_end = TRUE;
!       job_cleanup(job); // may free "job"
      }
  
      if (channel_need_redraw)
      {
        channel_need_redraw = FALSE;
--- 5650,5661 ----
        if (job == NULL)
            break;
        did_end = TRUE;
!       job_cleanup(job); // may add "job" to jobs_to_free
      }
  
+     // Actually free jobs that were cleaned up.
+     free_jobs_to_free_later();
+ 
      if (channel_need_redraw)
      {
        channel_need_redraw = FALSE;
*** ../vim-8.1.0844/src/terminal.c      2019-01-26 15:12:52.558260916 +0100
--- src/terminal.c      2019-01-29 22:27:59.339243929 +0100
***************
*** 803,812 ****
--- 803,819 ----
      ga_clear(&term->tl_scrollback);
  }
  
+ 
+ // Terminals that need to be freed soon.
+ term_T        *terminals_to_free = NULL;
+ 
  /*
   * Free a terminal and everything it refers to.
   * Kills the job if there is one.
   * Called when wiping out a buffer.
+  * The actual terminal structure is freed later in free_unused_terminals(),
+  * because callbacks may wipe out a buffer while the terminal is still
+  * referenced.
   */
      void
  free_terminal(buf_T *buf)
***************
*** 816,821 ****
--- 823,830 ----
  
      if (term == NULL)
        return;
+ 
+     // Unlink the terminal form the list of terminals.
      if (first_term == term)
        first_term = term->tl_next;
      else
***************
*** 834,860 ****
            job_stop(term->tl_job, NULL, "kill");
        job_unref(term->tl_job);
      }
  
!     free_scrollback(term);
  
!     term_free_vterm(term);
!     vim_free(term->tl_title);
  #ifdef FEAT_SESSION
!     vim_free(term->tl_command);
  #endif
!     vim_free(term->tl_kill);
!     vim_free(term->tl_status_text);
!     vim_free(term->tl_opencmd);
!     vim_free(term->tl_eof_chars);
  #ifdef WIN3264
!     if (term->tl_out_fd != NULL)
!       fclose(term->tl_out_fd);
  #endif
!     vim_free(term->tl_cursor_color);
!     vim_free(term);
!     buf->b_term = NULL;
!     if (in_terminal_loop == term)
!       in_terminal_loop = NULL;
  }
  
  /*
--- 843,883 ----
            job_stop(term->tl_job, NULL, "kill");
        job_unref(term->tl_job);
      }
+     term->tl_next = terminals_to_free;
+     terminals_to_free = term;
  
!     buf->b_term = NULL;
!     if (in_terminal_loop == term)
!       in_terminal_loop = NULL;
! }
  
!     void
! free_unused_terminals()
! {
!     while (terminals_to_free != NULL)
!     {
!       term_T      *term = terminals_to_free;
! 
!       terminals_to_free = term->tl_next;
! 
!       free_scrollback(term);
! 
!       term_free_vterm(term);
!       vim_free(term->tl_title);
  #ifdef FEAT_SESSION
!       vim_free(term->tl_command);
  #endif
!       vim_free(term->tl_kill);
!       vim_free(term->tl_status_text);
!       vim_free(term->tl_opencmd);
!       vim_free(term->tl_eof_chars);
  #ifdef WIN3264
!       if (term->tl_out_fd != NULL)
!           fclose(term->tl_out_fd);
  #endif
!       vim_free(term->tl_cursor_color);
!       vim_free(term);
!     }
  }
  
  /*
***************
*** 1275,1280 ****
--- 1298,1304 ----
  /*
   * Return TRUE if the job for "term" is still running.
   * If "check_job_status" is TRUE update the job status.
+  * NOTE: "term" may be freed by callbacks.
   */
      static int
  term_job_running_check(term_T *term, int check_job_status)
***************
*** 1285,1294 ****
        && term->tl_job != NULL
        && channel_is_open(term->tl_job->jv_channel))
      {
        if (check_job_status)
!           job_status(term->tl_job);
!       return (term->tl_job->jv_status == JOB_STARTED
!               || term->tl_job->jv_channel->ch_keep_open);
      }
      return FALSE;
  }
--- 1309,1323 ----
        && term->tl_job != NULL
        && channel_is_open(term->tl_job->jv_channel))
      {
+       job_T *job = term->tl_job;
+ 
+       // Careful: Checking the job status may invoked callbacks, which close
+       // the buffer and terminate "term".  However, "job" will not be freed
+       // yet.
        if (check_job_status)
!           job_status(job);
!       return (job->jv_status == JOB_STARTED
!               || (job->jv_channel != NULL && job->jv_channel->ch_keep_open));
      }
      return FALSE;
  }
***************
*** 2151,2159 ****
  #ifdef FEAT_GUI
        if (!curbuf->b_term->tl_system)
  #endif
!           /* TODO: skip screen update when handling a sequence of keys. */
!           /* Repeat redrawing in case a message is received while redrawing.
!            */
            while (must_redraw != 0)
                if (update_screen(0) == FAIL)
                    break;
--- 2180,2187 ----
  #ifdef FEAT_GUI
        if (!curbuf->b_term->tl_system)
  #endif
!           // TODO: skip screen update when handling a sequence of keys.
!           // Repeat redrawing in case a message is received while redrawing.
            while (must_redraw != 0)
                if (update_screen(0) == FAIL)
                    break;
*** ../vim-8.1.0844/src/proto/terminal.pro      2019-01-20 15:30:36.893328693 
+0100
--- src/proto/terminal.pro      2019-01-29 22:07:47.982845114 +0100
***************
*** 5,10 ****
--- 5,11 ----
  int term_write_session(FILE *fd, win_T *wp);
  int term_should_restore(buf_T *buf);
  void free_terminal(buf_T *buf);
+ void free_unused_terminals(void);
  void write_to_term(buf_T *buffer, char_u *msg, channel_T *channel);
  int term_job_running(term_T *term);
  int term_none_open(term_T *term);
*** ../vim-8.1.0844/src/misc2.c 2019-01-29 20:17:25.550548240 +0100
--- src/misc2.c 2019-01-29 22:07:18.670987912 +0100
***************
*** 6387,6392 ****
--- 6387,6395 ----
        if (job_check_ended())
            continue;
  # endif
+ # ifdef FEAT_TERMINAL
+       free_unused_terminals();
+ # endif
        break;
      }
  
*** ../vim-8.1.0844/src/version.c       2019-01-29 20:36:53.350466201 +0100
--- src/version.c       2019-01-29 21:39:08.114348374 +0100
***************
*** 785,786 ****
--- 785,788 ----
  {   /* Add new patch number below this line */
+ /**/
+     845,
  /**/

-- 
"Hegel was right when he said that we learn from history that man can
never learn anything from history."       (George Bernard Shaw)

 /// Bram Moolenaar -- [email protected] -- 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    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Raspunde prin e-mail lui