# HG changeset patch # User Christian Brabandt # Date 1462809606 -7200 # Node ID d362e6df1debf33db37afce3395a3458859f8bd5 # Parent 8d3354777bf1359b4a8380a81b6079e14bf29b31 commit https://github.com/vim/vim/commit/fb6ffc732e65dbc459c89247ff78134402f1a18b Author: Bram Moolenaar Date: Mon May 9 17:58:04 2016 +0200 patch 7.4.1827 Problem: No error when invoking a callback when it's not safe. Solution: Add an error message. Avoid the error when freeing a channel. diff --git a/src/channel.c b/src/channel.c --- a/src/channel.c +++ b/src/channel.c @@ -59,6 +59,9 @@ static void channel_read(channel_T *chan /* Whether a redraw is needed for appending a line to a buffer. */ static int channel_need_redraw = FALSE; +/* Whether we are inside channel_parse_messages() or another situation where it + * is safe to invoke callbacks. */ +static int safe_to_invoke_callback = 0; #ifdef WIN32 static int @@ -403,8 +406,15 @@ channel_free(channel_T *channel) { if (!in_free_unref_items) { - channel_free_contents(channel); - channel_free_channel(channel); + if (safe_to_invoke_callback == 0) + { + channel->ch_to_be_freed = TRUE; + } + else + { + channel_free_contents(channel); + channel_free_channel(channel); + } } } @@ -444,6 +454,10 @@ free_unused_channels_contents(int copyID int did_free = FALSE; channel_T *ch; + /* This is invoked from the garbage collector, which only runs at a safe + * point. */ + ++safe_to_invoke_callback; + for (ch = first_channel; ch != NULL; ch = ch->ch_next) if (!channel_still_useful(ch) && (ch->ch_copyID & mask) != (copyID & mask)) @@ -453,6 +467,8 @@ free_unused_channels_contents(int copyID channel_free_contents(ch); did_free = TRUE; } + + --safe_to_invoke_callback; return did_free; } @@ -1450,6 +1466,9 @@ invoke_callback(channel_T *channel, char typval_T rettv; int dummy; + if (safe_to_invoke_callback == 0) + EMSG("INTERNAL: Invoking callback when it is not safe"); + argv[0].v_type = VAR_CHANNEL; argv[0].vval.v_channel = channel; @@ -3515,6 +3534,8 @@ channel_parse_messages(void) int r; int part = PART_SOCK; + ++safe_to_invoke_callback; + /* Only do this message when another message was given, otherwise we get * lots of them. */ if (did_log_msg) @@ -3532,6 +3553,13 @@ channel_parse_messages(void) channel = first_channel; continue; } + if (channel->ch_to_be_freed) + { + channel_free(channel); + /* channel has been freed, start over */ + channel = first_channel; + continue; + } if (channel->ch_refcount == 0 && !channel_still_useful(channel)) { /* channel is no longer useful, free it */ @@ -3572,6 +3600,8 @@ channel_parse_messages(void) redraw_after_callback(); } + --safe_to_invoke_callback; + return ret; } diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -1419,6 +1419,8 @@ struct channel_S { int ch_to_be_closed; /* When TRUE reading or writing failed and * the channel must be closed when it's safe * to invoke callbacks. */ + int ch_to_be_freed; /* When TRUE channel must be freed when it's + * safe to invoke callbacks. */ int ch_error; /* When TRUE an error was reported. Avoids * giving pages full of error messages when * the other side has exited, only mention the diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1827, +/**/ 1826, /**/ 1825,