changeset 9475:4d8f7f8da90c v7.4.2018

commit https://github.com/vim/vim/commit/b25f9a97e9aad3cbb4bc3fe87cdbd5700f8aa0c6 Author: Bram Moolenaar <Bram@vim.org> Date: Sun Jul 10 18:21:50 2016 +0200 patch 7.4.2018 Problem: buf_valid() can be slow when there are many buffers. Solution: Add bufref_valid(), only go through the buffer list when a buffer was freed.
author Christian Brabandt <cb@256bit.org>
date Sun, 10 Jul 2016 18:30:05 +0200
parents 3601fd30708f
children 9c072d3b98e8
files src/buffer.c src/proto/buffer.pro src/quickfix.c src/structs.h src/version.c
diffstat 5 files changed, 109 insertions(+), 30 deletions(-) [+]
line wrap: on
line diff
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -67,6 +67,9 @@ static char *msg_qflist = N_("[Quickfix 
 static char *e_auabort = N_("E855: Autocommands caused command to abort");
 #endif
 
+/* Number of times free_buffer() was called. */
+static int	buf_free_count = 0;
+
 /*
  * Open current buffer, that is: open the memfile and read the file into
  * memory.
@@ -309,7 +312,29 @@ open_buffer(
 }
 
 /*
+ * Store "buf" in "bufref" and set the free count.
+ */
+    void
+set_bufref(bufref_T *bufref, buf_T *buf)
+{
+    bufref->br_buf = buf;
+    bufref->br_buf_free_count = buf_free_count;
+}
+
+/*
+ * Return TRUE if "bufref->br_buf" points to a valid buffer.
+ * Only goes through the buffer list if buf_free_count changed.
+ */
+    int
+bufref_valid(bufref_T *bufref)
+{
+    return bufref->br_buf_free_count == buf_free_count
+					   ? TRUE : buf_valid(bufref->br_buf);
+}
+
+/*
  * Return TRUE if "buf" points to a valid buffer (in the buffer list).
+ * This can be slow if there are many buffers, prefer using bufref_valid().
  */
     int
 buf_valid(buf_T *buf)
@@ -351,6 +376,7 @@ close_buffer(
 #ifdef FEAT_AUTOCMD
     int		is_curbuf;
     int		nwindows;
+    bufref_T	bufref;
 #endif
     int		unload_buf = (action != 0);
     int		del_buf = (action == DOBUF_DEL || action == DOBUF_WIPE);
@@ -395,13 +421,15 @@ close_buffer(
     }
 
 #ifdef FEAT_AUTOCMD
+    set_bufref(&bufref, buf);
+
     /* When the buffer is no longer in a window, trigger BufWinLeave */
     if (buf->b_nwindows == 1)
     {
 	buf->b_closing = TRUE;
 	if (apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname, buf->b_fname,
 								  FALSE, buf)
-		&& !buf_valid(buf))
+		&& !bufref_valid(&bufref))
 	{
 	    /* Autocommands deleted the buffer. */
 aucmd_abort:
@@ -420,7 +448,7 @@ aucmd_abort:
 	    buf->b_closing = TRUE;
 	    if (apply_autocmds(EVENT_BUFHIDDEN, buf->b_fname, buf->b_fname,
 								  FALSE, buf)
-		    && !buf_valid(buf))
+		    && !bufref_valid(&bufref))
 		/* Autocommands deleted the buffer. */
 		goto aucmd_abort;
 	    buf->b_closing = FALSE;
@@ -464,7 +492,7 @@ aucmd_abort:
 
 #ifdef FEAT_AUTOCMD
     /* Autocommands may have deleted the buffer. */
-    if (!buf_valid(buf))
+    if (!bufref_valid(&bufref))
 	return;
 # ifdef FEAT_EVAL
     if (aborting())	    /* autocmds may abort script processing */
@@ -575,27 +603,32 @@ buf_freeall(buf_T *buf, int flags)
 {
 #ifdef FEAT_AUTOCMD
     int		is_curbuf = (buf == curbuf);
+    bufref_T	bufref;
 
     buf->b_closing = TRUE;
+    set_bufref(&bufref, buf);
     if (buf->b_ml.ml_mfp != NULL)
     {
 	if (apply_autocmds(EVENT_BUFUNLOAD, buf->b_fname, buf->b_fname,
 								  FALSE, buf)
-		&& !buf_valid(buf))	/* autocommands may delete the buffer */
+		&& !bufref_valid(&bufref))
+	    /* autocommands deleted the buffer */
 	    return;
     }
     if ((flags & BFA_DEL) && buf->b_p_bl)
     {
 	if (apply_autocmds(EVENT_BUFDELETE, buf->b_fname, buf->b_fname,
 								   FALSE, buf)
-		&& !buf_valid(buf))	/* autocommands may delete the buffer */
+		&& !bufref_valid(&bufref))
+	    /* autocommands deleted the buffer */
 	    return;
     }
     if (flags & BFA_WIPE)
     {
 	if (apply_autocmds(EVENT_BUFWIPEOUT, buf->b_fname, buf->b_fname,
 								  FALSE, buf)
-		&& !buf_valid(buf))	/* autocommands may delete the buffer */
+		&& !bufref_valid(&bufref))
+	    /* autocommands deleted the buffer */
 	    return;
     }
     buf->b_closing = FALSE;
