Skip to content

Commit 81b9d0b

Browse files
committed
patch 8.0.0492: a failing client-server request can make Vim hang
Problem: A failing client-server request can make Vim hang. Solution: Add a timeout argument to functions that wait.
1 parent bfd830d commit 81b9d0b

File tree

10 files changed

+84
-76
lines changed

10 files changed

+84
-76
lines changed

runtime/doc/eval.txt

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6320,15 +6320,17 @@ reltimestr({time}) *reltimestr()*
63206320
{only available when compiled with the |+reltime| feature}
63216321

63226322
*remote_expr()* *E449*
6323-
remote_expr({server}, {string} [, {idvar}])
6323+
remote_expr({server}, {string} [, {idvar} [, {timeout}]])
63246324
Send the {string} to {server}. The string is sent as an
63256325
expression and the result is returned after evaluation.
63266326
The result must be a String or a |List|. A |List| is turned
63276327
into a String by joining the items with a line break in
63286328
between (not at the end), like with join(expr, "\n").
6329-
If {idvar} is present, it is taken as the name of a
6330-
variable and a {serverid} for later use with
6329+
If {idvar} is present and not empty, it is taken as the name
6330+
of a variable and a {serverid} for later use with
63316331
remote_read() is stored there.
6332+
If {timeout} is given the read times out after this many
6333+
seconds. Otherwise a timeout of 600 seconds is used.
63326334
See also |clientserver| |RemoteReply|.
63336335
This function is not available in the |sandbox|.
63346336
{only available when compiled with the |+clientserver| feature}
@@ -6367,9 +6369,10 @@ remote_peek({serverid} [, {retvar}]) *remote_peek()*
63676369
:let repl = ""
63686370
:echo "PEEK: ".remote_peek(id, "repl").": ".repl
63696371
6370-
remote_read({serverid}) *remote_read()*
6372+
remote_read({serverid}, [{timeout}]) *remote_read()*
63716373
Return the oldest available reply from {serverid} and consume
6372-
it. It blocks until a reply is available.
6374+
it. Unless a {timeout} in seconds is given, it blocks until a
6375+
reply is available.
63736376
See also |clientserver|.
63746377
This function is not available in the |sandbox|.
63756378
{only available when compiled with the |+clientserver| feature}

src/evalfunc.c

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -739,10 +739,10 @@ static struct fst
739739
{"reltimefloat", 1, 1, f_reltimefloat},
740740
#endif
741741
{"reltimestr", 1, 1, f_reltimestr},
742-
{"remote_expr", 2, 3, f_remote_expr},
742+
{"remote_expr", 2, 4, f_remote_expr},
743743
{"remote_foreground", 1, 1, f_remote_foreground},
744744
{"remote_peek", 1, 2, f_remote_peek},
745-
{"remote_read", 1, 1, f_remote_read},
745+
{"remote_read", 1, 2, f_remote_read},
746746
{"remote_send", 2, 3, f_remote_send},
747747
{"remote_startserver", 1, 1, f_remote_startserver},
748748
{"remove", 2, 3, f_remove},
@@ -8515,6 +8515,7 @@ remote_common(typval_T *argvars, typval_T *rettv, int expr)
85158515
char_u *keys;
85168516
char_u *r = NULL;
85178517
char_u buf[NUMBUFLEN];
8518+
int timeout = 0;
85188519
# ifdef WIN32
85198520
HWND w;
85208521
# else
@@ -8528,16 +8529,19 @@ remote_common(typval_T *argvars, typval_T *rettv, int expr)
85288529
if (check_connection() == FAIL)
85298530
return;
85308531
# endif
8532+
if (argvars[2].v_type != VAR_UNKNOWN
8533+
&& argvars[3].v_type != VAR_UNKNOWN)
8534+
timeout = get_tv_number(&argvars[3]);
85318535

