@@ -803,10 +803,17 @@ free_scrollback(term_T *term)
803803 ga_clear (& term -> tl_scrollback );
804804}
805805
806+
807+ // Terminals that need to be freed soon.
808+ term_T * terminals_to_free = NULL ;
809+
806810/*
807811 * Free a terminal and everything it refers to.
808812 * Kills the job if there is one.
809813 * Called when wiping out a buffer.
814+ * The actual terminal structure is freed later in free_unused_terminals(),
815+ * because callbacks may wipe out a buffer while the terminal is still
816+ * referenced.
810817 */
811818 void
812819free_terminal (buf_T * buf )
@@ -816,6 +823,8 @@ free_terminal(buf_T *buf)
816823
817824 if (term == NULL )
818825 return ;
826+
827+ // Unlink the terminal form the list of terminals.
819828 if (first_term == term )
820829 first_term = term -> tl_next ;
821830 else
@@ -834,27 +843,41 @@ free_terminal(buf_T *buf)
834843 job_stop (term -> tl_job , NULL , "kill" );
835844 job_unref (term -> tl_job );
836845 }
846+ term -> tl_next = terminals_to_free ;
847+ terminals_to_free = term ;
848+
849+ buf -> b_term = NULL ;
850+ if (in_terminal_loop == term )
851+ in_terminal_loop = NULL ;
852+ }
837853
838- free_scrollback (term );
854+ void
855+ free_unused_terminals ()
856+ {
857+ while (terminals_to_free != NULL )
858+ {
859+ term_T * term = terminals_to_free ;
839860
840- term_free_vterm (term );
841- vim_free (term -> tl_title );
861+ terminals_to_free = term -> tl_next ;
862+
863+ free_scrollback (term );
864+
865+ term_free_vterm (term );
866+ vim_free (term -> tl_title );
842867#ifdef FEAT_SESSION
843- vim_free (term -> tl_command );
868+ vim_free (term -> tl_command );
844869#endif
845- vim_free (term -> tl_kill );
846- vim_free (term -> tl_status_text );
847- vim_free (term -> tl_opencmd );
848- vim_free (term -> tl_eof_chars );
870+ vim_free (term -> tl_kill );
871+ vim_free (term -> tl_status_text );
872+ vim_free (term -> tl_opencmd );
873+ vim_free (term -> tl_eof_chars );
849874#ifdef WIN3264
850- if (term -> tl_out_fd != NULL )
851- fclose (term -> tl_out_fd );
875+ if (term -> tl_out_fd != NULL )
876+ fclose (term -> tl_out_fd );
852877#endif
853- vim_free (term -> tl_cursor_color );
854- vim_free (term );
855- buf -> b_term = NULL ;
856- if (in_terminal_loop == term )
857- in_terminal_loop = NULL ;
878+ vim_free (term -> tl_cursor_color );
879+ vim_free (term );
880+ }
858881}
859882
860883/*
@@ -1275,6 +1298,7 @@ term_convert_key(term_T *term, int c, char *buf)
12751298/*
12761299 * Return TRUE if the job for "term" is still running.
12771300 * If "check_job_status" is TRUE update the job status.
1301+ * NOTE: "term" may be freed by callbacks.
12781302 */
12791303 static int
12801304term_job_running_check (term_T * term , int check_job_status )
@@ -1285,10 +1309,15 @@ term_job_running_check(term_T *term, int check_job_status)
12851309 && term -> tl_job != NULL
12861310 && channel_is_open (term -> tl_job -> jv_channel ))
12871311 {
1312+ job_T * job = term -> tl_job ;
1313+
1314+ // Careful: Checking the job status may invoked callbacks, which close
1315+ // the buffer and terminate "term". However, "job" will not be freed
1316+ // yet.
12881317 if (check_job_status )
1289- job_status (term -> tl_job );
1290- return (term -> tl_job -> jv_status == JOB_STARTED
1291- || term -> tl_job -> jv_channel -> ch_keep_open );
1318+ job_status (job );
1319+ return (job -> jv_status == JOB_STARTED
1320+ || ( job -> jv_channel != NULL && job -> jv_channel -> ch_keep_open ) );
12921321 }
12931322 return FALSE;
12941323}
@@ -2151,9 +2180,8 @@ terminal_loop(int blocking)
21512180#ifdef FEAT_GUI
21522181 if (!curbuf -> b_term -> tl_system )
21532182#endif
2154- /* TODO: skip screen update when handling a sequence of keys. */
2155- /* Repeat redrawing in case a message is received while redrawing.
2156- */
2183+ // TODO: skip screen update when handling a sequence of keys.
2184+ // Repeat redrawing in case a message is received while redrawing.
21572185 while (must_redraw != 0 )
21582186 if (update_screen (0 ) == FAIL )
21592187 break ;
0 commit comments