Skip to content

Commit 62769fc

Browse files
committed
Merge remote-tracking branch 'vim/master'
2 parents 3cf3c48 + e081e21 commit 62769fc

File tree

5 files changed

+115
-53
lines changed

5 files changed

+115
-53
lines changed

src/channel.c

Lines changed: 77 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
# define ECONNREFUSED WSAECONNREFUSED
2929
# undef EWOULDBLOCK
3030
# define EWOULDBLOCK WSAEWOULDBLOCK
31+
# undef EINPROGRESS
32+
# define EINPROGRESS WSAEINPROGRESS
3133
# ifdef EINTR
3234
# undef EINTR
3335
# endif
@@ -317,20 +319,47 @@ add_channel(void)
317319
* Called when the refcount of a channel is zero.
318320
* Return TRUE if "channel" has a callback and the associated job wasn't
319321
* killed.
320-
* If the job was killed the channel is not expected to work anymore.
321-
* If there is no callback then nobody can get readahead.
322322
*/
323323
static int
324324
channel_still_useful(channel_T *channel)
325325
{
326+
int has_sock_msg;
327+
#ifdef CHANNEL_PIPES
328+
int has_out_msg;
329+
int has_err_msg;
330+
#endif
331+
332+
/* If the job was killed the channel is not expected to work anymore. */
326333
if (channel->ch_job_killed && channel->ch_job == NULL)
327334
return FALSE;
328-
return channel->ch_callback != NULL
335+
336+
/* If there is a close callback it may still need to be invoked. */
337+
if (channel->ch_close_cb != NULL)
338+
return TRUE;
339+
340+
/* If there is no callback then nobody can get readahead. If the fd is
341+
* closed and there is no readahead then the callback won't be called. */
342+
has_sock_msg = channel->ch_part[PART_SOCK].ch_fd != INVALID_FD
343+
|| channel->ch_part[PART_SOCK].ch_head.rq_next != NULL
344+
|| channel->ch_part[PART_SOCK].ch_json_head.jq_next != NULL;
345+
#ifdef CHANNEL_PIPES
346+
has_out_msg = channel->ch_part[PART_OUT].ch_fd != INVALID_FD
347+
|| channel->ch_part[PART_OUT].ch_head.rq_next != NULL
348+
|| channel->ch_part[PART_OUT].ch_json_head.jq_next != NULL;
349+
has_err_msg = channel->ch_part[PART_ERR].ch_fd != INVALID_FD
350+
|| channel->ch_part[PART_ERR].ch_head.rq_next != NULL
351+
|| channel->ch_part[PART_ERR].ch_json_head.jq_next != NULL;
352+
#endif
353+
return (channel->ch_callback != NULL && (has_sock_msg
329354
#ifdef CHANNEL_PIPES
330-
|| channel->ch_part[PART_OUT].ch_callback != NULL
331-
|| channel->ch_part[PART_ERR].ch_callback != NULL
355+
|| has_out_msg || has_err_msg
332356
#endif
333-
|| channel->ch_close_cb != NULL;
357+
))
358+
#ifdef CHANNEL_PIPES
359+
|| (channel->ch_part[PART_OUT].ch_callback != NULL && has_out_msg)
360+
|| (channel->ch_part[PART_ERR].ch_callback != NULL && has_err_msg)
361+
#endif
362+
;
334363
}
335364

