From fb6ffc732e65dbc459c89247ff78134402f1a18b Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 9 May 2016 17:58:04 +0200 Subject: 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. --- src/channel.c | 34 ++++++++++++++++++++++++++++++++-- src/structs.h | 2 ++ src/version.c | 2 ++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/channel.c b/src/channel.c index 159a3a036..a7f4c4b8c 100644 --- a/src/channel.c +++ b/src/channel.c @@ -59,6 +59,9 @@ static void channel_read(channel_T *channel, int part, char *func); /* 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 mask) 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, int mask) channel_free_contents(ch); did_free = TRUE; } + + --safe_to_invoke_callback; return did_free; } @@ -1450,6 +1466,9 @@ invoke_callback(channel_T *channel, char_u *callback, partial_T *partial, 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 index a2b38bffd..24d819bf5 100644 --- 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 index 373aff2dd..266e59148 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1827, /**/ 1826, /**/ -- cgit v1.2.3