@@ -662,6 +695,7 @@ buf_freeall(buf_T *buf, int flags)
     static void
 free_buffer(buf_T *buf)
 {
+    ++buf_free_count;
     free_buffer_stuff(buf, TRUE);
 #ifdef FEAT_EVAL
     unref_var_dict(buf->b_vars);
@@ -1027,6 +1061,7 @@ empty_curbuf(
 {
     int	    retval;
     buf_T   *buf = curbuf;
+    bufref_T bufref;
 
     if (action == DOBUF_UNLOAD)
     {
@@ -1034,13 +1069,12 @@ empty_curbuf(
 	return FAIL;
     }
 
-    if (close_others)
-    {
-	/* Close any other windows on this buffer, then make it empty. */
+    set_bufref(&bufref, buf);
 #ifdef FEAT_WINDOWS
+    if (close_others)
+	/* Close any other windows on this buffer, then make it empty. */
 	close_windows(buf, TRUE);
 #endif
-    }
 
     setpcmark();
     retval = do_ecmd(0, NULL, NULL, NULL, ECMD_ONE,
@@ -1051,7 +1085,7 @@ empty_curbuf(
      * the old one.  But do_ecmd() may have done that already, check
      * if the buffer still exists.
      */
-    if (buf != curbuf && buf_valid(buf) && buf->b_nwindows == 0)
+    if (buf != curbuf && bufref_valid(&bufref) && buf->b_nwindows == 0)
 	close_buffer(NULL, buf, action, FALSE);
     if (!close_others)
 	need_fileinfo = FALSE;
@@ -1177,6 +1211,11 @@ do_buffer(
     if (unload)
     {
 	int	forward;
+# if defined(FEAT_AUTOCMD) || defined(FEAT_WINDOWS)
+	bufref_T bufref;
+
+	set_bufref(&bufref, buf);
+# endif
 
 	/* When unloading or deleting a buffer that's already unloaded and
 	 * unlisted: fail silently. */
@@ -1190,7 +1229,7 @@ do_buffer(
 	    {
 		dialog_changed(buf, FALSE);
 # ifdef FEAT_AUTOCMD
-		if (!buf_valid(buf))
+		if (!bufref_valid(&bufref))
 		    /* Autocommand deleted buffer, oops!  It's not changed
 		     * now. */
 		    return FAIL;
@@ -1243,9 +1282,10 @@ do_buffer(
 	{
 #ifdef FEAT_WINDOWS
 	    close_windows(buf, FALSE);
-#endif
-	    if (buf != curbuf && buf_valid(buf) && buf->b_nwindows <= 0)
-		close_buffer(NULL, buf, action, FALSE);
+	    if (buf != curbuf && bufref_valid(&bufref))
+#endif
+		if (buf->b_nwindows <= 0)
+		    close_buffer(NULL, buf, action, FALSE);
 	    return OK;
 	}
 
@@ -1390,9 +1430,14 @@ do_buffer(
 #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
 	if ((p_confirm || cmdmod.confirm) && p_write)
 	{
+# ifdef FEAT_AUTOCMD
+	    bufref_T bufref;
+
+	    set_bufref(&bufref, buf);
+# endif
 	    dialog_changed(curbuf, FALSE);
 # ifdef FEAT_AUTOCMD
-	    if (!buf_valid(buf))
+	    if (!bufref_valid(&bufref))
 		/* Autocommand deleted buffer, oops! */
 		return FAIL;
 # endif
@@ -1443,6 +1488,9 @@ set_curbuf(buf_T *buf, int action)
 #ifdef FEAT_SYN_HL
     long	old_tw = curbuf->b_p_tw;
 #endif
+#ifdef FEAT_AUTOCMD
+    bufref_T	bufref;
+#endif
 
     setpcmark();
     if (!cmdmod.keepalt)
@@ -1456,11 +1504,12 @@ set_curbuf(buf_T *buf, int action)
     prevbuf = curbuf;
 
 #ifdef FEAT_AUTOCMD
+    set_bufref(&bufref, prevbuf);
     if (!apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf)
 # ifdef FEAT_EVAL
-	    || (buf_valid(prevbuf) && !aborting()))
+	    || (bufref_valid(&bufref) && !aborting()))
 # else
-	    || buf_valid(prevbuf))
+	    || bufref_valid(&bufref))
 # endif
 #endif
     {
@@ -1473,9 +1522,9 @@ set_curbuf(buf_T *buf, int action)
 	    close_windows(prevbuf, FALSE);
 #endif
 #if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL)
-	if (buf_valid(prevbuf) && !aborting())
+	if (bufref_valid(&bufref) && !aborting())
 #else
-	if (buf_valid(prevbuf))
+	if (bufref_valid(&bufref))
 #endif
 	{
 #ifdef FEAT_WINDOWS
@@ -1496,7 +1545,7 @@ set_curbuf(buf_T *buf, int action)
     }
 #ifdef FEAT_AUTOCMD
     /* An autocommand may have deleted "buf", already entered it (e.g., when
-     * it did ":bunload") or aborted the script processing!
+     * it did ":bunload") or aborted the script processing.
      * If curwin->w_buffer is null, enter_buffer() will make it valid again */
     if ((buf_valid(buf) && buf != curbuf
 # ifdef FEAT_EVAL
@@ -1706,12 +1755,16 @@ buflist_new(
 
 	if ((flags & BLN_LISTED) && !buf->b_p_bl)
 	{
+#ifdef FEAT_AUTOCMD
+	    bufref_T bufref;
+#endif
 	    buf->b_p_bl = TRUE;
 #ifdef FEAT_AUTOCMD
+	    set_bufref(&bufref, buf);
 	    if (!(flags & BLN_DUMMY))
 	    {
 		if (apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, buf)
-			&& !buf_valid(buf))
+			&& !bufref_valid(&bufref))
 		    return NULL;
 	    }
 #endif
@@ -1887,16 +1940,19 @@ buflist_new(
 #ifdef FEAT_AUTOCMD
     if (!(flags & BLN_DUMMY))
     {
+	bufref_T bufref;
+
 	/* Tricky: these autocommands may change the buffer list.  They could
 	 * also split the window with re-using the one empty buffer. This may
 	 * result in unexpectedly losing the empty buffer. */
+	set_bufref(&bufref, buf);
 	if (apply_autocmds(EVENT_BUFNEW, NULL, NULL, FALSE, buf)
-		&& !buf_valid(buf))
+		&& !bufref_valid(&bufref))
 	    return NULL;
 	if (flags & BLN_LISTED)
 	{
 	    if (apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, buf)
-		    && !buf_valid(buf))
+		    && !bufref_valid(&bufref))
 		return NULL;
 	}
 # ifdef FEAT_EVAL
@@ -4708,10 +4764,15 @@ do_arg_all(
 		    if (!P_HID(buf) && buf->b_nwindows <= 1
 							 && bufIsChanged(buf))
 		    {
+#ifdef FEAT_AUTOCMD
+			bufref_T    bufref;
+
+			set_bufref(&bufref, buf);
+#endif
 			(void)autowrite(buf, FALSE);
 #ifdef FEAT_AUTOCMD
 			/* check if autocommands removed the window */
-			if (!win_valid(wp) || !buf_valid(buf))
+			if (!win_valid(wp) || !bufref_valid(&bufref))
 			{
 			    wpnext = firstwin;	/* start all over... */
 			    continue;
@@ -4993,6 +5054,11 @@ ex_buffer_all(exarg_T *eap)
 
 	if (wp == NULL && split_ret == OK)
 	{
+#ifdef FEAT_AUTOCMD
+	    bufref_T	bufref;
+
+	    set_bufref(&bufref, buf);
+#endif
 	    /* Split the window and put the buffer in it */
 	    p_ea_save = p_ea;
 	    p_ea = TRUE;		/* use space from all windows */
@@ -5008,8 +5074,9 @@ ex_buffer_all(exarg_T *eap)
 #endif
 	    set_curbuf(buf, DOBUF_GOTO);
 #ifdef FEAT_AUTOCMD
-	    if (!buf_valid(buf))	/* autocommands deleted the buffer!!! */
+	    if (!bufref_valid(&bufref))
 	    {
+		/* autocommands deleted the buffer!!! */
 #if defined(HAS_SWAP_EXISTS_ACTION)
 		swap_exists_action = SEA_NONE;
 # endif
--- a/src/proto/buffer.pro
+++ b/src/proto/buffer.pro
@@ -1,5 +1,7 @@
 /* buffer.c */
 int open_buffer(int read_stdin, exarg_T *eap, int flags);
+void set_bufref(bufref_T *bufref, buf_T *buf);
+int bufref_valid(bufref_T *bufref);
 int buf_valid(buf_T *buf);
 void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last);
 void buf_clear_file(buf_T *buf);
--- a/src/quickfix.c
+++ b/src/quickfix.c
@@ -1487,7 +1487,7 @@ copy_loclist(win_T *from, win_T *to)
  * to make this a lot faster if there are multiple matches in the same file.
  */
 static char_u *qf_last_bufname = NULL;
-static buf_T  *qf_last_buf = NULL;
+static bufref_T  qf_last_bufref = {NULL, 0};
 
 /*
  * Get buffer number for file "dir.name".
@@ -1536,9 +1536,9 @@ qf_get_fnum(qf_info_T *qi, char_u *direc
 	bufname = fname;
 
     if (qf_last_bufname != NULL && STRCMP(bufname, qf_last_bufname) == 0
-	    && buf_valid(qf_last_buf))
+	    && bufref_valid(&qf_last_bufref))
     {
-	buf = qf_last_buf;
+	buf = qf_last_bufref.br_buf;
 	vim_free(ptr);
     }
     else
@@ -1549,7 +1549,7 @@ qf_get_fnum(qf_info_T *qi, char_u *direc
 	    qf_last_bufname = bufname;
 	else
 	    qf_last_bufname = vim_strsave(bufname);
-	qf_last_buf = buf;
+	set_bufref(&qf_last_bufref, buf);
     }
     if (buf == NULL)
 	return 0;
--- a/src/structs.h
+++ b/src/structs.h
@@ -69,6 +69,14 @@ typedef struct frame_S		frame_T;
 typedef int			scid_T;		/* script ID */
 typedef struct file_buffer	buf_T;  /* forward declaration */
 
+/* Reference to a buffer that stores the value of buf_free_count.
+ * bufref_valid() only needs to check "buf" when the count differs.
+ */
+typedef struct {
+    buf_T   *br_buf;
+    int	    br_buf_free_count;
+} bufref_T;
+
 /*
  * This is here because regexp.h needs pos_T and below regprog_T is used.
  */
--- a/src/version.c
+++ b/src/version.c
@@ -759,6 +759,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    2018,
+/**/
     2017,
 /**/
     2016,