add a simple fork test
[qemu/linux-user.git] / posix-aio-compat.c
blob92ec234575b5c89e183caa0259c1f7cd9a30e7a5
1 /*
2 * QEMU posix-aio emulation
4 * Copyright IBM, Corp. 2008
6 * Authors:
7 * Anthony Liguori <aliguori@us.ibm.com>
9 * This work is licensed under the terms of the GNU GPL, version 2. See
10 * the COPYING file in the top-level directory.
14 #include <pthread.h>
15 #include <unistd.h>
16 #include <errno.h>
17 #include <sys/time.h>
18 #include "osdep.h"
20 #include "posix-aio-compat.h"
22 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
23 static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
24 static pthread_t thread_id;
25 static int max_threads = 64;
26 static int cur_threads = 0;
27 static int idle_threads = 0;
28 static TAILQ_HEAD(, qemu_paiocb) request_list;
30 static void *aio_thread(void *unused)
32 sigset_t set;
34 /* block all signals */
35 sigfillset(&set);
36 sigprocmask(SIG_BLOCK, &set, NULL);
38 while (1) {
39 struct qemu_paiocb *aiocb;
40 size_t offset;
41 int ret = 0;
43 pthread_mutex_lock(&lock);
45 while (TAILQ_EMPTY(&request_list) &&
46 !(ret == ETIMEDOUT)) {
47 struct timespec ts = { 0 };
48 qemu_timeval tv;
50 qemu_gettimeofday(&tv);
51 ts.tv_sec = tv.tv_sec + 10;
52 ret = pthread_cond_timedwait(&cond, &lock, &ts);
55 if (ret == ETIMEDOUT)
56 break;
58 aiocb = TAILQ_FIRST(&request_list);
59 TAILQ_REMOVE(&request_list, aiocb, node);
61 offset = 0;
62 aiocb->active = 1;
64 idle_threads--;
65 pthread_mutex_unlock(&lock);
67 while (offset < aiocb->aio_nbytes) {
68 ssize_t len;
70 if (aiocb->is_write)
71 len = pwrite(aiocb->aio_fildes,
72 (const char *)aiocb->aio_buf + offset,
73 aiocb->aio_nbytes - offset,
74 aiocb->aio_offset + offset);
75 else
76 len = pread(aiocb->aio_fildes,
77 (char *)aiocb->aio_buf + offset,
78 aiocb->aio_nbytes - offset,
79 aiocb->aio_offset + offset);
81 if (len == -1 && errno == EINTR)
82 continue;
83 else if (len == -1) {
84 pthread_mutex_lock(&lock);
85 aiocb->ret = -errno;
86 pthread_mutex_unlock(&lock);
87 break;
88 } else if (len == 0)
89 break;
91 offset += len;
93 pthread_mutex_lock(&lock);
94 aiocb->ret = offset;
95 pthread_mutex_unlock(&lock);
98 pthread_mutex_lock(&lock);
99 idle_threads++;
100 pthread_mutex_unlock(&lock);
102 sigqueue(getpid(),
103 aiocb->aio_sigevent.sigev_signo,
104 aiocb->aio_sigevent.sigev_value);
107 idle_threads--;
108 cur_threads--;
109 pthread_mutex_unlock(&lock);
111 return NULL;
114 static int spawn_thread(void)
116 pthread_attr_t attr;
117 int ret;
119 cur_threads++;
120 idle_threads++;
122 pthread_attr_init(&attr);
123 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
124 ret = pthread_create(&thread_id, &attr, aio_thread, NULL);
125 pthread_attr_destroy(&attr);
127 return ret;
130 int qemu_paio_init(struct qemu_paioinit *aioinit)
132 TAILQ_INIT(&request_list);
134 return 0;
137 static int qemu_paio_submit(struct qemu_paiocb *aiocb, int is_write)
139 aiocb->is_write = is_write;
140 aiocb->ret = -EINPROGRESS;
141 aiocb->active = 0;
142 pthread_mutex_lock(&lock);
143 if (idle_threads == 0 && cur_threads < max_threads)
144 spawn_thread();
145 TAILQ_INSERT_TAIL(&request_list, aiocb, node);
146 pthread_mutex_unlock(&lock);
147 pthread_cond_broadcast(&cond);
149 return 0;
152 int qemu_paio_read(struct qemu_paiocb *aiocb)
154 return qemu_paio_submit(aiocb, 0);
157 int qemu_paio_write(struct qemu_paiocb *aiocb)
159 return qemu_paio_submit(aiocb, 1);
162 ssize_t qemu_paio_return(struct qemu_paiocb *aiocb)
164 ssize_t ret;
166 pthread_mutex_lock(&lock);
167 ret = aiocb->ret;
168 pthread_mutex_unlock(&lock);
170 return ret;
173 int qemu_paio_error(struct qemu_paiocb *aiocb)
175 ssize_t ret = qemu_paio_return(aiocb);
177 if (ret < 0)
178 ret = -ret;
179 else
180 ret = 0;
182 return ret;
185 int qemu_paio_cancel(int fd, struct qemu_paiocb *aiocb)
187 int ret;
189 pthread_mutex_lock(&lock);
190 if (!aiocb->active) {
191 TAILQ_REMOVE(&request_list, aiocb, node);
192 aiocb->ret = -ECANCELED;
193 ret = QEMU_PAIO_CANCELED;
194 } else if (aiocb->ret == -EINPROGRESS)
195 ret = QEMU_PAIO_NOTCANCELED;
196 else
197 ret = QEMU_PAIO_ALLDONE;
198 pthread_mutex_unlock(&lock);
200 return ret;