changeset 9085:d362e6df1deb v7.4.1827

commit https://github.com/vim/vim/commit/fb6ffc732e65dbc459c89247ff78134402f1a18b Author: Bram Moolenaar <Bram@vim.org> 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.
author Christian Brabandt <cb@256bit.org>
date Mon, 09 May 2016 18:00:06 +0200
parents 8d3354777bf1
children 75f46a491f61
files src/channel.c src/structs.h src/version.c
diffstat 3 files changed, 36 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- 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;
 }
 
--- 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
--- 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,