Skip to content

Commit a07fec9

Browse files
committed
patch 7.4.1262
Problem: The channel callback is not invoked. Solution: Make a list of pending callbacks.
1 parent 4b6a6dc commit a07fec9

File tree

5 files changed

+88
-26
lines changed

5 files changed

+88
-26
lines changed

src/channel.c

Lines changed: 65 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,15 @@ struct jsonqueue
8484
};
8585
typedef struct jsonqueue jsonq_T;
8686

87+
struct cbqueue
88+
{
89+
char_u *callback;
90+
int seq_nr;
91+
struct cbqueue *next;
92+
struct cbqueue *prev;
93+
};
94+
typedef struct cbqueue cbq_T;
95+
8796
typedef struct {
8897
sock_T ch_fd; /* the socket, -1 for a closed channel */
8998
int ch_idx; /* used by channel_poll_setup() */
@@ -106,7 +115,7 @@ typedef struct {
106115
void (*ch_close_cb)(void); /* callback for when channel is closed */
107116

108117
char_u *ch_callback; /* function to call when a msg is not handled */
109-
char_u *ch_req_callback; /* function to call for current request */
118+
cbq_T ch_cb_head; /* dummy node for pre-request callbacks */
110119

111120
int ch_json_mode; /* TRUE for a json channel */
112121
jsonq_T ch_json_head; /* dummy node, header for circular queue */
@@ -168,6 +177,8 @@ add_channel(void)
168177
/* initialize circular queues */
169178
ch->ch_head.next = &ch->ch_head;
170179
ch->ch_head.prev = &ch->ch_head;
180+
ch->ch_cb_head.next = &ch->ch_cb_head;
181+
ch->ch_cb_head.prev = &ch->ch_cb_head;
171182
ch->ch_json_head.next = &ch->ch_json_head;
172183
ch->ch_json_head.prev = &ch->ch_json_head;
173184

@@ -426,15 +437,23 @@ channel_set_callback(int idx, char_u *callback)
426437
}
427438

428439
/*
429-
* Set the callback for channel "idx" for the next response.
440+
* Set the callback for channel "idx" for the response with "id".
430441
*/
431442
void
432-
channel_set_req_callback(int idx, char_u *callback)
443+
channel_set_req_callback(int idx, char_u *callback, int id)
433444
{
434-
/* TODO: make a list of callbacks */
435-
vim_free(channels[idx].ch_req_callback);
436-
channels[idx].ch_req_callback = callback == NULL
437-
? NULL : vim_strsave(callback);
445+
cbq_T *cbhead = &channels[idx].ch_cb_head;
446+
cbq_T *item = (cbq_T *)alloc((int)sizeof(cbq_T));
447+
448+
if (item != NULL)
449+
{
450+
item->callback = vim_strsave(callback);
451+
item->seq_nr = id;
452+
item->prev = cbhead->prev;
453+
cbhead->prev = item;
454+
item->next = cbhead;
455+
item->prev->next = item;
456+
}
438457
}
439458

440459
/*
@@ -597,6 +616,19 @@ channel_parse_json(int ch_idx)
597616
return ret;
598617
}
599618

619+
/*
620+
* Remove "node" from the queue that it is in and free it.
621+
* Also frees the contained callback name.
622+
*/
623+
static void
624+
remove_cb_node(cbq_T *node)
625+
{
626+
node->prev->next = node->next;
627+
node->next->prev = node->prev;
628+
vim_free(node->callback);
629+
vim_free(node);
630+
}
631+
600632
/*
601633
* Remove "node" from the queue that it is in and free it.
602634
* Caller should have freed or used node->value.
@@ -628,8 +660,7 @@ channel_get_json(int ch_idx, int id, typval_T **rettv)
628660
typval_T *tv = &l->lv_first->li_tv;
629661

630662
if ((id > 0 && tv->v_type == VAR_NUMBER && tv->vval.v_number == id)
631-
|| (id <= 0
632-
&& (tv->v_type != VAR_NUMBER || tv->vval.v_number < 0)))
663+
|| id <= 0)
633664
{
634665
*rettv = item->value;
635666
remove_json_node(item);
@@ -742,9 +773,10 @@ may_invoke_callback(int idx)
742773
typval_T *typetv;
743774
typval_T argv[3];
744775
int seq_nr = -1;
745-
int json_mode = channels[idx].ch_json_mode;
776+
channel_T *channel = &channels[idx];
777+
int json_mode = channel->ch_json_mode;
746778

747-
if (channels[idx].ch_close_cb != NULL)
779+
if (channel->ch_close_cb != NULL)
748780
/* this channel is handled elsewhere (netbeans) */
749781
return FALSE;
750782

@@ -804,17 +836,27 @@ may_invoke_callback(int idx)
804836
argv[1].vval.v_string = msg;
805837
}
806838

