aio: introduce AioContext, move bottom halves there
[sdk/emulator/qemu.git] / iohandler.c
1 /*
2  * QEMU System Emulator - managing I/O handler
3  *
4  * Copyright (c) 2003-2008 Fabrice Bellard
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
24
25 #include "config-host.h"
26 #include "qemu-common.h"
27 #include "qemu-char.h"
28 #include "qemu-queue.h"
29 #include "qemu-aio.h"
30 #include "main-loop.h"
31
32 #ifndef _WIN32
33 #include <sys/wait.h>
34 #endif
35
36 typedef struct IOHandlerRecord {
37     IOCanReadHandler *fd_read_poll;
38     IOHandler *fd_read;
39     IOHandler *fd_write;
40     void *opaque;
41     QLIST_ENTRY(IOHandlerRecord) next;
42     int fd;
43     bool deleted;
44 } IOHandlerRecord;
45
46 static QLIST_HEAD(, IOHandlerRecord) io_handlers =
47     QLIST_HEAD_INITIALIZER(io_handlers);
48
49
50 /* XXX: fd_read_poll should be suppressed, but an API change is
51    necessary in the character devices to suppress fd_can_read(). */
52 int qemu_set_fd_handler2(int fd,
53                          IOCanReadHandler *fd_read_poll,
54                          IOHandler *fd_read,
55                          IOHandler *fd_write,
56                          void *opaque)
57 {
58     IOHandlerRecord *ioh;
59
60     assert(fd >= 0);
61
62     if (!fd_read && !fd_write) {
63         QLIST_FOREACH(ioh, &io_handlers, next) {
64             if (ioh->fd == fd) {
65                 ioh->deleted = 1;
66                 break;
67             }
68         }
69     } else {
70         QLIST_FOREACH(ioh, &io_handlers, next) {
71             if (ioh->fd == fd)
72                 goto found;
73         }
74         ioh = g_malloc0(sizeof(IOHandlerRecord));
75         QLIST_INSERT_HEAD(&io_handlers, ioh, next);
76     found:
77         ioh->fd = fd;
78         ioh->fd_read_poll = fd_read_poll;
79         ioh->fd_read = fd_read;
80         ioh->fd_write = fd_write;
81         ioh->opaque = opaque;
82         ioh->deleted = 0;
83         qemu_notify_event();
84     }
85     return 0;
86 }
87
88 int qemu_set_fd_handler(int fd,
89                         IOHandler *fd_read,
90                         IOHandler *fd_write,
91                         void *opaque)
92 {
93     return qemu_set_fd_handler2(fd, NULL, fd_read, fd_write, opaque);
94 }
95
96 void qemu_iohandler_fill(int *pnfds, fd_set *readfds, fd_set *writefds, fd_set *xfds)
97 {
98     IOHandlerRecord *ioh;
99
100     QLIST_FOREACH(ioh, &io_handlers, next) {
101         if (ioh->deleted)
102             continue;
103         if (ioh->fd_read &&
104             (!ioh->fd_read_poll ||
105              ioh->fd_read_poll(ioh->opaque) != 0)) {
106             FD_SET(ioh->fd, readfds);
107             if (ioh->fd > *pnfds)
108                 *pnfds = ioh->fd;
109         }
110         if (ioh->fd_write) {
111             FD_SET(ioh->fd, writefds);
112             if (ioh->fd > *pnfds)
113                 *pnfds = ioh->fd;
114         }
115     }
116 }
117
118 void qemu_iohandler_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds, int ret)
119 {
120     if (ret > 0) {
121         IOHandlerRecord *pioh, *ioh;
122
123         QLIST_FOREACH_SAFE(ioh, &io_handlers, next, pioh) {
124             if (!ioh->deleted && ioh->fd_read && FD_ISSET(ioh->fd, readfds)) {
125                 ioh->fd_read(ioh->opaque);
126             }
127             if (!ioh->deleted && ioh->fd_write && FD_ISSET(ioh->fd, writefds)) {
128                 ioh->fd_write(ioh->opaque);
129             }
130
131             /* Do this last in case read/write handlers marked it for deletion */
132             if (ioh->deleted) {
133                 QLIST_REMOVE(ioh, next);
134                 g_free(ioh);
135             }
136         }
137     }
138 }
139
140 /* reaping of zombies.  right now we're not passing the status to
141    anyone, but it would be possible to add a callback.  */
142 #ifndef _WIN32
143 typedef struct ChildProcessRecord {
144     int pid;
145     QLIST_ENTRY(ChildProcessRecord) next;
146 } ChildProcessRecord;
147
148 static QLIST_HEAD(, ChildProcessRecord) child_watches =
149     QLIST_HEAD_INITIALIZER(child_watches);
150
151 static QEMUBH *sigchld_bh;
152
153 static void sigchld_handler(int signal)
154 {
155     qemu_bh_schedule(sigchld_bh);
156 }
157
158 static void sigchld_bh_handler(void *opaque)
159 {
160     ChildProcessRecord *rec, *next;
161
162     QLIST_FOREACH_SAFE(rec, &child_watches, next, next) {
163         if (waitpid(rec->pid, NULL, WNOHANG) == rec->pid) {
164             QLIST_REMOVE(rec, next);
165             g_free(rec);
166         }
167     }
168 }
169
170 static void qemu_init_child_watch(void)
171 {
172     struct sigaction act;
173     sigchld_bh = qemu_bh_new(sigchld_bh_handler, NULL);
174
175     act.sa_handler = sigchld_handler;
176     act.sa_flags = SA_NOCLDSTOP;
177     sigaction(SIGCHLD, &act, NULL);
178 }
179
180 int qemu_add_child_watch(pid_t pid)
181 {
182     ChildProcessRecord *rec;
183
184     if (!sigchld_bh) {
185         qemu_init_child_watch();
186     }
187
188     QLIST_FOREACH(rec, &child_watches, next) {
189         if (rec->pid == pid) {
190             return 1;
191         }
192     }
193     rec = g_malloc0(sizeof(ChildProcessRecord));
194     rec->pid = pid;
195     QLIST_INSERT_HEAD(&child_watches, rec, next);
196     return 0;
197 }
198 #endif