summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2018-09-01 15:30:03 +0200
committerBram Moolenaar <Bram@vim.org>2018-09-01 15:30:03 +0200
commit94f01956a583223dafe24135489d0ab1100ab0ad (patch)
tree536ab4543d93fd2f354a555b49698b1985636576
parent32bbd00949c585ea1c9da13197279a175097eddd (diff)
downloadvim-git-94f01956a583223dafe24135489d0ab1100ab0ad.tar.gz
patch 8.1.0342: crash when a callback deletes a window that is being usedv8.1.0342
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)
-rw-r--r--src/buffer.c42
-rw-r--r--src/misc2.c21
-rw-r--r--src/version.c2
3 files changed, 42 insertions, 23 deletions
diff --git a/src/buffer.c b/src/buffer.c
index 2ca131d23..dcff94ba8 100644
--- 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
index b7716cea1..7c5a45883 100644
--- 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
index c7787e752..b88258e8b 100644
--- 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,