Using error_prepend() in qio_channel_readv_full_all() causes a segfault
as errp is not set when ret is 0. This results in the failure of iotest
83. Replacing with error_setg() fixes the problem.
Additionally, removes a full stop at the end of error message
Reported-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Jagannathan Raman <jag.raman@oracle.com>
Fixes: bebab91ebd
  (io: add qio_channel_readv_full_all_eof & qio_channel_readv_full_all helpers)
Message-Id: <be476bcdb99e820fec0fa09fe8f04c9dd3e62473.1613128220.git.jag.raman@oracle.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: Eric Blake <eblake@redhat.com>
		
	
		
			
				
	
	
		
			624 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			624 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * QEMU I/O channels
 | |
|  *
 | |
|  * Copyright (c) 2015 Red Hat, Inc.
 | |
|  *
 | |
|  * This library is free software; you can redistribute it and/or
 | |
|  * modify it under the terms of the GNU Lesser General Public
 | |
|  * License as published by the Free Software Foundation; either
 | |
|  * version 2.1 of the License, or (at your option) any later version.
 | |
|  *
 | |
|  * This library is distributed in the hope that it will be useful,
 | |
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
|  * Lesser General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU Lesser General Public
 | |
|  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #include "qemu/osdep.h"
 | |
| #include "io/channel.h"
 | |
| #include "qapi/error.h"
 | |
| #include "qemu/main-loop.h"
 | |
| #include "qemu/module.h"
 | |
| #include "qemu/iov.h"
 | |
| 
 | |
| bool qio_channel_has_feature(QIOChannel *ioc,
 | |
|                              QIOChannelFeature feature)
 | |
| {
 | |
|     return ioc->features & (1 << feature);
 | |
| }
 | |
| 
 | |
| 
 | |
| void qio_channel_set_feature(QIOChannel *ioc,
 | |
|                              QIOChannelFeature feature)
 | |
| {
 | |
|     ioc->features |= (1 << feature);
 | |
| }
 | |
| 
 | |
| 
 | |
| void qio_channel_set_name(QIOChannel *ioc,
 | |
|                           const char *name)
 | |
| {
 | |
|     g_free(ioc->name);
 | |
|     ioc->name = g_strdup(name);
 | |
| }
 | |
| 
 | |
| 
 | |
| ssize_t qio_channel_readv_full(QIOChannel *ioc,
 | |
|                                const struct iovec *iov,
 | |
|                                size_t niov,
 | |
|                                int **fds,
 | |
|                                size_t *nfds,
 | |
|                                Error **errp)
 | |
| {
 | |
|     QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
 | |
| 
 | |
|     if ((fds || nfds) &&
 | |
|         !qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_FD_PASS)) {
 | |
|         error_setg_errno(errp, EINVAL,
 | |
|                          "Channel does not support file descriptor passing");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     return klass->io_readv(ioc, iov, niov, fds, nfds, errp);
 | |
| }
 | |
| 
 | |
| 
 | |
| ssize_t qio_channel_writev_full(QIOChannel *ioc,
 | |
|                                 const struct iovec *iov,
 | |
|                                 size_t niov,
 | |
|                                 int *fds,
 | |
|                                 size_t nfds,
 | |
|                                 Error **errp)
 | |
| {
 | |
|     QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
 | |
| 
 | |
|     if ((fds || nfds) &&
 | |
|         !qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_FD_PASS)) {
 | |
|         error_setg_errno(errp, EINVAL,
 | |
|                          "Channel does not support file descriptor passing");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     return klass->io_writev(ioc, iov, niov, fds, nfds, errp);
 | |
| }
 | |
| 
 | |
| 
 | |
| int qio_channel_readv_all_eof(QIOChannel *ioc,
 | |
|                               const struct iovec *iov,
 | |
|                               size_t niov,
 | |
|                               Error **errp)
 | |
| {
 | |
|     return qio_channel_readv_full_all_eof(ioc, iov, niov, NULL, NULL, errp);
 | |
| }
 | |
| 
 | |
