To: vim_dev@googlegroups.com Subject: Patch 7.4.2018 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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. Files: src/structs.h, src/buffer.c, src/quickfix.c, src/proto/buffer.pro *** ../vim-7.4.2017/src/structs.h 2016-07-03 17:47:21.858812593 +0200 --- src/structs.h 2016-07-10 17:03:56.106490100 +0200 *************** *** 69,74 **** --- 69,82 ---- 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. */ *** ../vim-7.4.2017/src/buffer.c 2016-07-10 17:00:33.313537282 +0200 --- src/buffer.c 2016-07-10 18:18:04.572170378 +0200 *************** *** 67,72 **** --- 67,75 ---- 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,315 **** --- 312,340 ---- } /* + * 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,356 **** --- 376,382 ---- #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,407 **** } #ifdef FEAT_AUTOCMD /* 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)) { /* Autocommands deleted the buffer. */ aucmd_abort: --- 421,435 ---- } #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) ! && !bufref_valid(&bufref)) { /* Autocommands deleted the buffer. */ aucmd_abort: *************** *** 420,426 **** buf->b_closing = TRUE; if (apply_autocmds(EVENT_BUFHIDDEN, buf->b_fname, buf->b_fname, FALSE, buf) ! && !buf_valid(buf)) /* Autocommands deleted the buffer. */ goto aucmd_abort; buf->b_closing = FALSE; --- 448,454 ---- buf->b_closing = TRUE; if (apply_autocmds(EVENT_BUFHIDDEN, buf->b_fname, buf->b_fname, FALSE, buf) ! && !bufref_valid(&bufref)) /* Autocommands deleted the buffer. */ goto aucmd_abort; buf->b_closing = FALSE; *************** *** 464,470 **** #ifdef FEAT_AUTOCMD /* Autocommands may have deleted the buffer. */ ! if (!buf_valid(buf)) return; # ifdef FEAT_EVAL if (aborting()) /* autocmds may abort script processing */ --- 492,498 ---- #ifdef FEAT_AUTOCMD /* Autocommands may have deleted the buffer. */ ! if (!bufref_valid(&bufref)) return; # ifdef FEAT_EVAL if (aborting()) /* autocmds may abort script processing */ *************** *** 575,601 **** { #ifdef FEAT_AUTOCMD int is_curbuf = (buf == curbuf); buf->b_closing = TRUE; 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 */ 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 */ 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 */ return; } buf->b_closing = FALSE; --- 603,634 ---- { #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) ! && !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) ! && !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) ! && !bufref_valid(&bufref)) ! /* autocommands deleted the buffer */ return; } buf->b_closing = FALSE; *************** *** 662,667 **** --- 695,701 ---- 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,1032 **** --- 1061,1067 ---- { int retval; buf_T *buf = curbuf; + bufref_T bufref; if (action == DOBUF_UNLOAD) { *************** *** 1034,1046 **** return FAIL; } if (close_others) - { /* Close any other windows on this buffer, then make it empty. */ - #ifdef FEAT_WINDOWS close_windows(buf, TRUE); #endif - } setpcmark(); retval = do_ecmd(0, NULL, NULL, NULL, ECMD_ONE, --- 1069,1080 ---- return FAIL; } + 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,1057 **** * 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) close_buffer(NULL, buf, action, FALSE); if (!close_others) need_fileinfo = FALSE; --- 1085,1091 ---- * the old one. But do_ecmd() may have done that already, check * if the buffer still exists. */ ! if (buf != curbuf && bufref_valid(&bufref) && buf->b_nwindows == 0) close_buffer(NULL, buf, action, FALSE); if (!close_others) need_fileinfo = FALSE; *************** *** 1177,1182 **** --- 1211,1221 ---- 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,1196 **** { dialog_changed(buf, FALSE); # ifdef FEAT_AUTOCMD ! if (!buf_valid(buf)) /* Autocommand deleted buffer, oops! It's not changed * now. */ return FAIL; --- 1229,1235 ---- { dialog_changed(buf, FALSE); # ifdef FEAT_AUTOCMD ! if (!bufref_valid(&bufref)) /* Autocommand deleted buffer, oops! It's not changed * now. */ return FAIL; *************** *** 1243,1251 **** { #ifdef FEAT_WINDOWS close_windows(buf, FALSE); #endif ! if (buf != curbuf && buf_valid(buf) && buf->b_nwindows <= 0) ! close_buffer(NULL, buf, action, FALSE); return OK; } --- 1282,1291 ---- { #ifdef FEAT_WINDOWS close_windows(buf, FALSE); + if (buf != curbuf && bufref_valid(&bufref)) #endif ! if (buf->b_nwindows <= 0) ! close_buffer(NULL, buf, action, FALSE); return OK; } *************** *** 1390,1398 **** #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) if ((p_confirm || cmdmod.confirm) && p_write) { dialog_changed(curbuf, FALSE); # ifdef FEAT_AUTOCMD ! if (!buf_valid(buf)) /* Autocommand deleted buffer, oops! */ return FAIL; # endif --- 1430,1443 ---- #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 (!bufref_valid(&bufref)) /* Autocommand deleted buffer, oops! */ return FAIL; # endif *************** *** 1443,1448 **** --- 1488,1496 ---- #ifdef FEAT_SYN_HL long old_tw = curbuf->b_p_tw; #endif + #ifdef FEAT_AUTOCMD + bufref_T bufref; + #endif setpcmark(); if (!cmdmod.keepalt) *************** *** 1456,1466 **** prevbuf = curbuf; #ifdef FEAT_AUTOCMD if (!apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf) # ifdef FEAT_EVAL ! || (buf_valid(prevbuf) && !aborting())) # else ! || buf_valid(prevbuf)) # endif #endif { --- 1504,1515 ---- prevbuf = curbuf; #ifdef FEAT_AUTOCMD + set_bufref(&bufref, prevbuf); if (!apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf) # ifdef FEAT_EVAL ! || (bufref_valid(&bufref) && !aborting())) # else ! || bufref_valid(&bufref)) # endif #endif { *************** *** 1473,1481 **** close_windows(prevbuf, FALSE); #endif #if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL) ! if (buf_valid(prevbuf) && !aborting()) #else ! if (buf_valid(prevbuf)) #endif { #ifdef FEAT_WINDOWS --- 1522,1530 ---- close_windows(prevbuf, FALSE); #endif #if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL) ! if (bufref_valid(&bufref) && !aborting()) #else ! if (bufref_valid(&bufref)) #endif { #ifdef FEAT_WINDOWS *************** *** 1496,1502 **** } #ifdef FEAT_AUTOCMD /* An autocommand may have deleted "buf", already entered it (e.g., when ! * 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 --- 1545,1551 ---- } #ifdef FEAT_AUTOCMD /* An autocommand may have deleted "buf", already entered it (e.g., when ! * 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,1717 **** if ((flags & BLN_LISTED) && !buf->b_p_bl) { buf->b_p_bl = TRUE; #ifdef FEAT_AUTOCMD if (!(flags & BLN_DUMMY)) { if (apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, buf) ! && !buf_valid(buf)) return NULL; } #endif --- 1755,1770 ---- 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) ! && !bufref_valid(&bufref)) return NULL; } #endif *************** *** 1887,1902 **** #ifdef FEAT_AUTOCMD if (!(flags & BLN_DUMMY)) { /* 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. */ if (apply_autocmds(EVENT_BUFNEW, NULL, NULL, FALSE, buf) ! && !buf_valid(buf)) return NULL; if (flags & BLN_LISTED) { if (apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, buf) ! && !buf_valid(buf)) return NULL; } # ifdef FEAT_EVAL --- 1940,1958 ---- #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) ! && !bufref_valid(&bufref)) return NULL; if (flags & BLN_LISTED) { if (apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, buf) ! && !bufref_valid(&bufref)) return NULL; } # ifdef FEAT_EVAL *************** *** 4708,4717 **** if (!P_HID(buf) && buf->b_nwindows <= 1 && bufIsChanged(buf)) { (void)autowrite(buf, FALSE); #ifdef FEAT_AUTOCMD /* check if autocommands removed the window */ ! if (!win_valid(wp) || !buf_valid(buf)) { wpnext = firstwin; /* start all over... */ continue; --- 4764,4778 ---- 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) || !bufref_valid(&bufref)) { wpnext = firstwin; /* start all over... */ continue; *************** *** 4993,4998 **** --- 5054,5064 ---- 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,5015 **** #endif set_curbuf(buf, DOBUF_GOTO); #ifdef FEAT_AUTOCMD ! if (!buf_valid(buf)) /* autocommands deleted the buffer!!! */ { #if defined(HAS_SWAP_EXISTS_ACTION) swap_exists_action = SEA_NONE; # endif --- 5074,5082 ---- #endif set_curbuf(buf, DOBUF_GOTO); #ifdef FEAT_AUTOCMD ! if (!bufref_valid(&bufref)) { + /* autocommands deleted the buffer!!! */ #if defined(HAS_SWAP_EXISTS_ACTION) swap_exists_action = SEA_NONE; # endif *** ../vim-7.4.2017/src/quickfix.c 2016-07-10 17:00:33.317537221 +0200 --- src/quickfix.c 2016-07-10 17:16:38.511074061 +0200 *************** *** 1487,1493 **** * 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; /* * Get buffer number for file "dir.name". --- 1487,1493 ---- * to make this a lot faster if there are multiple matches in the same file. */ static char_u *qf_last_bufname = NULL; ! static bufref_T qf_last_bufref = {NULL, 0}; /* * Get buffer number for file "dir.name". *************** *** 1536,1544 **** bufname = fname; if (qf_last_bufname != NULL && STRCMP(bufname, qf_last_bufname) == 0 ! && buf_valid(qf_last_buf)) { ! buf = qf_last_buf; vim_free(ptr); } else --- 1536,1544 ---- bufname = fname; if (qf_last_bufname != NULL && STRCMP(bufname, qf_last_bufname) == 0 ! && bufref_valid(&qf_last_bufref)) { ! buf = qf_last_bufref.br_buf; vim_free(ptr); } else *************** *** 1549,1555 **** qf_last_bufname = bufname; else qf_last_bufname = vim_strsave(bufname); ! qf_last_buf = buf; } if (buf == NULL) return 0; --- 1549,1555 ---- qf_last_bufname = bufname; else qf_last_bufname = vim_strsave(bufname); ! set_bufref(&qf_last_bufref, buf); } if (buf == NULL) return 0; *** ../vim-7.4.2017/src/proto/buffer.pro 2016-01-19 13:21:55.833334420 +0100 --- src/proto/buffer.pro 2016-07-10 17:11:50.255384426 +0200 *************** *** 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); *** ../vim-7.4.2017/src/version.c 2016-07-10 17:00:33.321537162 +0200 --- src/version.c 2016-07-10 17:04:51.681655986 +0200 *************** *** 760,761 **** --- 760,763 ---- { /* Add new patch number below this line */ + /**/ + 2018, /**/ -- hundred-and-one symptoms of being an internet addict: 264. You turn to the teletext page "surfing report" and are surprised that it is about sizes of waves and a weather forecast for seaside resorts. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///