Since then I did some more investigation and it turns out that the channel is indeed read in channel_read(...), but then the blocking select() prevents the incoming message to be processed.
As soon as the blocking select returns (when I press a key), channel_parse_messages(...) is called and parses the buffer read before the select()-call.
I expect that select() should be called with 0 timeout as long as the channel buffers haven't been processed entirely, is that a correct assumption? Should I write an issue in the github tracker?
Regards, Pontus
One further step in the stack trace:
ui_wait_for_chars_or_timer(...), the line "if (wait_func(due_time, interrupted, ignore_input))".
Due_time is -1, interrupted 1 and ignore input 0.
I guess it's the calls to has_pending_job() and channel_any_readahead() that decides if there are more to read? But wouldn't channel_any_readahead() require the message to have been parsed to json before returning TRUE?
An example flow when it works:
Channel_read - 63 bytes
Channel_parse_messages
Channel_parse_json
Channel_parse_json
Channel_parse_json
RealWaitForChar (from mch_breakcheck, with 0 timeout)
Then after a while:
Channel_read - 64 bytes
RealWaitForChar (from mch_breakcheck, with 0 timeout)
RealWaitForChar (from mch_breakcheck, with 0 timeout)
RealWaitForChar (from mch_breakcheck, with 0 timeout)
RealWaitForChar (from mch_breakcheck, with 0 timeout)
RealWaitForChar (from ui_wait_for_chars_or_timer as wait_func.. with -1 timeout)
// The select() call that hangs until keypressed
Channel_parse_messages
Channel_parse_json
Channel_parse_json
Channel_parse_json
@@ -2853,7 +2853,7 @@ channel_has_readahead(channel_T *channel, ch_part_T part)
jsonq_T *head = &channel->ch_part[part].ch_json_head;
jsonq_T *item = head->jq_next;
- return item != NULL;
+ return (item != NULL) || (channel_peek(channel, part) != NULL);
}
return channel_peek(channel, part) != NULL;
}
Yes, maybe there is a channel_parse_json() missing somewhere. But as the flow log shows it's also a channel_parse_messages()-call missing.
> If the JSON is incomplete we need to receive more text to be able to parse it. Thus we might as well block on receiving something.
Thing is, I think that the JSON is actually complete, but since channel_parse_messages() isn't called it never gets the chance to parse the JSON content. That's how I read the logs at least.
> So what is in those 64 bytes? If you look in the channel log it should be there. You don't quote from a channel log, did you use ch_logfile()?
That was just pseudo-logs handwritten :) I ran it again and this time include what is actually read from the channel in channel_read().
Since I do get a few signs placed I can see in the log how it looks like when it works, this is one of the signs that gets placed:
RealWaitForChar() is called with 0 delay
channel_read() is called from RealWaitForChar(), it reads 63 bytes into the buffer
channel_save() is called from channel_read() with the buffer that starts with:
[0,[34,"call","sign_place",0,"govim","govimerr",1,{"lnum":6}]] (and 0x0a at the end)
RealWaitForChar() is called with 0 delay
RealWaitForChar() is called with 0 delay
RealWaitForChar() is called with 0 delay
channel_parse_messages() is called from parse_queued_messages()
channel_peek is called (for part 0)
channel_get_json is called
channel_parse_json is called
channel_peek is called (for part 1)
channel_get_json is called
channel_peek is called (for part 0)
channel_get_json is called
channel_parse_json is called
channel_peek is called (for part 1)
channel_get_json is called
channel_get_json is called
channel_parse_json is called
channel_peek is called (for part 2)
channel_get_json is called
And when it doesn't work it never gets to the channel_parse_messages() call:
RealWaitForChar() is called with 0 delay
channel_read() is called from RealWaitForChar(), it reads 64 bytes into the buffer
channel_save() is called from channel_read() with the buffer that starts with:
[0,[41,"call","sign_place",0,"govim","govimerr",1,{"lnum":12}]] (and 0x0a at the end)
RealWaitForChar() is called with 0 delay
RealWaitForChar() is called with 0 delay
RealWaitForChar() is called with 0 delay
channel_peek is called (part 0, I don't know from where)
channel_peek is called (part 0, I don't know from where)
RealWaitForChar() is called with -1 (as wait_func)
Ok, so I see what you referred to with:
> In inchar_loop() it should call parse_queued_messages() before blocking,
I did run again and logged the inchar_loop() calls, there are only two in the entire run and both with -1 as wtime.
So, we end up inside inchar_loop(), before calling wait_func() with a wait_time value set to -1 (that will block).
And to ensure that parse_queued_messages() are called before sleeping, either has_pending_job() or channel_any_readahead() must return TRUE, right?
That would explain why the patch to channel_any_readahead() works, since it will make it return TRUE?
Or should it be another check to ensure that parse_queued_messages() is called before blocking?
Best regards, Pontus
To convert it to JSON someone must call channel_parse_json(), and it can be either from channel_read_json_block() or may_invoke_callback().
My logs shows that it is via may_invoke_callback() until it hangs.
- may_invoke_callback() is called from channel_parse_messages()
- channel_parse_messages() is called from parse_queued_messages()
- parse_queued_messages() is called from inchar_loop()
But, since channel_parse_json() haven't been called yet for the latest message, inchar_loop() will allow a wait_func()-call with wait_time == -1 (that leads to RealWaitForChar() NULL).
To avoid it, inchar_loop() calls channel_any_readahead(), but it will return FALSE since the messages haven't been parsed to JSON yet and it only checks the JSON queue in JSON mode.
So I guess the solution to this is to either let channel_any_readahead() call channel_peek() in JSON mode as well, or add a call to channel_parse_json() somewhere to ensure that the incoming message has been parsed so that channel_any_readahead() returns TRUE in JSON-mode (?).
I'm sorry if my explanation is messy, it's probably a combination of me not being familiar with the code base as well as not being native English :)
Best regards, Pontus
Works like a charm!
One thing I thought about is the 10ms delay that is added if there is more to read from the channel(?). That might introduce some latency to channel-based plugins that send many small messages.
In my case Paul Jolly's proposal about a sign_setlist (https://github.com/vim/vim/issues/4557) would help, but I guess there are other cases where it might get slow.