static void io_watch_poll_finalize(GSource *source)
{
+ /* Due to a glib bug, removing the last reference to a source
+ * inside a finalize callback causes recursive locking (and a
+ * deadlock). This is not a problem inside other callbacks,
+ * including dispatch callbacks, so we call io_remove_watch_poll
+ * to remove this source. At this point, iwp->src must
+ * be NULL, or we would leak it.
+ *
+ * This would be solved much more elegantly by child sources,
+ * but we support older glib versions that do not have them.
+ */
IOWatchPoll *iwp = io_watch_poll_from_source(source);
- if (iwp->src) {
- g_source_destroy(iwp->src);
- g_source_unref(iwp->src);
- iwp->src = NULL;
- }
+ assert(iwp->src == NULL);
}
static GSourceFuncs io_watch_poll_funcs = {
return tag;
}
+static void io_remove_watch_poll(guint tag)
+{
+ GSource *source;
+ IOWatchPoll *iwp;
+
+ g_return_if_fail (tag > 0);
+
+ source = g_main_context_find_source_by_id(NULL, tag);
+ g_return_if_fail (source != NULL);
+
+ iwp = io_watch_poll_from_source(source);
+ if (iwp->src) {
+ g_source_destroy(iwp->src);
+ g_source_unref(iwp->src);
+ iwp->src = NULL;
+ }
+ g_source_destroy(&iwp->parent);
+}
+
#ifndef _WIN32
static GIOChannel *io_channel_from_fd(int fd)
{
len, &bytes_read, NULL);
if (status == G_IO_STATUS_EOF) {
if (s->fd_in_tag) {
- g_source_remove(s->fd_in_tag);
+ io_remove_watch_poll(s->fd_in_tag);
s->fd_in_tag = 0;
}
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
FDCharDriver *s = chr->opaque;
if (s->fd_in_tag) {
- g_source_remove(s->fd_in_tag);
+ io_remove_watch_poll(s->fd_in_tag);
s->fd_in_tag = 0;
}
FDCharDriver *s = chr->opaque;
if (s->fd_in_tag) {
- g_source_remove(s->fd_in_tag);
+ io_remove_watch_poll(s->fd_in_tag);
s->fd_in_tag = 0;
}
pty_chr_update_read_handler(chr);
out:
+ s->timer_tag = 0;
return FALSE;
}
if (!connected) {
if (s->fd_tag) {
- g_source_remove(s->fd_tag);
+ io_remove_watch_poll(s->fd_tag);
s->fd_tag = 0;
}
s->connected = 0;
int fd;
if (s->fd_tag) {
- g_source_remove(s->fd_tag);
+ io_remove_watch_poll(s->fd_tag);
s->fd_tag = 0;
}
fd = g_io_channel_unix_get_fd(s->fd);
s->bufptr = s->bufcnt;
if (status != G_IO_STATUS_NORMAL) {
if (s->tag) {
- g_source_remove(s->tag);
+ io_remove_watch_poll(s->tag);
s->tag = 0;
}
return FALSE;
NetCharDriver *s = chr->opaque;
if (s->tag) {
- g_source_remove(s->tag);
+ io_remove_watch_poll(s->tag);
s->tag = 0;
}
{
NetCharDriver *s = chr->opaque;
if (s->tag) {
- g_source_remove(s->tag);
+ io_remove_watch_poll(s->tag);
s->tag = 0;
}
if (s->chan) {
s->listen_tag = g_io_add_watch(s->listen_chan, G_IO_IN, tcp_chr_accept, chr);
}
if (s->tag) {
- g_source_remove(s->tag);
+ io_remove_watch_poll(s->tag);
s->tag = 0;
}
g_io_channel_unref(s->chan);
}
fd = qemu_accept(s->listen_fd, addr, &len);
if (fd < 0 && errno != EINTR) {
+ s->listen_tag = 0;
return FALSE;
} else if (fd >= 0) {
if (s->do_telnetopt)
TCPCharDriver *s = chr->opaque;
if (s->fd >= 0) {
if (s->tag) {
- g_source_remove(s->tag);
+ io_remove_watch_poll(s->tag);
s->tag = 0;
}
if (s->chan) {