# HG changeset patch # User Christian Brabandt # Date 1468168205 -7200 # Node ID 4d8f7f8da90c7cbc72a302908f9f92b5e62253d1 # Parent 3601fd30708f7e65a27729ee8420f96f5d2dd6ab commit https://github.com/vim/vim/commit/b25f9a97e9aad3cbb4bc3fe87cdbd5700f8aa0c6 Author: Bram Moolenaar 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. diff --git a/src/buffer.c b/src/buffer.c --- 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 diff --git a/src/proto/buffer.pro b/src/proto/buffer.pro --- 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); diff --git a/src/quickfix.c b/src/quickfix.c --- 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; diff --git a/src/structs.h b/src/structs.h --- 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. */ diff --git a/src/version.c b/src/version.c --- 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,