# HG changeset patch # User Christian Brabandt # Date 1535809505 -7200 # Node ID 9ffd7d0650c6910769917e4b9a94dd40d8938c7e # Parent 1f979ad27ab5cea2cef1dc7eb003626bd3112a7c patch 8.1.0342: crash when a callback deletes a window that is being used commit https://github.com/vim/vim/commit/94f01956a583223dafe24135489d0ab1100ab0ad Author: Bram Moolenaar Date: Sat Sep 1 15:30:03 2018 +0200 patch 8.1.0342: crash when a callback deletes a window that is being used Problem: Crash when a callback deletes a window that is being used. Solution: Do not unload a buffer that is being displayed while redrawing the screen. Also avoid invoking callbacks while redrawing. (closes #2107) diff --git a/src/buffer.c b/src/buffer.c --- a/src/buffer.c +++ b/src/buffer.c @@ -412,7 +412,28 @@ buf_hashtab_remove(buf_T *buf) hash_remove(&buf_hashtab, hi); } -static char *e_buflocked = N_("E937: Attempt to delete a buffer that is in use"); +/* + * Return TRUE when buffer "buf" can be unloaded. + * Give an error message and return FALSE when the buffer is locked or the + * screen is being redrawn and the buffer is in a window. + */ + static int +can_unload_buffer(buf_T *buf) +{ + int can_unload = !buf->b_locked; + + if (can_unload && updating_screen) + { + win_T *wp; + + FOR_ALL_WINDOWS(wp) + if (wp->w_buffer == buf) + can_unload = FALSE; + } + if (!can_unload) + EMSG(_("E937: Attempt to delete a buffer that is in use")); + return can_unload; +} /* * Close the link to a buffer. @@ -474,11 +495,9 @@ close_buffer( { if (wipe_buf || unload_buf) { - if (buf->b_locked) - { - EMSG(_(e_buflocked)); + if (!can_unload_buffer(buf)) return; - } + /* Wiping out or unloading a terminal buffer kills the job. */ free_terminal(buf); } @@ -501,11 +520,8 @@ close_buffer( /* Disallow deleting the buffer when it is locked (already being closed or * halfway a command that relies on it). Unloading is allowed. */ - if (buf->b_locked > 0 && (del_buf || wipe_buf)) - { - EMSG(_(e_buflocked)); + if ((del_buf || wipe_buf) && !can_unload_buffer(buf)) return; - } /* check no autocommands closed the window */ if (win != NULL && win_valid_any_tab(win)) @@ -1196,8 +1212,6 @@ do_bufdel( return errormsg; } -static int empty_curbuf(int close_others, int forceit, int action); - /* * Make the current buffer empty. * Used when it is wiped out and it's the last buffer. @@ -1238,6 +1252,7 @@ empty_curbuf( need_fileinfo = FALSE; return retval; } + /* * Implementation of the commands for the buffer list. * @@ -1359,11 +1374,8 @@ do_buffer( int forward; bufref_T bufref; - if (buf->b_locked) - { - EMSG(_(e_buflocked)); + if (!can_unload_buffer(buf)) return FAIL; - } set_bufref(&bufref, buf); diff --git a/src/misc2.c b/src/misc2.c --- a/src/misc2.c +++ b/src/misc2.c @@ -6366,33 +6366,38 @@ parse_queued_messages(void) { win_T *old_curwin = curwin; - /* For Win32 mch_breakcheck() does not check for input, do it here. */ + // Do not handle messages while redrawing, because it may cause buffers to + // change or be wiped while they are being redrawn. + if (updating_screen) + return; + + // For Win32 mch_breakcheck() does not check for input, do it here. # if defined(WIN32) && defined(FEAT_JOB_CHANNEL) channel_handle_events(FALSE); # endif # ifdef FEAT_NETBEANS_INTG - /* Process the queued netbeans messages. */ + // Process the queued netbeans messages. netbeans_parse_messages(); # endif # ifdef FEAT_JOB_CHANNEL - /* Write any buffer lines still to be written. */ + // Write any buffer lines still to be written. channel_write_any_lines(); - /* Process the messages queued on channels. */ + // Process the messages queued on channels. channel_parse_messages(); # endif # if defined(FEAT_CLIENTSERVER) && defined(FEAT_X11) - /* Process the queued clientserver messages. */ + // Process the queued clientserver messages. server_parse_messages(); # endif # ifdef FEAT_JOB_CHANNEL - /* Check if any jobs have ended. */ + // Check if any jobs have ended. job_check_ended(); # endif - /* If the current window changed we need to bail out of the waiting loop. - * E.g. when a job exit callback closes the terminal window. */ + // If the current window changed we need to bail out of the waiting loop. + // E.g. when a job exit callback closes the terminal window. if (curwin != old_curwin) ins_char_typebuf(K_IGNORE); } diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -795,6 +795,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 342, +/**/ 341, /**/ 340,