Paul Jolly wrote:
> > > For example, we might happen to call from govim -> Vim while Vim is in > > > the middle of handling a listener_add callback that itself calls > > > ch_evalexpr which results in a call Vim ->govim. > > > > Can this actually happen? Is this considered a "safe" situation then? > > Yes I think it can. See below for an example. > > > I assume adding a zero tick timer wouldn't help there because vim could > > just schedule the timer callback within the listener callback. > > I think you're right. The timer-based solution was something of a stab > in the dark. However, trying it and this discussion has, I think, > brought me to the "right" answer. > > Consider the following example (requires Go to be installed): > https://gist.github.com/myitcv/8a55f106a604584164d50e82a31d28ef > > vi -u slow_function.vim > > Then: > > :call MyFunc() > > The sequence here (which you can see from Vim screen, > /tmp/vim_channel.log and /tmp/slow_job.log) is as follows: > > * the channel is established between Vim and slow_job.go > * we call MyFunc() > * MyFunc calls ch_evalexpr which is blocking; MyFunc does not, at this > point, return therefore > * slow_job.go receives the message from Vim's ch_evalexpr call: > > {'Comment':'hello from Vim', 'Delay': '10s', 'Calls':[['ex', 'echom > "Hello from govim"']]} > > * slow_job.go does some logging, then it makes the calls requested in > the message > * slow_job.go calls back into Vim with the channel command: ['ex', > 'echom "Hello from govim"'] > * Vim shows this message, but note MyFunc is still blocked on > ch_evalexpr (and hence so is the user) > * slow_job.go then sleeps for the requested 10s > * slow_job.go then returns, which allows ch_evalexpr to return, which > allows MyFunc() to return, at which point the user can continue > > The above example is slightly contrived, because the ex call from > slow_job.go into Vim happens whilst Vim is blocked executing the > remote MyJob() function. > > Things can go wrong when the call from govim (i.e. in place of > slow_job.go) to Vim happens at any random time (and they do happen at > random times). Because sometimes it's the case that Vim is in the > middle of something where we then get a "cannot do this here" error in > response to the call from govim -> Vim. I think the canonical example > here is when Vim is in the middle of a listener_add callback, but a) I I think that is only true if the callback does something involving a blocking wait. Such as that ch_evalexpr(). So the problem is not invoking the listener callback itself, but what happens inside the callback. There are many callbacks these days, thus this can happen in many places and there is not much point for Vim to do something for that. We can document where a blocking wait happens, which is where other callbacks can be invoked, possibly leading to recursive calls. > might be wrong on this and b) there could be other cases. We see the > classic race condition of "govim starts to call Vim just as Vim starts > to call govim" regularly. > > The answer here is I think to keep track on the Vim side (in the govim > VimScript shim) of the number of active ch_evalexpr calls. Instead of > scheduling with a timer_start as I previously suggested, we could do > the following: > > * if a "sensitive" call from govim -> Vim comes in and there are no > active ch_evalexpr calls from Vim -> govim, then handle immediately > * if, however, there are active ch_evalexpr calls, add the request to > the end of a pending queue > * when the number of active ch_evalexpr calls reaches zero, process > the pending queue in order > > We can keep track of all of this from within the govim VimScript shim. > > I think we also have a handle on the calls that are "sensitive". > > Any thoughts on the above analysis/suggestion? I think you are correct that you need to look at places with a blocking wait where a callback would cause trouble. Then adding the event to a queue, instead of doing the work immediately, and check that queue for work after the blocking wait would indeed be the best solution. If this gets too complicated, my suggestion to add an autocommand event when Vim is in a "safe" state would help. E.g. when waiting for the start of a command in Normal mode (without a register, count, pending operator, etc.). Perhaps even when not halfway a mapping, thus actually waiting for they user to type. This would be too long when in Insert mode or editing the command line, thus perhaps we need events for these as well. -- You can tune a file system, but you can't tuna fish -- man tunefs /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org /// -- -- You received this message from the "vim_dev" maillist. Do not top-post! Type your reply below the text you are replying to. For more information, visit http://www.vim.org/maillist.php --- You received this message because you are subscribed to the Google Groups "vim_dev" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To view this discussion on the web visit https://groups.google.com/d/msgid/vim_dev/201909111859.x8BIxXFn029733%40masaka.moolenaar.net.