85328536
server_name = get_tv_string_chk(&argvars[0]);
85338537
if (server_name == NULL)
85348538
return; /* type error; errmsg already given */
85358539
keys = get_tv_string_buf(&argvars[1], buf);
85368540
# ifdef WIN32
8537-
if (serverSendToVim(server_name, keys, &r, &w, expr, TRUE) < 0)
8541+
if (serverSendToVim(server_name, keys, &r, &w, expr, timeout, TRUE) < 0)
85388542
# else
8539-
if (serverSendToVim(X_DISPLAY, server_name, keys, &r, &w, expr, 0, TRUE)
8540-
< 0)
8543+
if (serverSendToVim(X_DISPLAY, server_name, keys, &r, &w, expr, timeout,
8544+
0, TRUE) < 0)
85418545
# endif
85428546
{
85438547
if (r != NULL)
@@ -8555,13 +8559,15 @@ remote_common(typval_T *argvars, typval_T *rettv, int expr)
85558559
char_u str[30];
85568560
char_u *idvar;
85578561

8558-
sprintf((char *)str, PRINTF_HEX_LONG_U, (long_u)w);
8559-
v.di_tv.v_type = VAR_STRING;
8560-
v.di_tv.vval.v_string = vim_strsave(str);
85618562
idvar = get_tv_string_chk(&argvars[2]);
8562-
if (idvar != NULL)
8563+
if (idvar != NULL && *idvar != NUL)
8564+
{
8565+
sprintf((char *)str, PRINTF_HEX_LONG_U, (long_u)w);
8566+
v.di_tv.v_type = VAR_STRING;
8567+
v.di_tv.vval.v_string = vim_strsave(str);
85638568
set_var(idvar, &v.di_tv, FALSE);
8564-
vim_free(v.di_tv.vval.v_string);
8569+
vim_free(v.di_tv.vval.v_string);
8570+
}
85658571
}
85668572
}
85678573
#endif
@@ -8633,7 +8639,7 @@ f_remote_peek(typval_T *argvars UNUSED, typval_T *rettv)
86338639
rettv->vval.v_number = -1;
86348640
else
86358641
{
8636-
s = serverGetReply((HWND)n, FALSE, FALSE, FALSE);
8642+
s = serverGetReply((HWND)n, FALSE, FALSE, FALSE, 0);
86378643
rettv->vval.v_number = (s != NULL);
86388644
}
86398645
# else
@@ -8670,17 +8676,23 @@ f_remote_read(typval_T *argvars UNUSED, typval_T *rettv)
86708676

86718677
if (serverid != NULL && !check_restricted() && !check_secure())
86728678
{
8679+
int timeout = 0;
8680+
8681+
if (argvars[1].v_type != VAR_UNKNOWN)
8682+
timeout = get_tv_number(&argvars[1]);
8683+
86738684
# ifdef WIN32
86748685
/* The server's HWND is encoded in the 'id' parameter */
86758686
long_u n = 0;
86768687

86778688
sscanf((char *)serverid, SCANF_HEX_LONG_U, &n);
86788689
if (n != 0)
8679-
r = serverGetReply((HWND)n, FALSE, TRUE, TRUE);
8690+
r = serverGetReply((HWND)n, FALSE, TRUE, TRUE, timeout);
86808691
if (r == NULL)
86818692
# else
8682-
if (check_connection() == FAIL || serverReadReply(X_DISPLAY,
8683-
serverStrToWin(serverid), &r, FALSE) < 0)
8693+
if (check_connection() == FAIL
8694+
|| serverReadReply(X_DISPLAY, serverStrToWin(serverid),
8695+
&r, FALSE, timeout) < 0)
86848696
# endif
86858697
EMSG(_("E277: Unable to read a server reply"));
86868698
}