336365
/*
@@ -569,8 +598,6 @@ channel_open(
569598
#else
570599
int port = port_in;
571600
struct timeval start_tv;
572-
int so_error;
573-
socklen_t so_error_len = sizeof(so_error);
574601
#endif
575602
channel_T *channel;
576603
int ret;
@@ -652,7 +679,6 @@ channel_open(
652679
{
653680
if (errno != EWOULDBLOCK
654681
&& errno != ECONNREFUSED
655-
656682
#ifdef EINPROGRESS
657683
&& errno != EINPROGRESS
658684
#endif
@@ -672,14 +698,15 @@ channel_open(
672698
if (waittime >= 0 && ret < 0)
673699
{
674700
struct timeval tv;
701+
fd_set rfds;
675702
fd_set wfds;
676-
#if defined(__APPLE__) && __APPLE__ == 1
677-
# define PASS_RFDS
678-
fd_set rfds;
703+
#ifndef WIN32
704+
int so_error = 0;
705+
socklen_t so_error_len = sizeof(so_error);
706+
#endif
679707

680708
FD_ZERO(&rfds);
681709
FD_SET(sd, &rfds);
682-
#endif
683710
FD_ZERO(&wfds);
684711
FD_SET(sd, &wfds);
685712

@@ -690,13 +717,7 @@ channel_open(
690717
#endif
691718
ch_logn(channel,
692719
"Waiting for connection (waittime %d msec)...", waittime);
693-
ret = select((int)sd + 1,
694-
#ifdef PASS_RFDS
695-
&rfds,
696-
#else
697-
NULL,
698-
#endif
699-
&wfds, NULL, &tv);
720+
ret = select((int)sd + 1, &rfds, &wfds, NULL, &tv);
700721

701722
if (ret < 0)
702723
{
@@ -708,29 +729,42 @@ channel_open(
708729
channel_free(channel);
709730
return NULL;
710731
}
711-
#ifdef PASS_RFDS
712-
if (ret == 0 && FD_ISSET(sd, &rfds) && FD_ISSET(sd, &wfds))
713-
{
714-
/* For OS X, this implies error. See tcp(4). */
715-
ch_error(channel, "channel_open: Connect failed");
716-
EMSG(_(e_cannot_connect));
717-
sock_close(sd);
718-
channel_free(channel);
719-
return NULL;
720-
}
721-
#endif
732+
722733
#ifdef WIN32
723-
/* On Win32 select() is expected to work and wait for up to the
734+
/* On Win32: select() is expected to work and wait for up to the
724735
* waittime for the socket to be open. */
725736
if (!FD_ISSET(sd, &wfds) || ret == 0)
726737
#else
727-
/* See socket(7) for the behavior on Linux-like systems:
738+
/* On Linux-like systems: See socket(7) for the behavior
728739
* After putting the socket in non-blocking mode, connect() will
729740
* return EINPROGRESS, select() will not wait (as if writing is
730741
* possible), need to use getsockopt() to check if the socket is
731-
* actually open. */
732-
getsockopt(sd, SOL_SOCKET, SO_ERROR, &so_error, &so_error_len);
733-
if (!FD_ISSET(sd, &wfds) || ret == 0 || so_error != 0)
742+
* actually connect.
743+
* We detect an failure to connect when both read and write fds
744+
* are set. Use getsockopt() to find out what kind of failure. */
745+
if (FD_ISSET(sd, &rfds) && FD_ISSET(sd, &wfds))
746+
{
747+
ret = getsockopt(sd,
748+
SOL_SOCKET, SO_ERROR, &so_error, &so_error_len);
749+
if (ret < 0 || (so_error != 0
750+
&& so_error != EWOULDBLOCK
751+
&& so_error != ECONNREFUSED
752+
# ifdef EINPROGRESS
753+
&& so_error != EINPROGRESS
754+
# endif
755+
))
756+
{
757+
ch_errorn(channel,
758+
"channel_open: Connect failed with errno %d",
759+
so_error);
760+
PERROR(_(e_cannot_connect));
761+
sock_close(sd);
762+
channel_free(channel);
763+
return NULL;
764+
}
765+
}
766+
767+
if (!FD_ISSET(sd, &wfds) || so_error != 0)
734768
#endif
735769
{
736770
#ifndef WIN32
@@ -1515,7 +1549,7 @@ may_invoke_callback(channel_T *channel, int part)
15151549
{
15161550
if (item->cq_seq_nr == seq_nr)
15171551
{
1518-
ch_logs(channel, "Invoking one-time callback '%s'",
1552+
ch_logs(channel, "Invoking one-time callback %s",
15191553
(char *)item->cq_callback);
15201554
/* Remove the item from the list first, if the callback
15211555
* invokes ch_close() the list will be cleared. */
@@ -1576,7 +1610,7 @@ may_invoke_callback(channel_T *channel, int part)
15761610
if (callback != NULL)
15771611
{
15781612
/* invoke the channel callback */
1579-
ch_log(channel, "Invoking channel callback");
1613+
ch_logs(channel, "Invoking channel callback %s", (char *)callback);
15801614
invoke_callback(channel, callback, argv);
15811615
}
15821616
}
@@ -1776,7 +1810,6 @@ channel_free_all(void)
17761810

17771811
/* Sent when the channel is found closed when reading. */
17781812
#define DETACH_MSG_RAW "DETACH\n"
1779-
#define DETACH_MSG_JSON "\"DETACH\"\n"
17801813

17811814
/* Buffer size for reading incoming messages. */
17821815
#define MAXMSGSIZE 4096
@@ -1872,7 +1905,6 @@ channel_read(channel_T *channel, int part, char *func)
18721905
int readlen = 0;
18731906
sock_T fd;
18741907
int use_socket = FALSE;
1875-
char *msg;
18761908

18771909
fd = channel->ch_part[part].ch_fd;
18781910
if (fd == INVALID_FD)
@@ -1927,11 +1959,12 @@ channel_read(channel_T *channel, int part, char *func)
19271959
* -> ui_breakcheck
19281960
* -> gui event loop or select loop
19291961
* -> channel_read()
1962+
* Don't send "DETACH" for a JS or JSON channel.
19301963
*/
1931-
msg = channel->ch_part[part].ch_mode == MODE_RAW
1932-
|| channel->ch_part[part].ch_mode == MODE_NL
1933-
? DETACH_MSG_RAW : DETACH_MSG_JSON;
1934-
channel_save(channel, part, (char_u *)msg, (int)STRLEN(msg));
1964+
if (channel->ch_part[part].ch_mode == MODE_RAW
1965+
|| channel->ch_part[part].ch_mode == MODE_NL)
1966+
channel_save(channel, part, (char_u *)DETACH_MSG_RAW,
1967+
(int)STRLEN(DETACH_MSG_RAW));
19351968

19361969
/* TODO: When reading from stdout is not possible, should we try to
19371970
* keep stdin and stderr open? Probably not, assume the other side

src/testdir/test87.in

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,8 @@ def ee(expr, g=globals(), l=locals()):
228228
cb.append(expr + ':' + repr((e.__class__, TypeError(msg))))
229229
else:
230230
cb.append(expr + ':' + repr((e.__class__, e)))
231+
elif sys.version_info >= (3, 5) and e.__class__ is ValueError and str(e) == 'embedded null byte':
232+
msg = cb.append(expr + ':' + repr((TypeError, TypeError('expected bytes with no null'))))
231233
else:
232234
cb.append(expr + ':' + repr((e.__class__, e)))
233235
else:
@@ -264,13 +266,17 @@ EOF
264266
:let messages=[]
265267
:delfunction DictNew
266268
py3 <<EOF
269+
import sys
267270
d=vim.bindeval('{}')
268271
m=vim.bindeval('messages')
269272
def em(expr, g=globals(), l=locals()):
270273
try:
271274
exec(expr, g, l)
272275
except Exception as e:
273-
m.extend([e.__class__.__name__])
276+
if sys.version_info >= (3, 5) and e.__class__ is ValueError and str(e) == 'embedded null byte':
277+
m.extend([TypeError.__name__])
278+
else:
279+
m.extend([e.__class__.__name__])
274280

275281
em('d["abc1"]')
276282
em('d["abc1"]="\\0"')

src/testdir/test_channel.vim

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -535,14 +535,21 @@ endfunc
535535

536536
func Test_exit_callback()
537537
if has('job')
538+
call ch_log('Test_exit_callback()')
538539
call s:run_server('s:test_exit_callback')
539540

540-
" the job may take a little while to exit
541-
sleep 50m
541+
" wait up to a second for the job to exit
542+
for i in range(100)
543+
if s:job_exit_ret == 'done'
544+
break
545+
endif
546+
sleep 10m
547+
" calling job_status() triggers the callback
548+
call job_status(s:exit_job)
549+
endfor
542550

543-
" calling job_status() triggers the callback
544-
call job_status(s:exit_job)
545551
call assert_equal('done', s:job_exit_ret)
552+
unlet s:exit_job
546553
endif
547554
endfunc
548555

@@ -571,3 +578,5 @@ func Test_close_callback()
571578
call s:run_server('s:test_close_callback')
572579
endfunc
573580

581+
" Uncomment this to see what happens, output is in src/testdir/channellog.
582+
" call ch_logfile('channellog', 'w')

src/testdir/test_json.vim

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,7 @@ func Test_json_encode()
8282

8383
if has('multi_byte')
8484
call assert_equal(s:jsonmb, json_encode(s:varmb))
85-
call assert_equal(s:varsp1, json_decode(s:jsonsp1))
86-
call assert_equal(s:varsp2, json_decode(s:jsonsp2))
85+
" no test for surrogate pair, json_encode() doesn't create them.
8786
endif
8887

8988
call assert_equal(s:jsonnr, json_encode(s:varnr))
@@ -120,8 +119,8 @@ func Test_json_decode()
120119

121120
if has('multi_byte')
122121
call assert_equal(s:varmb, json_decode(s:jsonmb))
123-
call assert_equal(s:varsp1, js_decode(s:jsonsp1))
124-
call assert_equal(s:varsp2, js_decode(s:jsonsp2))
122+
call assert_equal(s:varsp1, json_decode(s:jsonsp1))
123+
call assert_equal(s:varsp2, json_decode(s:jsonsp2))
125124
endif
126125

127126
call assert_equal(s:varnr, json_decode(s:jsonnr))
@@ -185,6 +184,7 @@ func Test_js_encode()
185184

186185
if has('multi_byte')
187186
call assert_equal(s:jsonmb, js_encode(s:varmb))
187+
" no test for surrogate pair, js_encode() doesn't create them.
188188
endif
189189

190190
call assert_equal(s:jsonnr, js_encode(s:varnr))
@@ -223,6 +223,8 @@ func Test_js_decode()
223223

224224
if has('multi_byte')
225225
call assert_equal(s:varmb, js_decode(s:jsonmb))
226+
call assert_equal(s:varsp1, js_decode(s:jsonsp1))
227+
call assert_equal(s:varsp2, js_decode(s:jsonsp2))
226228
endif
227229

228230
call assert_equal(s:varnr, js_decode(s:jsonnr))

src/version.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,18 @@ static char *(features[]) =
758758

759759
static int included_patches[] =
760760
{ /* Add new patch number below this line */
761+
/**/
762+
1459,
763+
/**/
764+
1458,
765+
/**/
766+
1457,
767+
/**/
768+
1456,
769+
/**/
770+
1455,
771+
/**/
772+
1454,
761773
/**/
762774
1453,
763775
/**/

0 commit comments

Comments
 (0)