summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2016-05-09 17:58:04 +0200
committerBram Moolenaar <Bram@vim.org>2016-05-09 17:58:04 +0200
commitfb6ffc732e65dbc459c89247ff78134402f1a18b (patch)
tree4c29f995f7ea77f4426e057fa2bf9e5b6eea0cb8
parentcf7ff70ca73218d618e7c00ab785bcf5f9120a94 (diff)
patch 7.4.1827v7.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.
-rw-r--r--src/channel.c34
-rw-r--r--src/structs.h2
-rw-r--r--src/version.c2
3 files changed, 36 insertions, 2 deletions
diff --git a/src/channel.c b/src/channel.c
index 159a3a0368..a7f4c4b8cb 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 a2b38bffd8..24d819bf5d 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 373aff2dd0..266e591488 100644
--- 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,