807-
if (channels[idx].ch_req_callback != NULL && seq_nr != 0)
839+
if (seq_nr > 0)
808840
{
809-
/* TODO: check the sequence number */
810-
/* invoke the one-time callback */
811-
invoke_callback(idx, channels[idx].ch_req_callback, argv);
812-
channels[idx].ch_req_callback = NULL;
841+
cbq_T *cbhead = &channel->ch_cb_head;
842+
cbq_T *cbitem = cbhead->next;
843+
844+
/* invoke the one-time callback with the matching nr */
845+
while (cbitem != cbhead)
846+
{
847+
if (cbitem->seq_nr == seq_nr)
848+
{
849+
invoke_callback(idx, cbitem->callback, argv);
850+
remove_cb_node(cbitem);
851+
break;
852+
}
853+
cbitem = cbitem->next;
854+
}
813855
}
814-
else if (channels[idx].ch_callback != NULL)
856+
else if (channel->ch_callback != NULL)
815857
{
816858
/* invoke the channel callback */
817-
invoke_callback(idx, channels[idx].ch_callback, argv);
859+
invoke_callback(idx, channel->ch_callback, argv);
818860
}
819861
/* else: drop the message TODO: give error */
820862

@@ -844,6 +886,7 @@ channel_close(int idx)
844886
{
845887
channel_T *channel = &channels[idx];
846888
jsonq_T *jhead;
889+
cbq_T *cbhead;
847890

848891
if (channel->ch_fd >= 0)
849892
{
@@ -859,6 +902,10 @@ channel_close(int idx)
859902
while (channel_peek(idx) != NULL)
860903
vim_free(channel_get(idx));
861904

905+
cbhead = &channel->ch_cb_head;
906+
while (cbhead->next != cbhead)
907+
remove_cb_node(cbhead->next);
908+
862909
jhead = &channel->ch_json_head;
863910
while (jhead->next != jhead)
864911
{

src/eval.c

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9800,7 +9800,7 @@ f_ch_open(typval_T *argvars, typval_T *rettv)
98009800
* Otherwise returns -1.
98019801
*/
98029802
static int
9803-
send_common(typval_T *argvars, char_u *text, char *fun)
9803+
send_common(typval_T *argvars, char_u *text, int id, char *fun)
98049804
{
98059805
int ch_idx;
98069806
char_u *callback = NULL;
@@ -9815,10 +9815,10 @@ send_common(typval_T *argvars, char_u *text, char *fun)
98159815
if (callback == NULL)
98169816
return -1;
98179817
}
9818-
/* Set the callback or clear it. An empty callback means no callback and
9819-
* not reading the response. */
9820-
channel_set_req_callback(ch_idx,
9821-
callback != NULL && *callback == NUL ? NULL : callback);
9818+
/* Set the callback. An empty callback means no callback and not reading
9819+
* the response. */
9820+
if (callback != NULL && *callback != NUL)
9821+
channel_set_req_callback(ch_idx, callback, id);
98229822

98239823
if (channel_send(ch_idx, text, fun) == OK && callback == NULL)
98249824
return ch_idx;
@@ -9845,7 +9845,7 @@ f_ch_sendexpr(typval_T *argvars, typval_T *rettv)
98459845
if (text == NULL)
98469846
return;
98479847

9848-
ch_idx = send_common(argvars, text, "sendexpr");
9848+
ch_idx = send_common(argvars, text, id, "sendexpr");
98499849
vim_free(text);
98509850
if (ch_idx >= 0)
98519851
{
@@ -9883,7 +9883,7 @@ f_ch_sendraw(typval_T *argvars, typval_T *rettv)
98839883
rettv->vval.v_string = NULL;
98849884

98859885
text = get_tv_string_buf(&argvars[1], buf);
9886-
ch_idx = send_common(argvars, text, "sendraw");
9886+
ch_idx = send_common(argvars, text, 0, "sendraw");
98879887
if (ch_idx >= 0)
98889888
rettv->vval.v_string = channel_read_block(ch_idx);
98899889
}

src/proto/channel.pro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ void channel_gui_register_all(void);
33
int channel_open(char *hostname, int port_in, void (*close_cb)(void));
44
void channel_set_json_mode(int idx, int json_mode);
55
void channel_set_callback(int idx, char_u *callback);
6-
void channel_set_req_callback(int idx, char_u *callback);
6+
void channel_set_req_callback(int idx, char_u *callback, int id);
77
char_u *channel_get(int idx);
88
int channel_collapse(int idx);
99
int channel_is_open(int idx);

src/testdir/test_channel.vim

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,13 @@ func s:kill_server()
6969
endif
7070
endfunc
7171

72+
let s:responseHandle = -1
73+
let s:responseMsg = ''
74+
func s:RequestHandler(handle, msg)
75+
let s:responseHandle = a:handle
76+
let s:responseMsg = a:msg
77+
endfunc
78+
7279
func Test_communicate()
7380
let handle = s:start_server()
7481
if handle < 0
@@ -86,6 +93,12 @@ func Test_communicate()
8693
call assert_equal('added1', getline(line('$') - 1))
8794
call assert_equal('added2', getline('$'))
8895

96+
" Send a request with a specific handler.
97+
call ch_sendexpr(handle, 'hello!', 's:RequestHandler')
98+
sleep 10m
99+
call assert_equal(handle, s:responseHandle)
100+
call assert_equal('got it', s:responseMsg)
101+
89102
" Send an eval request that works.
90103
call assert_equal('ok', ch_sendexpr(handle, 'eval-works'))
91104
sleep 10m

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,8 @@ static char *(features[]) =
742742

743743
static int included_patches[] =
744744
{ /* Add new patch number below this line */
745+
/**/
746+
1262,
745747
/**/
746748
1261,
747749
/**/

0 commit comments

Comments
 (0)