src/if_xcmdsrv.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,7 @@ serverSendToVim(
373373
char_u **result, /* Result of eval'ed expression */
374374
Window *server, /* Actual ID of receiving app */
375375
Bool asExpr, /* Interpret as keystrokes or expr ? */
376+
int timeout, /* seconds to wait or zero */
376377
Bool localLoop, /* Throw away everything but result */
377378
int silent) /* don't complain about no server */
378379
{
@@ -485,7 +486,8 @@ serverSendToVim(
485486
pending.nextPtr = pendingCommands;
486487
pendingCommands = &pending;
487488

488-
ServerWait(dpy, w, WaitForPend, &pending, localLoop, 600);
489+
ServerWait(dpy, w, WaitForPend, &pending, localLoop,
490+
timeout > 0 ? timeout : 600);
489491

490492
/*
491493
* Unregister the information about the pending command
@@ -790,6 +792,7 @@ WaitForReply(void *p)
790792

791793
/*
792794
* Wait for replies from id (win)
795+
* When "timeout" is non-zero wait up to this many seconds.
793796
* Return 0 and the malloc'ed string when a reply is available.
794797
* Return -1 if the window becomes invalid while waiting.
795798
*/
@@ -798,13 +801,15 @@ serverReadReply(
798801
Display *dpy,
799802
Window win,
800803
char_u **str,
801-
int localLoop)
804+
int localLoop,
805+
int timeout)
802806
{
803807
int len;
804808
char_u *s;
805809
struct ServerReply *p;
806810

807-
ServerWait(dpy, win, WaitForReply, &win, localLoop, -1);
811+
ServerWait(dpy, win, WaitForReply, &win, localLoop,
812+
timeout > 0 ? timeout : -1);
808813

809814
if ((p = ServerReplyFind(win, SROP_Find)) != NULL && p->strings.ga_len > 0)
810815
{

src/main.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3791,10 +3791,10 @@ cmdsrv_main(
37913791
}
37923792
else
37933793
ret = serverSendToVim(xterm_dpy, sname, *serverStr,
3794-
NULL, &srv, 0, 0, silent);
3794+
NULL, &srv, 0, 0, 0, silent);
37953795
# else
37963796
/* Win32 always works? */
3797-
ret = serverSendToVim(sname, *serverStr, NULL, &srv, 0, silent);
3797+
ret = serverSendToVim(sname, *serverStr, NULL, &srv, 0, 0, silent);
37983798
# endif
37993799
if (ret < 0)
38003800
{
@@ -3854,11 +3854,11 @@ cmdsrv_main(
38543854
while (memchr(done, 0, numFiles) != NULL)
38553855
{
38563856
# ifdef WIN32
3857-
p = serverGetReply(srv, NULL, TRUE, TRUE);
3857+
p = serverGetReply(srv, NULL, TRUE, TRUE, 0);
38583858
if (p == NULL)
38593859
break;
38603860
# else
3861-
if (serverReadReply(xterm_dpy, srv, &p, TRUE) < 0)
3861+
if (serverReadReply(xterm_dpy, srv, &p, TRUE, -1) < 0)
38623862
break;
38633863
# endif
38643864
j = atoi((char *)p);
@@ -3885,12 +3885,12 @@ cmdsrv_main(
38853885
# ifdef WIN32
38863886
/* Win32 always works? */
38873887
if (serverSendToVim(sname, (char_u *)argv[i + 1],
3888-
&res, NULL, 1, FALSE) < 0)
3888+
&res, NULL, 1, 0, FALSE) < 0)
38893889
# else
38903890
if (xterm_dpy == NULL)
38913891
mch_errmsg(_("No display: Send expression failed.\n"));
38923892
else if (serverSendToVim(xterm_dpy, sname, (char_u *)argv[i + 1],
3893-
&res, NULL, 1, 1, FALSE) < 0)
3893+
&res, NULL, 1, 0, 1, FALSE) < 0)
38943894
# endif
38953895
{
38963896
if (res != NULL && *res != NUL)

src/os_mswin.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2401,6 +2401,7 @@ serverSendToVim(
24012401
char_u **result, /* Result of eval'ed expression */
24022402
void *ptarget, /* HWND of server */
24032403
int asExpr, /* Expression or keys? */
2404+
int timeout, /* timeout in seconds or zero */
24042405
int silent) /* don't complain about no server */
24052406
{
24062407
HWND target;
@@ -2444,7 +2445,7 @@ serverSendToVim(
24442445
return -1;
24452446

24462447
if (asExpr)
2447-
retval = serverGetReply(target, &retcode, TRUE, TRUE);
2448+
retval = serverGetReply(target, &retcode, TRUE, TRUE, timeout);
24482449

24492450
if (result == NULL)
24502451
vim_free(retval);
@@ -2521,14 +2522,17 @@ save_reply(HWND server, char_u *reply, int expr)
25212522
* if "wait" is TRUE block until a message arrives (or the server exits).
25222523
*/
25232524
char_u *
2524-
serverGetReply(HWND server, int *expr_res, int remove, int wait)
2525+
serverGetReply(HWND server, int *expr_res, int remove, int wait, int timeout)
25252526
{
25262527
int i;
25272528
char_u *reply;
25282529
reply_T *rep;
25292530
int did_process = FALSE;
2531+
time_t start;
2532+
time_t now;
25302533

25312534
/* When waiting, loop until the message waiting for is received. */
2535+
time(&start);
25322536
for (;;)
25332537
{
25342538
/* Reset this here, in case a message arrives while we are going
@@ -2584,6 +2588,10 @@ serverGetReply(HWND server, int *expr_res, int remove, int wait)
25842588
#ifdef FEAT_TIMERS
25852589
check_due_timer();
25862590
#endif
2591+
time(&now);
2592+
if (timeout > 0 && (now - start) >= timeout)
2593+
break;
2594+
25872595
/* Wait for a SendMessage() call to us. This could be the reply
25882596
* we are waiting for. Use a timeout of a second, to catch the
25892597
* situation that the server died unexpectedly. */

src/proto/if_xcmdsrv.pro

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
/* if_xcmdsrv.c */
22
int serverRegisterName(Display *dpy, char_u *name);
33
void serverChangeRegisteredWindow(Display *dpy, Window newwin);
4-
int serverSendToVim(Display *dpy, char_u *name, char_u *cmd, char_u **result, Window *server, int asExpr, int localLoop, int silent);
4+
int serverSendToVim(Display *dpy, char_u *name, char_u *cmd, char_u **result, Window *server, int asExpr, int timeout, int localLoop, int silent);
55
char_u *serverGetVimNames(Display *dpy);
66
Window serverStrToWin(char_u *str);
77
int serverSendReply(char_u *name, char_u *str);
8-
int serverReadReply(Display *dpy, Window win, char_u **str, int localLoop);
8+
int serverReadReply(Display *dpy, Window win, char_u **str, int localLoop, int timeout);
99
int serverPeekReply(Display *dpy, Window win, char_u **str);
1010
void serverEventProc(Display *dpy, XEvent *eventPtr, int immediate);
1111
void server_parse_messages(void);

src/proto/os_mswin.pro

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ void serverInitMessaging(void);
4343
void serverSetName(char_u *name);
4444
char_u *serverGetVimNames(void);
4545
int serverSendReply(char_u *name, char_u *reply);
46-
int serverSendToVim(char_u *name, char_u *cmd, char_u **result, void *ptarget, int asExpr, int silent);
46+
int serverSendToVim(char_u *name, char_u *cmd, char_u **result, void *ptarget, int asExpr, int timeout, int silent);
4747
void serverForeground(char_u *name);
48-
char_u *serverGetReply(HWND server, int *expr_res, int remove, int wait);
48+
char_u *serverGetReply(HWND server, int *expr_res, int remove, int wait, int timeout);
4949
void serverProcessPendingMessages(void);
5050
char *charset_id2name(int id);
5151
char *quality_id2name(DWORD id);

0 commit comments

Comments
 (0)