# HG changeset patch # User Christian Brabandt # Date 1442319305 -7200 # Node ID fa95595fbc5236d4318d653795078db4dfe6aa7f # Parent 917ffa98aeeba80f6aea81e1e53aebbef0906236 commit https://github.com/vim/vim/commit/93c88e0f6a4a8f7634ed84721daf4af46fc0d5db Author: Bram Moolenaar Date: Tue Sep 15 14:12:05 2015 +0200 patch 7.4.866 Problem: Crash when changing the 'tags' option from a remote command. (Benjamin Fritz) Solution: Instead of executing messages immediately, use a queue, like for netbeans. (James Kolb) diff --git a/src/ex_docmd.c b/src/ex_docmd.c --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -9033,11 +9033,11 @@ do_sleep(msec) { ui_delay(msec - done > 1000L ? 1000L : msec - done, TRUE); ui_breakcheck(); -#ifdef FEAT_NETBEANS_INTG - /* Process the netbeans messages that may have been received in the - * call to ui_breakcheck() when the GUI is in use. This may occur when - * running a test case. */ - netbeans_parse_messages(); +#ifdef MESSAGE_QUEUE + /* Process the netbeans and clientserver messages that may have been + * received in the call to ui_breakcheck() when the GUI is in use. This + * may occur when running a test case. */ + parse_queued_messages(); #endif } } diff --git a/src/getchar.c b/src/getchar.c --- a/src/getchar.c +++ b/src/getchar.c @@ -3034,9 +3034,8 @@ inchar(buf, maxlen, wait_time, tb_change ) { -#if defined(FEAT_NETBEANS_INTG) - /* Process the queued netbeans messages. */ - netbeans_parse_messages(); +#ifdef MESSAGE_QUEUE + parse_queued_messages(); #endif if (got_int || (script_char = getc(scriptin[curscript])) < 0) diff --git a/src/gui_gtk_x11.c b/src/gui_gtk_x11.c --- a/src/gui_gtk_x11.c +++ b/src/gui_gtk_x11.c @@ -650,7 +650,7 @@ property_event(GtkWidget *widget, xev.xproperty.atom = commProperty; xev.xproperty.window = commWindow; xev.xproperty.state = PropertyNewValue; - serverEventProc(GDK_WINDOW_XDISPLAY(widget->window), &xev); + serverEventProc(GDK_WINDOW_XDISPLAY(widget->window), &xev, 0); } return FALSE; } @@ -5476,9 +5476,8 @@ gui_mch_wait_for_chars(long wtime) focus = gui.in_focus; } -#if defined(FEAT_NETBEANS_INTG) - /* Process any queued netbeans messages. */ - netbeans_parse_messages(); +#ifdef MESSAGE_QUEUE + parse_queued_messages(); #endif /* diff --git a/src/gui_w48.c b/src/gui_w48.c --- a/src/gui_w48.c +++ b/src/gui_w48.c @@ -2016,9 +2016,8 @@ gui_mch_wait_for_chars(int wtime) s_need_activate = FALSE; } -#ifdef FEAT_NETBEANS_INTG - /* Process the queued netbeans messages. */ - netbeans_parse_messages(); +#ifdef MESSAGE_QUEUE + parse_queued_messages(); #endif /* diff --git a/src/gui_x11.c b/src/gui_x11.c --- a/src/gui_x11.c +++ b/src/gui_x11.c @@ -2895,9 +2895,8 @@ gui_mch_wait_for_chars(wtime) focus = gui.in_focus; } -#if defined(FEAT_NETBEANS_INTG) - /* Process any queued netbeans messages. */ - netbeans_parse_messages(); +#ifdef MESSAGE_QUEUE + parse_queued_messages(); #endif /* @@ -3199,7 +3198,7 @@ gui_x11_send_event_handler(w, client_dat if (e->type == PropertyNotify && e->window == commWindow && e->atom == commProperty && e->state == PropertyNewValue) { - serverEventProc(gui.dpy, event); + serverEventProc(gui.dpy, event, 0); } } #endif diff --git a/src/if_xcmdsrv.c b/src/if_xcmdsrv.c --- a/src/if_xcmdsrv.c +++ b/src/if_xcmdsrv.c @@ -169,6 +169,19 @@ enum ServerReplyOp { SROP_Find, SROP_Add typedef int (*EndCond) __ARGS((void *)); +struct x_cmdqueue +{ + char_u *propInfo; + int len; + struct x_cmdqueue *next; + struct x_cmdqueue *prev; +}; + +typedef struct x_cmdqueue x_queue_T; + +/* dummy node, header for circular queue */ +static x_queue_T head = {NULL, 0, NULL, NULL}; + /* * Forward declarations for procedures defined later in this file: */ @@ -186,6 +199,8 @@ static struct ServerReply *ServerReplyFi static int AppendPropCarefully __ARGS((Display *display, Window window, Atom property, char_u *value, int length)); static int x_error_check __ARGS((Display *dpy, XErrorEvent *error_event)); static int IsSerialName __ARGS((char_u *name)); +static void save_in_queue __ARGS((char_u *buf, int len)); +static void server_parse_message __ARGS((Display *dpy, char_u *propInfo, int numItems)); /* Private variables for the "server" functionality */ static Atom registryProperty = None; @@ -595,7 +610,7 @@ ServerWait(dpy, w, endCond, endData, loc while (TRUE) { while (XCheckWindowEvent(dpy, commWindow, PropertyChangeMask, &event)) - serverEventProc(dpy, &event); + serverEventProc(dpy, &event, 1); if (endCond(endData) != 0) break; @@ -1127,22 +1142,25 @@ GetRegProp(dpy, regPropp, numItemsp, dom return OK; } + /* * This procedure is invoked by the various X event loops throughout Vims when * a property changes on the communication window. This procedure reads the - * property and handles command requests and responses. + * property and enqueues command requests and responses. If immediate is true, + * it runs the event immediatly instead of enqueuing it. Immediate can cause + * unintended behavior and should only be used for code that blocks for a + * response. */ void -serverEventProc(dpy, eventPtr) +serverEventProc(dpy, eventPtr, immediate) Display *dpy; - XEvent *eventPtr; /* Information about event. */ + XEvent *eventPtr; /* Information about event. */ + int immediate; /* Run event immediately. Should mostly be 0. */ { char_u *propInfo; - char_u *p; - int result, actualFormat, code; + int result, actualFormat; long_u numItems, bytesAfter; Atom actualType; - char_u *tofree; if (eventPtr != NULL) { @@ -1168,6 +1186,87 @@ serverEventProc(dpy, eventPtr) XFree(propInfo); return; } + if (immediate) + server_parse_message(dpy, propInfo, numItems); + else + save_in_queue(propInfo, numItems); +} + +/* + * Saves x clientserver commands in a queue so that they can be called when + * vim is idle. + */ + static void +save_in_queue(propInfo, len) + char_u *propInfo; + int len; +{ + x_queue_T *node; + + node = (x_queue_T *)alloc(sizeof(x_queue_T)); + if (node == NULL) + return; /* out of memory */ + node->propInfo = propInfo; + node->len = len; + + if (head.next == NULL) /* initialize circular queue */ + { + head.next = &head; + head.prev = &head; + } + + /* insert node at tail of queue */ + node->next = &head; + node->prev = head.prev; + head.prev->next = node; + head.prev = node; +} + +/* + * Parses queued clientserver messages. + */ + void +server_parse_messages() +{ + char_u *p; + x_queue_T *node; + + if (!X_DISPLAY) + return; /* cannot happen? */ + while (head.next != NULL && head.next != &head) + { + node = head.next; + server_parse_message(X_DISPLAY, node->propInfo, node->len); + head.next = node->next; + node->next->prev = node->prev; + vim_free(node); + } +} + +/* + * Returns a non-zero value if there are clientserver messages waiting + * int the queue. + */ + int +server_waiting() +{ + return head.next != NULL && head.next != &head; +} + +/* + * Prases a single clientserver message. A single message may contain multiple + * commands. + * "propInfo" will be freed. + */ + static void +server_parse_message(dpy, propInfo, numItems) + Display *dpy; + char_u *propInfo; /* A string containing 0 or more X commands */ + int numItems; /* The size of propInfo in bytes. */ +{ + char_u *p; + int code; + char_u *tofree; /* * Several commands and results could arrive in the property at @@ -1248,16 +1347,16 @@ serverEventProc(dpy, eventPtr) if (script == NULL || name == NULL) continue; - if (serverName != NULL && STRICMP(name, serverName) == 0) - { - script = serverConvert(enc, script, &tofree); - if (asKeys) - server_to_input_buf(script); - else - { - char_u *res; + if (serverName != NULL && STRICMP(name, serverName) == 0) + { + script = serverConvert(enc, script, &tofree); + if (asKeys) + server_to_input_buf(script); + else + { + char_u *res; - res = eval_client_expr_to_string(script); + res = eval_client_expr_to_string(script); if (resWindow != None) { garray_T reply; @@ -1290,10 +1389,10 @@ serverEventProc(dpy, eventPtr) reply.ga_data, reply.ga_len); ga_clear(&reply); } - vim_free(res); - } - vim_free(tofree); - } + vim_free(res); + } + vim_free(tofree); + } } else if (*p == 'r' && p[1] == 0) { diff --git a/src/macros.h b/src/macros.h --- a/src/macros.h +++ b/src/macros.h @@ -321,3 +321,7 @@ #else # define PLINES_NOFILL(x) plines(x) #endif + +#if defined(FEAT_NETBEANS_INTG) || defined(FEAT_CLIENTSERVER) +# define MESSAGE_QUEUE +#endif diff --git a/src/misc2.c b/src/misc2.c --- a/src/misc2.c +++ b/src/misc2.c @@ -6328,3 +6328,23 @@ has_non_ascii(s) return FALSE; } #endif + +#if defined(MESSAGE_QUEUE) || defined(PROTO) +/* + * Process messages that have been queued for netbeans or clientserver. + * These functions can call arbitrary vimscript and should only be called when + * it is safe to do so. + */ + void +parse_queued_messages() +{ +# ifdef FEAT_NETBEANS_INTG + /* Process the queued netbeans messages. */ + netbeans_parse_messages(); +# endif +# ifdef FEAT_CLIENTSERVER + /* Process the queued clientserver messages. */ + server_parse_messages(); +# endif +} +#endif diff --git a/src/os_unix.c b/src/os_unix.c --- a/src/os_unix.c +++ b/src/os_unix.c @@ -388,9 +388,8 @@ mch_inchar(buf, maxlen, wtime, tb_change { int len; -#ifdef FEAT_NETBEANS_INTG - /* Process the queued netbeans messages. */ - netbeans_parse_messages(); +#ifdef MESSAGE_QUEUE + parse_queued_messages(); #endif /* Check if window changed size while we were busy, perhaps the ":set @@ -405,9 +404,8 @@ mch_inchar(buf, maxlen, wtime, tb_change if (!do_resize) /* return if not interrupted by resize */ return 0; handle_resize(); -#ifdef FEAT_NETBEANS_INTG - /* Process the queued netbeans messages. */ - netbeans_parse_messages(); +#ifdef MESSAGE_QUEUE + parse_queued_messages(); #endif } } @@ -439,9 +437,8 @@ mch_inchar(buf, maxlen, wtime, tb_change while (do_resize) /* window changed size */ handle_resize(); -#ifdef FEAT_NETBEANS_INTG - /* Process the queued netbeans messages. */ - netbeans_parse_messages(); +#ifdef MESSAGE_QUEUE + parse_queued_messages(); #endif /* * We want to be interrupted by the winch signal @@ -5208,6 +5205,7 @@ WaitForChar(msec) * When a GUI is being used, this will not be used for input -- webb * Returns also, when a request from Sniff is waiting -- toni. * Or when a Linux GPM mouse event is waiting. + * Or when a clientserver message is on the queue. */ #if defined(__BEOS__) int @@ -5601,6 +5599,11 @@ select_eintr: if (finished || msec == 0) break; +# ifdef FEAT_CLIENTSERVER + if (server_waiting()) + break; +# endif + /* We're going to loop around again, find out for how long */ if (msec > 0) { @@ -7106,31 +7109,31 @@ xterm_update() for (;;) { - XtInputMask mask = XtAppPending(app_context); - - if (mask == 0 || vim_is_input_buf_full()) + XtInputMask mask = XtAppPending(app_context); + + if (mask == 0 || vim_is_input_buf_full()) break; - if (mask & XtIMXEvent) + if (mask & XtIMXEvent) { /* There is an event to process. */ - XtAppNextEvent(app_context, &event); + XtAppNextEvent(app_context, &event); #ifdef FEAT_CLIENTSERVER { XPropertyEvent *e = (XPropertyEvent *)&event; if (e->type == PropertyNotify && e->window == commWindow && e->atom == commProperty && e->state == PropertyNewValue) - serverEventProc(xterm_dpy, &event); + serverEventProc(xterm_dpy, &event, 0); } #endif - XtDispatchEvent(&event); - } + XtDispatchEvent(&event); + } else { /* There is something else than an event to process. */ - XtAppProcessEvent(app_context, mask); - } + XtAppProcessEvent(app_context, mask); + } } } diff --git a/src/proto/if_xcmdsrv.pro b/src/proto/if_xcmdsrv.pro --- a/src/proto/if_xcmdsrv.pro +++ b/src/proto/if_xcmdsrv.pro @@ -7,5 +7,7 @@ Window serverStrToWin __ARGS((char_u *st int serverSendReply __ARGS((char_u *name, char_u *str)); int serverReadReply __ARGS((Display *dpy, Window win, char_u **str, int localLoop)); int serverPeekReply __ARGS((Display *dpy, Window win, char_u **str)); -void serverEventProc __ARGS((Display *dpy, XEvent *eventPtr)); +void serverEventProc __ARGS((Display *dpy, XEvent *eventPtr, int immediate)); +void server_parse_messages __ARGS((void)); +int server_waiting __ARGS((void)); /* vim: set ft=c : */ diff --git a/src/proto/misc2.pro b/src/proto/misc2.pro --- a/src/proto/misc2.pro +++ b/src/proto/misc2.pro @@ -106,4 +106,5 @@ int put_bytes __ARGS((FILE *fd, long_u n void put_time __ARGS((FILE *fd, time_t the_time)); void time_to_bytes __ARGS((time_t the_time, char_u *buf)); int has_non_ascii __ARGS((char_u *s)); +void parse_queued_messages __ARGS((void)); /* vim: set ft=c : */ diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -742,6 +742,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 866, +/**/ 865, /**/ 864,