| int qio_channel_readv_all(QIOChannel *ioc,
 | |
|                           const struct iovec *iov,
 | |
|                           size_t niov,
 | |
|                           Error **errp)
 | |
| {
 | |
|     return qio_channel_readv_full_all(ioc, iov, niov, NULL, NULL, errp);
 | |
| }
 | |
| 
 | |
| int qio_channel_readv_full_all_eof(QIOChannel *ioc,
 | |
|                                    const struct iovec *iov,
 | |
|                                    size_t niov,
 | |
|                                    int **fds, size_t *nfds,
 | |
|                                    Error **errp)
 | |
| {
 | |
|     int ret = -1;
 | |
|     struct iovec *local_iov = g_new(struct iovec, niov);
 | |
|     struct iovec *local_iov_head = local_iov;
 | |
|     unsigned int nlocal_iov = niov;
 | |
|     int **local_fds = fds;
 | |
|     size_t *local_nfds = nfds;
 | |
|     bool partial = false;
 | |
| 
 | |
|     if (nfds) {
 | |
|         *nfds = 0;
 | |
|     }
 | |
| 
 | |
|     if (fds) {
 | |
|         *fds = NULL;
 | |
|     }
 | |
| 
 | |
|     nlocal_iov = iov_copy(local_iov, nlocal_iov,
 | |
|                           iov, niov,
 | |
|                           0, iov_size(iov, niov));
 | |
| 
 | |
|     while ((nlocal_iov > 0) || local_fds) {
 | |
|         ssize_t len;
 | |
|         len = qio_channel_readv_full(ioc, local_iov, nlocal_iov, local_fds,
 | |
|                                      local_nfds, errp);
 | |
|         if (len == QIO_CHANNEL_ERR_BLOCK) {
 | |
|             if (qemu_in_coroutine()) {
 | |
|                 qio_channel_yield(ioc, G_IO_IN);
 | |
|             } else {
 | |
|                 qio_channel_wait(ioc, G_IO_IN);
 | |
|             }
 | |
|             continue;
 | |
|         }
 | |
| 
 | |
|         if (len == 0) {
 | |
|             if (local_nfds && *local_nfds) {
 | |
|                 /*
 | |
|                  * Got some FDs, but no data yet. This isn't an EOF
 | |
|                  * scenario (yet), so carry on to try to read data
 | |
|                  * on next loop iteration
 | |
|                  */
 | |
|                 goto next_iter;
 | |
|             } else if (!partial) {
 | |
|                 /* No fds and no data - EOF before any data read */
 | |
|                 ret = 0;
 | |
|                 goto cleanup;
 | |
|             } else {
 | |
|                 len = -1;
 | |
|                 error_setg(errp,
 | |
|                            "Unexpected end-of-file before all data were read");
 | |
|                 /* Fallthrough into len < 0 handling */
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if (len < 0) {
 | |
|             /* Close any FDs we previously received */
 | |
|             if (nfds && fds) {
 | |
|                 size_t i;
 | |
|                 for (i = 0; i < (*nfds); i++) {
 | |
|                     close((*fds)[i]);
 | |
|                 }
 | |
|                 g_free(*fds);
 | |
|                 *fds = NULL;
 | |
|                 *nfds = 0;
 | |
|             }
 | |
|             goto cleanup;
 | |
|         }
 | |
| 
 | |
|         if (nlocal_iov) {
 | |
|             iov_discard_front(&local_iov, &nlocal_iov, len);
 | |
|         }
 | |
| 
 | |
| next_iter:
 | |
|         partial = true;
 | |
|         local_fds = NULL;
 | |
|         local_nfds = NULL;
 | |
|     }
 | |
| 
 | |
|     ret = 1;
 | |
| 
 | |
|  cleanup:
 | |
|     g_free(local_iov_head);
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| int qio_channel_readv_full_all(QIOChannel *ioc,
 | |
|                                const struct iovec *iov,
 | |
|                                size_t niov,
 | |
|                                int **fds, size_t *nfds,
 | |
|                                Error **errp)
 | |
| {
 | |
|     int ret = qio_channel_readv_full_all_eof(ioc, iov, niov, fds, nfds, errp);
 | |
| 
 | |
|     if (ret == 0) {
 | |
|         error_setg(errp, "Unexpected end-of-file before all data were read");
 | |
|         return -1;
 | |
|     }
 | |
|     if (ret == 1) {
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| int qio_channel_writev_all(QIOChannel *ioc,
 | |
|                            const struct iovec *iov,
 | |
|                            size_t niov,
 | |
|                            Error **errp)
 | |
| {
 | |
|     return qio_channel_writev_full_all(ioc, iov, niov, NULL, 0, errp);
 | |
| }
 | |
| 
 | |
| int qio_channel_writev_full_all(QIOChannel *ioc,
 | |
|                                 const struct iovec *iov,
 | |
|                                 size_t niov,
 | |
|                                 int *fds, size_t nfds,
 | |
|                                 Error **errp)
 | |
| {
 | |
|     int ret = -1;
 | |
|     struct iovec *local_iov = g_new(struct iovec, niov);
 | |
|     struct iovec *local_iov_head = local_iov;
 | |
|     unsigned int nlocal_iov = niov;
 | |
| 
 | |
|     nlocal_iov = iov_copy(local_iov, nlocal_iov,
 | |
|                           iov, niov,
 | |
|                           0, iov_size(iov, niov));
 | |
| 
 | |
|     while (nlocal_iov > 0) {
 | |
|         ssize_t len;
 | |
|         len = qio_channel_writev_full(ioc, local_iov, nlocal_iov, fds, nfds,
 | |
|                                       errp);
 | |
|         if (len == QIO_CHANNEL_ERR_BLOCK) {
 | |
|             if (qemu_in_coroutine()) {
 | |
|                 qio_channel_yield(ioc, G_IO_OUT);
 | |
|             } else {
 | |
|                 qio_channel_wait(ioc, G_IO_OUT);
 | |
|             }
 | |
|             continue;
 | |
|         }
 | |
|         if (len < 0) {
 | |
|             goto cleanup;
 | |
|         }
 | |
| 
 | |
|         iov_discard_front(&local_iov, &nlocal_iov, len);
 | |
| 
 | |
|         fds = NULL;
 | |
|         nfds = 0;
 | |
|     }
 | |
| 
 | |
|     ret = 0;
 | |
|  cleanup:
 | |
|     g_free(local_iov_head);
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| ssize_t qio_channel_readv(QIOChannel *ioc,
 | |
|                           const struct iovec *iov,
 | |
|                           size_t niov,
 | |
|                           Error **errp)
 | |
| {
 | |
|     return qio_channel_readv_full(ioc, iov, niov, NULL, NULL, errp);
 | |
| }
 | |
| 
 | |
| 
 | |
| ssize_t qio_channel_writev(QIOChannel *ioc,
 | |
|                            const struct iovec *iov,
 | |
|                            size_t niov,
 | |
|                            Error **errp)
 | |
| {
 | |
|     return qio_channel_writev_full(ioc, iov, niov, NULL, 0, errp);
 | |
| }
 | |
| 
 | |
| 
 | |
| ssize_t qio_channel_read(QIOChannel *ioc,
 | |
|                          char *buf,
 | |
|                          size_t buflen,
 | |
|                          Error **errp)
 | |
| {
 | |
|     struct iovec iov = { .iov_base = buf, .iov_len = buflen };
 | |
|     return qio_channel_readv_full(ioc, &iov, 1, NULL, NULL, errp);
 | |
| }
 | |
| 
 | |
| 
 | |
| ssize_t qio_channel_write(QIOChannel *ioc,
 | |
|                           const char *buf,
 | |
|                           size_t buflen,
 | |
|                           Error **errp)
 | |
| {
 | |
|     struct iovec iov = { .iov_base = (char *)buf, .iov_len = buflen };
 | |
|     return qio_channel_writev_full(ioc, &iov, 1, NULL, 0, errp);
 | |
| }
 | |
| 
 | |
| 
 | |
| int qio_channel_read_all_eof(QIOChannel *ioc,
 | |
|                              char *buf,
 | |
|                              size_t buflen,
 | |
|                              Error **errp)
 | |
| {
 | |
|     struct iovec iov = { .iov_base = buf, .iov_len = buflen };
 | |
|     return qio_channel_readv_all_eof(ioc, &iov, 1, errp);
 | |
| }
 | |
| 
 | |
| 
 | |
| int qio_channel_read_all(QIOChannel *ioc,
 | |
|                          char *buf,
 | |
|                          size_t buflen,
 | |
|                          Error **errp)
 | |
| {
 | |
|     struct iovec iov = { .iov_base = buf, .iov_len = buflen };
 | |
|     return qio_channel_readv_all(ioc, &iov, 1, errp);
 | |
| }
 | |
| 
 | |
| 
 | |
| int qio_channel_write_all(QIOChannel *ioc,
 | |
|                           const char *buf,
 | |
|                           size_t buflen,
 | |
|                           Error **errp)
 | |
| {
 | |
|     struct iovec iov = { .iov_base = (char *)buf, .iov_len = buflen };
 | |
|     return qio_channel_writev_all(ioc, &iov, 1, errp);
 | |
| }
 | |
| 
 | |
| 
 | |
| int qio_channel_set_blocking(QIOChannel *ioc,
 | |
|                               bool enabled,
 | |
|                               Error **errp)
 | |
| {
 | |
|     QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
 | |
|     return klass->io_set_blocking(ioc, enabled, errp);
 | |
| }
 | |
| 
 | |
| 
 | |
| int qio_channel_close(QIOChannel *ioc,
 | |
|                       Error **errp)
 | |
| {
 | |
|     QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
 | |
|     return klass->io_close(ioc, errp);
 | |
| }
 | |
| 
 | |
| 
 | |
| GSource *qio_channel_create_watch(QIOChannel *ioc,
 | |
|                                   GIOCondition condition)
 | |
| {
 | |
|     QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
 | |
|     GSource *ret = klass->io_create_watch(ioc, condition);
 | |
| 
 | |
|     if (ioc->name) {
 | |
|         g_source_set_name(ret, ioc->name);
 | |
|     }
 | |
| 
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| 
 | |
| void qio_channel_set_aio_fd_handler(QIOChannel *ioc,
 | |
|                                     AioContext *ctx,
 | |
|                                     IOHandler *io_read,
 | |
|                                     IOHandler *io_write,
 | |
|                                     void *opaque)
 | |
| {
 | |
|     QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
 | |
| 
 | |
|     klass->io_set_aio_fd_handler(ioc, ctx, io_read, io_write, opaque);
 | |
| }
 | |
| 
 | |
| guint qio_channel_add_watch_full(QIOChannel *ioc,
 | |
|                                  GIOCondition condition,
 | |
|                                  QIOChannelFunc func,
 | |
|                                  gpointer user_data,
 | |
|                                  GDestroyNotify notify,
 | |
|                                  GMainContext *context)
 | |
| {
 | |
|     GSource *source;
 | |
|     guint id;
 | |
| 
 | |
|     source = qio_channel_create_watch(ioc, condition);
 | |
| 
 | |
|     g_source_set_callback(source, (GSourceFunc)func, user_data, notify);
 | |
| 
 | |
|     id = g_source_attach(source, context);
 | |
|     g_source_unref(source);
 | |
| 
 | |
|     return id;
 | |
| }
 | |
| 
 | |
| guint qio_channel_add_watch(QIOChannel *ioc,
 | |
|                             GIOCondition condition,
 | |
|                             QIOChannelFunc func,
 | |
|                             gpointer user_data,
 | |
|                             GDestroyNotify notify)
 | |
| {
 | |
|     return qio_channel_add_watch_full(ioc, condition, func,
 | |
|                                       user_data, notify, NULL);
 | |
| }
 | |
| 
 | |
| GSource *qio_channel_add_watch_source(QIOChannel *ioc,
 | |
|                                       GIOCondition condition,
 | |
|                                       QIOChannelFunc func,
 | |
|                                       gpointer user_data,
 | |
|                                       GDestroyNotify notify,
 | |
|                                       GMainContext *context)
 | |
| {
 | |
|     GSource *source;
 | |
|     guint id;
 | |
| 
 | |
|     id = qio_channel_add_watch_full(ioc, condition, func,
 | |
|                                     user_data, notify, context);
 | |
|     source = g_main_context_find_source_by_id(context, id);
 | |
|     g_source_ref(source);
 | |
|     return source;
 | |
| }
 | |
| 
 | |
| 
 | |
| int qio_channel_shutdown(QIOChannel *ioc,
 | |
|                          QIOChannelShutdown how,
 | |
|                          Error **errp)
 | |
| {
 | |
|     QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
 | |
| 
 | |
|     if (!klass->io_shutdown) {
 | |
|         error_setg(errp, "Data path shutdown not supported");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     return klass->io_shutdown(ioc, how, errp);
 | |
| }
 | |
| 
 | |
| 
 | |
| void qio_channel_set_delay(QIOChannel *ioc,
 | |
|                            bool enabled)
 | |
| {
 | |
|     QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
 | |
| 
 | |
|     if (klass->io_set_delay) {
 | |
|         klass->io_set_delay(ioc, enabled);
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| void qio_channel_set_cork(QIOChannel *ioc,
 | |
|                           bool enabled)
 | |
| {
 | |
|     QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
 | |
| 
 | |
|     if (klass->io_set_cork) {
 | |
|         klass->io_set_cork(ioc, enabled);
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| off_t qio_channel_io_seek(QIOChannel *ioc,
 | |
|                           off_t offset,
 | |
|                           int whence,
 | |
|                           Error **errp)
 | |
| {
 | |
|     QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
 | |
| 
 | |
|     if (!klass->io_seek) {
 | |
|         error_setg(errp, "Channel does not support random access");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     return klass->io_seek(ioc, offset, whence, errp);
 | |
| }
 | |
| 
 | |
| 
 | |
| static void qio_channel_restart_read(void *opaque)
 | |
| {
 | |
|     QIOChannel *ioc = opaque;
 | |
|     Coroutine *co = ioc->read_coroutine;
 | |
| 
 | |
|     /* Assert that aio_co_wake() reenters the coroutine directly */
 | |
|     assert(qemu_get_current_aio_context() ==
 | |
|            qemu_coroutine_get_aio_context(co));
 | |
|     aio_co_wake(co);
 | |
| }
 | |
| 
 | |
| static void qio_channel_restart_write(void *opaque)
 | |
| {
 | |
|     QIOChannel *ioc = opaque;
 | |
|     Coroutine *co = ioc->write_coroutine;
 | |
| 
 | |
|     /* Assert that aio_co_wake() reenters the coroutine directly */
 | |
|     assert(qemu_get_current_aio_context() ==
 | |
|            qemu_coroutine_get_aio_context(co));
 | |
|     aio_co_wake(co);
 | |
| }
 | |
| 
 | |
| static void qio_channel_set_aio_fd_handlers(QIOChannel *ioc)
 | |
| {
 | |
|     IOHandler *rd_handler = NULL, *wr_handler = NULL;
 | |
|     AioContext *ctx;
 | |
| 
 | |
|     if (ioc->read_coroutine) {
 | |
|         rd_handler = qio_channel_restart_read;
 | |
|     }
 | |
|     if (ioc->write_coroutine) {
 | |
|         wr_handler = qio_channel_restart_write;
 | |
|     }
 | |
| 
 | |
|     ctx = ioc->ctx ? ioc->ctx : iohandler_get_aio_context();
 | |
|     qio_channel_set_aio_fd_handler(ioc, ctx, rd_handler, wr_handler, ioc);
 | |
| }
 | |
| 
 | |
| void qio_channel_attach_aio_context(QIOChannel *ioc,
 | |
|                                     AioContext *ctx)
 | |
| {
 | |
|     assert(!ioc->read_coroutine);
 | |
|     assert(!ioc->write_coroutine);
 | |
|     ioc->ctx = ctx;
 | |
| }
 | |
| 
 | |
| void qio_channel_detach_aio_context(QIOChannel *ioc)
 | |
| {
 | |
|     ioc->read_coroutine = NULL;
 | |
|     ioc->write_coroutine = NULL;
 | |
|     qio_channel_set_aio_fd_handlers(ioc);
 | |
|     ioc->ctx = NULL;
 | |
| }
 | |
| 
 | |
| void coroutine_fn qio_channel_yield(QIOChannel *ioc,
 | |
|                                     GIOCondition condition)
 | |
| {
 | |
|     assert(qemu_in_coroutine());
 | |
|     if (condition == G_IO_IN) {
 | |
|         assert(!ioc->read_coroutine);
 | |
|         ioc->read_coroutine = qemu_coroutine_self();
 | |
|     } else if (condition == G_IO_OUT) {
 | |
|         assert(!ioc->write_coroutine);
 | |
|         ioc->write_coroutine = qemu_coroutine_self();
 | |
|     } else {
 | |
|         abort();
 | |
|     }
 | |
|     qio_channel_set_aio_fd_handlers(ioc);
 | |
|     qemu_coroutine_yield();
 | |
| 
 | |
|     /* Allow interrupting the operation by reentering the coroutine other than
 | |
|      * through the aio_fd_handlers. */
 | |
|     if (condition == G_IO_IN && ioc->read_coroutine) {
 | |
|         ioc->read_coroutine = NULL;
 | |
|         qio_channel_set_aio_fd_handlers(ioc);
 | |
|     } else if (condition == G_IO_OUT && ioc->write_coroutine) {
 | |
|         ioc->write_coroutine = NULL;
 | |
|         qio_channel_set_aio_fd_handlers(ioc);
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| static gboolean qio_channel_wait_complete(QIOChannel *ioc,
 | |
|                                           GIOCondition condition,
 | |
|                                           gpointer opaque)
 | |
| {
 | |
|     GMainLoop *loop = opaque;
 | |
| 
 | |
|     g_main_loop_quit(loop);
 | |
|     return FALSE;
 | |
| }
 | |
| 
 | |
| 
 | |
| void qio_channel_wait(QIOChannel *ioc,
 | |
|                       GIOCondition condition)
 | |
| {
 | |
|     GMainContext *ctxt = g_main_context_new();
 | |
|     GMainLoop *loop = g_main_loop_new(ctxt, TRUE);
 | |
|     GSource *source;
 | |
| 
 | |
|     source = qio_channel_create_watch(ioc, condition);
 | |
| 
 | |
|     g_source_set_callback(source,
 | |
|                           (GSourceFunc)qio_channel_wait_complete,
 | |
|                           loop,
 | |
|                           NULL);
 | |
| 
 | |
|     g_source_attach(source, ctxt);
 | |
| 
 | |
|     g_main_loop_run(loop);
 | |
| 
 | |
|     g_source_unref(source);
 | |
|     g_main_loop_unref(loop);
 | |
|     g_main_context_unref(ctxt);
 | |
| }
 | |
| 
 | |
| 
 | |
| static void qio_channel_finalize(Object *obj)
 | |
| {
 | |
|     QIOChannel *ioc = QIO_CHANNEL(obj);
 | |
| 
 | |
|     g_free(ioc->name);
 | |
| 
 | |
| #ifdef _WIN32
 | |
|     if (ioc->event) {
 | |
|         CloseHandle(ioc->event);
 | |
|     }
 | |
| #endif
 | |
| }
 | |
| 
 | |
| static const TypeInfo qio_channel_info = {
 | |
|     .parent = TYPE_OBJECT,
 | |
|     .name = TYPE_QIO_CHANNEL,
 | |
|     .instance_size = sizeof(QIOChannel),
 | |
|     .instance_finalize = qio_channel_finalize,
 | |
|     .abstract = true,
 | |
|     .class_size = sizeof(QIOChannelClass),
 | |
| };
 | |
| 
 | |
| 
 | |
| static void qio_channel_register_types(void)
 | |
| {
 | |
|     type_register_static(&qio_channel_info);
 | |
| }
 | |
| 
 | |
| 
 | |
| type_init(qio_channel_register_types);
 |