diff options
author | Bram Moolenaar <Bram@vim.org> | 2016-02-25 23:10:17 +0100 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2016-02-25 23:10:17 +0100 |
commit | c8dcbb12c5d7f3eb0c334daebb4475bb015b91e7 (patch) | |
tree | 1c59c8c123ad290f1753a0b38171fd9ad1fa3ebd /src | |
parent | d2227a02b03708eb0579e17612d5a96262f3d463 (diff) | |
download | vim-git-c8dcbb12c5d7f3eb0c334daebb4475bb015b91e7.tar.gz |
patch 7.4.1421v7.4.1421
Problem: May free a channel when a callback may need to be invoked.
Solution: Keep the channel when refcount is zero.
Diffstat (limited to 'src')
-rw-r--r-- | src/channel.c | 35 | ||||
-rw-r--r-- | src/eval.c | 6 | ||||
-rw-r--r-- | src/proto/channel.pro | 1 | ||||
-rw-r--r-- | src/version.c | 2 |
4 files changed, 40 insertions, 4 deletions
diff --git a/src/channel.c b/src/channel.c index 8044c4d2d..4a2b20c44 100644 --- a/src/channel.c +++ b/src/channel.c @@ -307,6 +307,31 @@ add_channel(void) } /* + * Return TRUE if "channel" has a callback. + */ + static int +channel_has_callback(channel_T *channel) +{ + return channel->ch_callback != NULL +#ifdef CHANNEL_PIPES + || channel->ch_part[PART_OUT].ch_callback != NULL + || channel->ch_part[PART_ERR].ch_callback != NULL +#endif + || channel->ch_close_cb != NULL; +} + +/* + * Close a channel and free all its resources if there is no further action + * possible, there is no callback to be invoked. + */ + void +channel_may_free(channel_T *channel) +{ + if (!channel_has_callback(channel)) + channel_free(channel); +} + +/* * Close a channel and free all its resources. */ void @@ -1463,7 +1488,7 @@ channel_status(channel_T *channel) /* * Close channel "channel". - * This does not trigger the close callback. + * Trigger the close callback if "invoke_close_cb" is TRUE. */ void channel_close(channel_T *channel, int invoke_close_cb) @@ -2149,6 +2174,14 @@ channel_parse_messages(void) while (channel != NULL) { + if (channel->ch_refcount == 0 && !channel_has_callback(channel)) + { + /* channel is no longer useful, free it */ + channel_free(channel); + channel = first_channel; + part = PART_SOCK; + continue; + } if (channel->ch_part[part].ch_fd != INVALID_FD) { /* Increase the refcount, in case the handler causes the channel diff --git a/src/eval.c b/src/eval.c index db9c6b754..0db6cfac5 100644 --- a/src/eval.c +++ b/src/eval.c @@ -7745,8 +7745,8 @@ failret: #if defined(FEAT_CHANNEL) || defined(PROTO) /* - * Decrement the reference count on "channel" and free it when it goes down to - * zero. + * Decrement the reference count on "channel" and maybe free it when it goes + * down to zero. Don't free it if there is a pending action. * Returns TRUE when the channel was freed. */ int @@ -7754,7 +7754,7 @@ channel_unref(channel_T *channel) { if (channel != NULL && --channel->ch_refcount <= 0) { - channel_free(channel); + channel_may_free(channel); return TRUE; } return FALSE; diff --git a/src/proto/channel.pro b/src/proto/channel.pro index 96b51b977..8dc4ad44d 100644 --- a/src/proto/channel.pro +++ b/src/proto/channel.pro @@ -4,6 +4,7 @@ int ch_log_active(void); void ch_log(channel_T *ch, char *msg); void ch_logs(channel_T *ch, char *msg, char *name); channel_T *add_channel(void); +void channel_may_free(channel_T *channel); void channel_free(channel_T *channel); void channel_gui_register(channel_T *channel); void channel_gui_register_all(void); diff --git a/src/version.c b/src/version.c index de20c3e1b..42200012a 100644 --- a/src/version.c +++ b/src/version.c @@ -749,6 +749,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1421, +/**/ 1420, /**/ 1419, |