# HG changeset patch # User Christian Brabandt # Date 1457030705 -3600 # Node ID 3dbe93a240d8ec560f210ba020f197e077801685 # Parent 74475e3eed322ece3267bf81ca9ed1c4fabacf0d commit https://github.com/vim/vim/commit/d6547fc6471d9084f942bdc4ae3aedb39361751d Author: Bram Moolenaar Date: Thu Mar 3 19:35:02 2016 +0100 patch 7.4.1483 Problem: A one-time callback is not used for a raw channel. Solution: Use a one-time callback when it exists. diff --git a/src/channel.c b/src/channel.c --- a/src/channel.c +++ b/src/channel.c @@ -1390,6 +1390,23 @@ channel_exe_cmd(channel_T *channel, int } } + static void +invoke_one_time_callback( + channel_T *channel, + cbq_T *cbhead, + cbq_T *item, + typval_T *argv) +{ + ch_logs(channel, "Invoking one-time callback %s", + (char *)item->cq_callback); + /* Remove the item from the list first, if the callback + * invokes ch_close() the list will be cleared. */ + remove_cb_node(cbhead, item); + invoke_callback(channel, item->cq_callback, argv); + vim_free(item->cq_callback); + vim_free(item); +} + /* * Invoke a callback for "channel"/"part" if needed. * Return TRUE when a message was handled, there might be another one. @@ -1402,6 +1419,8 @@ may_invoke_callback(channel_T *channel, typval_T argv[CH_JSON_MAX_ARGS]; int seq_nr = -1; ch_mode_T ch_mode = channel->ch_part[part].ch_mode; + cbq_T *cbhead = &channel->ch_part[part].ch_cb_head; + cbq_T *cbitem = cbhead->cq_next; char_u *callback = NULL; buf_T *buffer = NULL; @@ -1409,7 +1428,10 @@ may_invoke_callback(channel_T *channel, /* this channel is handled elsewhere (netbeans) */ return FALSE; - if (channel->ch_part[part].ch_callback != NULL) + /* use a message-specific callback, part callback or channel callback */ + if (cbitem != NULL) + callback = cbitem->cq_callback; + else if (channel->ch_part[part].ch_callback != NULL) callback = channel->ch_part[part].ch_callback; else callback = channel->ch_callback; @@ -1525,27 +1547,18 @@ may_invoke_callback(channel_T *channel, if (seq_nr > 0) { - cbq_T *head = &channel->ch_part[part].ch_cb_head; - cbq_T *item = head->cq_next; int done = FALSE; /* invoke the one-time callback with the matching nr */ - while (item != NULL) + while (cbitem != NULL) { - if (item->cq_seq_nr == seq_nr) + if (cbitem->cq_seq_nr == seq_nr) { - ch_logs(channel, "Invoking one-time callback %s", - (char *)item->cq_callback); - /* Remove the item from the list first, if the callback - * invokes ch_close() the list will be cleared. */ - remove_cb_node(head, item); - invoke_callback(channel, item->cq_callback, argv); - vim_free(item->cq_callback); - vim_free(item); + invoke_one_time_callback(channel, cbhead, cbitem, argv); done = TRUE; break; } - item = item->cq_next; + cbitem = cbitem->cq_next; } if (!done) ch_logn(channel, "Dropping message %d without callback", seq_nr); @@ -1599,11 +1612,18 @@ may_invoke_callback(channel_T *channel, } } } + if (callback != NULL) { - /* invoke the channel callback */ - ch_logs(channel, "Invoking channel callback %s", (char *)callback); - invoke_callback(channel, callback, argv); + if (cbitem != NULL) + invoke_one_time_callback(channel, cbhead, cbitem, argv); + else + { + /* invoke the channel callback */ + ch_logs(channel, "Invoking channel callback %s", + (char *)callback); + invoke_callback(channel, callback, argv); + } } } else diff --git a/src/testdir/test_channel.py b/src/testdir/test_channel.py --- a/src/testdir/test_channel.py +++ b/src/testdir/test_channel.py @@ -62,6 +62,9 @@ class ThreadedTCPRequestHandler(socketse if decoded[1] == 'hello!': # simply send back a string response = "got it" + elif decoded[1].startswith("echo "): + # send back the argument + response = decoded[1][5:] elif decoded[1] == 'make change': # Send two ex commands at the same time, before # replying to the request. diff --git a/src/testdir/test_channel.vim b/src/testdir/test_channel.vim --- a/src/testdir/test_channel.vim +++ b/src/testdir/test_channel.vim @@ -257,6 +257,8 @@ func Test_server_crash() call s:run_server('s:server_crash') endfunc +""""""""" + let s:reply = "" func s:Handler(chan, msg) unlet s:reply @@ -290,6 +292,61 @@ func Test_channel_handler() unlet s:chopt.callback endfunc +""""""""" + +let s:reply1 = "" +func s:HandleRaw1(chan, msg) + unlet s:reply1 + let s:reply1 = a:msg +endfunc + +let s:reply2 = "" +func s:HandleRaw2(chan, msg) + unlet s:reply2 + let s:reply2 = a:msg +endfunc + +let s:reply3 = "" +func s:HandleRaw3(chan, msg) + unlet s:reply3 + let s:reply3 = a:msg +endfunc + +func s:raw_one_time_callback(port) + let handle = ch_open('localhost:' . a:port, s:chopt) + if ch_status(handle) == "fail" + call assert_false(1, "Can't open channel") + return + endif + call ch_setoptions(handle, {'mode': 'raw'}) + + " The message are sent raw, we do our own JSON strings here. + call ch_sendraw(handle, "[1, \"hello!\"]", {'callback': 's:HandleRaw1'}) + sleep 10m + call assert_equal("[1, \"got it\"]", s:reply1) + call ch_sendraw(handle, "[2, \"echo something\"]", {'callback': 's:HandleRaw2'}) + call ch_sendraw(handle, "[3, \"wait a bit\"]", {'callback': 's:HandleRaw3'}) + sleep 10m + call assert_equal("[2, \"something\"]", s:reply2) + " wait for up to 500 msec for the 200 msec delayed reply + for i in range(50) + sleep 10m + if s:reply3 != '' + break + endif + endfor + call assert_equal("[3, \"waited\"]", s:reply3) +endfunc + +func Test_raw_one_time_callback() + call ch_logfile('channellog', 'w') + call ch_log('Test_raw_one_time_callback()') + call s:run_server('s:raw_one_time_callback') + call ch_logfile('') +endfunc + +""""""""" + " Test that trying to connect to a non-existing port fails quickly. func Test_connect_waittime() call ch_log('Test_connect_waittime()') @@ -325,6 +382,8 @@ func Test_connect_waittime() endtry endfunc +""""""""" + func Test_raw_pipe() if !has('job') return diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -744,6 +744,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1483, +/**/ 1482, /**/ 1481,