Commit | Line | Data |
---|---|---|
06db7052 AJ |
1 | /* |
2 | * Server-side async I/O support | |
3 | * | |
4 | * Copyright (C) 2007 Alexandre Julliard | |
5 | * | |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2.1 of the License, or (at your option) any later version. | |
10 | * | |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with this library; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | |
19 | */ | |
20 | ||
21 | #include <assert.h> | |
22 | #include <stdio.h> | |
23 | #include <stdlib.h> | |
24 | #include <stdarg.h> | |
25 | ||
26 | #include "ntstatus.h" | |
27 | #define WIN32_NO_STATUS | |
28 | #include "windef.h" | |
29 | #include "winternl.h" | |
30 | ||
31 | #include "object.h" | |
32 | #include "file.h" | |
33 | #include "request.h" | |
34 | ||
35 | struct async | |
36 | { | |
37 | struct object obj; /* object header */ | |
38 | struct thread *thread; /* owning thread */ | |
b2cba95a AJ |
39 | struct list queue_entry; /* entry in async queue list */ |
40 | struct async_queue *queue; /* queue containing this async */ | |
72bff2e4 | 41 | unsigned int status; /* current status */ |
06db7052 | 42 | struct timeout_user *timeout; |
4e5c7038 | 43 | unsigned int timeout_status; /* status to report upon timeout */ |
fa4679fe | 44 | struct event *event; |
3afbee52 AT |
45 | struct completion *completion; |
46 | unsigned long comp_key; | |
111610c4 | 47 | async_data_t data; /* data for async I/O call */ |
06db7052 AJ |
48 | }; |
49 | ||
50 | static void async_dump( struct object *obj, int verbose ); | |
51 | static void async_destroy( struct object *obj ); | |
52 | ||
53 | static const struct object_ops async_ops = | |
54 | { | |
55 | sizeof(struct async), /* size */ | |
56 | async_dump, /* dump */ | |
8382eb01 | 57 | no_get_type, /* get_type */ |
06db7052 AJ |
58 | no_add_queue, /* add_queue */ |
59 | NULL, /* remove_queue */ | |
60 | NULL, /* signaled */ | |
61 | NULL, /* satisfied */ | |
62 | no_signal, /* signal */ | |
63 | no_get_fd, /* get_fd */ | |
64 | no_map_access, /* map_access */ | |
c1707d89 RS |
65 | default_get_sd, /* get_sd */ |
66 | default_set_sd, /* set_sd */ | |
06db7052 | 67 | no_lookup_name, /* lookup_name */ |
7e71c1dd | 68 | no_open_file, /* open_file */ |
06db7052 AJ |
69 | no_close_handle, /* close_handle */ |
70 | async_destroy /* destroy */ | |
71 | }; | |
72 | ||
df09ac51 AJ |
73 | |
74 | struct async_queue | |
75 | { | |
76 | struct object obj; /* object header */ | |
77 | struct fd *fd; /* file descriptor owning this queue */ | |
78 | struct list queue; /* queue of async objects */ | |
79 | }; | |
80 | ||
81 | static void async_queue_dump( struct object *obj, int verbose ); | |
df09ac51 AJ |
82 | |
83 | static const struct object_ops async_queue_ops = | |
84 | { | |
85 | sizeof(struct async_queue), /* size */ | |
86 | async_queue_dump, /* dump */ | |
8382eb01 | 87 | no_get_type, /* get_type */ |
df09ac51 AJ |
88 | no_add_queue, /* add_queue */ |
89 | NULL, /* remove_queue */ | |
90 | NULL, /* signaled */ | |
91 | NULL, /* satisfied */ | |
92 | no_signal, /* signal */ | |
93 | no_get_fd, /* get_fd */ | |
94 | no_map_access, /* map_access */ | |
c1707d89 RS |
95 | default_get_sd, /* get_sd */ |
96 | default_set_sd, /* set_sd */ | |
df09ac51 AJ |
97 | no_lookup_name, /* lookup_name */ |
98 | no_open_file, /* open_file */ | |
99 | no_close_handle, /* close_handle */ | |
b2cba95a | 100 | no_destroy /* destroy */ |
df09ac51 AJ |
101 | }; |
102 | ||
103 | ||
72bff2e4 AJ |
104 | static inline void async_reselect( struct async *async ) |
105 | { | |
106 | if (async->queue->fd) fd_reselect_async( async->queue->fd, async->queue ); | |
107 | } | |
108 | ||
06db7052 AJ |
109 | static void async_dump( struct object *obj, int verbose ) |
110 | { | |
111 | struct async *async = (struct async *)obj; | |
112 | assert( obj->ops == &async_ops ); | |
113 | fprintf( stderr, "Async thread=%p\n", async->thread ); | |
114 | } | |
115 | ||
116 | static void async_destroy( struct object *obj ) | |
117 | { | |
118 | struct async *async = (struct async *)obj; | |
119 | assert( obj->ops == &async_ops ); | |
120 | ||
72bff2e4 AJ |
121 | list_remove( &async->queue_entry ); |
122 | async_reselect( async ); | |
123 | ||
06db7052 | 124 | if (async->timeout) remove_timeout_user( async->timeout ); |
fa4679fe | 125 | if (async->event) release_object( async->event ); |
3afbee52 | 126 | if (async->completion) release_object( async->completion ); |
b2cba95a | 127 | release_object( async->queue ); |
06db7052 AJ |
128 | release_object( async->thread ); |
129 | } | |
130 | ||
df09ac51 AJ |
131 | static void async_queue_dump( struct object *obj, int verbose ) |
132 | { | |
133 | struct async_queue *async_queue = (struct async_queue *)obj; | |
134 | assert( obj->ops == &async_queue_ops ); | |
135 | fprintf( stderr, "Async queue fd=%p\n", async_queue->fd ); | |
136 | } | |
137 | ||
06db7052 | 138 | /* notifies client thread of new status of its async request */ |
61e08b34 | 139 | void async_terminate( struct async *async, unsigned int status ) |
06db7052 AJ |
140 | { |
141 | apc_call_t data; | |
142 | ||
72bff2e4 AJ |
143 | assert( status != STATUS_PENDING ); |
144 | ||
145 | if (async->status != STATUS_PENDING) | |
146 | { | |
147 | /* already terminated, just update status */ | |
148 | async->status = status; | |
149 | return; | |
150 | } | |
151 | ||
06db7052 AJ |
152 | memset( &data, 0, sizeof(data) ); |
153 | data.type = APC_ASYNC_IO; | |
111610c4 AJ |
154 | data.async_io.func = async->data.callback; |
155 | data.async_io.user = async->data.arg; | |
156 | data.async_io.sb = async->data.iosb; | |
06db7052 AJ |
157 | data.async_io.status = status; |
158 | thread_queue_apc( async->thread, &async->obj, &data ); | |
72bff2e4 AJ |
159 | async->status = status; |
160 | async_reselect( async ); | |
161 | release_object( async ); /* so that it gets destroyed when the async is done */ | |
06db7052 AJ |
162 | } |
163 | ||
164 | /* callback for timeout on an async request */ | |
165 | static void async_timeout( void *private ) | |
166 | { | |
167 | struct async *async = private; | |
168 | ||
169 | async->timeout = NULL; | |
4e5c7038 | 170 | async_terminate( async, async->timeout_status ); |
06db7052 AJ |
171 | } |
172 | ||
df09ac51 AJ |
173 | /* create a new async queue for a given fd */ |
174 | struct async_queue *create_async_queue( struct fd *fd ) | |
175 | { | |
176 | struct async_queue *queue = alloc_object( &async_queue_ops ); | |
177 | ||
178 | if (queue) | |
179 | { | |
180 | queue->fd = fd; | |
181 | list_init( &queue->queue ); | |
182 | } | |
183 | return queue; | |
184 | } | |
185 | ||
b2cba95a AJ |
186 | /* free an async queue, cancelling all async operations */ |
187 | void free_async_queue( struct async_queue *queue ) | |
188 | { | |
189 | if (!queue) return; | |
190 | queue->fd = NULL; | |
191 | async_wake_up( queue, STATUS_HANDLES_CLOSED ); | |
192 | release_object( queue ); | |
193 | } | |
194 | ||
06db7052 | 195 | /* create an async on a given queue of a fd */ |
0aae1ca8 | 196 | struct async *create_async( struct thread *thread, struct async_queue *queue, const async_data_t *data ) |
06db7052 | 197 | { |
fa4679fe AJ |
198 | struct event *event = NULL; |
199 | struct async *async; | |
06db7052 | 200 | |
fa4679fe AJ |
201 | if (data->event && !(event = get_event_obj( thread->process, data->event, EVENT_MODIFY_STATE ))) |
202 | return NULL; | |
203 | ||
204 | if (!(async = alloc_object( &async_ops ))) | |
205 | { | |
206 | if (event) release_object( event ); | |
207 | return NULL; | |
208 | } | |
06db7052 | 209 | |
72bff2e4 AJ |
210 | async->thread = (struct thread *)grab_object( thread ); |
211 | async->event = event; | |
212 | async->status = STATUS_PENDING; | |
213 | async->data = *data; | |
0aae1ca8 | 214 | async->timeout = NULL; |
72bff2e4 | 215 | async->queue = (struct async_queue *)grab_object( queue ); |
c69468dd AJ |
216 | async->completion = NULL; |
217 | if (queue->fd) fd_assign_completion( queue->fd, &async->completion, &async->comp_key ); | |
06db7052 | 218 | |
df09ac51 | 219 | list_add_tail( &queue->queue, &async->queue_entry ); |
0aae1ca8 | 220 | grab_object( async ); |
06db7052 | 221 | |
ba896e75 | 222 | if (queue->fd) set_fd_signaled( queue->fd, 0 ); |
fa4679fe | 223 | if (event) reset_event( event ); |
06db7052 AJ |
224 | return async; |
225 | } | |
226 | ||
0aae1ca8 | 227 | /* set the timeout of an async operation */ |
aaf477f2 | 228 | void async_set_timeout( struct async *async, timeout_t timeout, unsigned int status ) |
0aae1ca8 AJ |
229 | { |
230 | if (async->timeout) remove_timeout_user( async->timeout ); | |
aaf477f2 | 231 | if (timeout != TIMEOUT_INFINITE) async->timeout = add_timeout_user( timeout, async_timeout, async ); |
0aae1ca8 | 232 | else async->timeout = NULL; |
4e5c7038 | 233 | async->timeout_status = status; |
0aae1ca8 AJ |
234 | } |
235 | ||
8adce776 | 236 | /* store the result of the client-side async callback */ |
7a9210fa | 237 | void async_set_result( struct object *obj, unsigned int status, unsigned long total ) |
8adce776 AJ |
238 | { |
239 | struct async *async = (struct async *)obj; | |
240 | ||
241 | if (obj->ops != &async_ops) return; /* in case the client messed up the APC results */ | |
242 | ||
72bff2e4 AJ |
243 | assert( async->status != STATUS_PENDING ); /* it must have been woken up if we get a result */ |
244 | ||
245 | if (status == STATUS_PENDING) /* restart it */ | |
8adce776 | 246 | { |
72bff2e4 AJ |
247 | status = async->status; |
248 | async->status = STATUS_PENDING; | |
249 | grab_object( async ); | |
250 | ||
251 | if (status != STATUS_ALERTED) /* it was terminated in the meantime */ | |
252 | async_terminate( async, status ); | |
253 | else | |
254 | async_reselect( async ); | |
8adce776 AJ |
255 | } |
256 | else | |
257 | { | |
72bff2e4 AJ |
258 | if (async->timeout) remove_timeout_user( async->timeout ); |
259 | async->timeout = NULL; | |
260 | async->status = status; | |
3afbee52 AT |
261 | if (async->completion && async->data.cvalue) |
262 | add_completion( async->completion, async->comp_key, async->data.cvalue, status, total ); | |
c16eb8ef AJ |
263 | if (async->data.apc) |
264 | { | |
265 | apc_call_t data; | |
0b0c75ed | 266 | memset( &data, 0, sizeof(data) ); |
c16eb8ef AJ |
267 | data.type = APC_USER; |
268 | data.user.func = async->data.apc; | |
8992f89f | 269 | data.user.args[0] = (unsigned long)async->data.arg; |
c16eb8ef AJ |
270 | data.user.args[1] = (unsigned long)async->data.iosb; |
271 | data.user.args[2] = 0; | |
272 | thread_queue_apc( async->thread, NULL, &data ); | |
273 | } | |
8adce776 | 274 | if (async->event) set_event( async->event ); |
ba896e75 | 275 | else if (async->queue->fd) set_fd_signaled( async->queue->fd, 1 ); |
8adce776 AJ |
276 | } |
277 | } | |
278 | ||
df09ac51 AJ |
279 | /* check if an async operation is waiting to be alerted */ |
280 | int async_waiting( struct async_queue *queue ) | |
06db7052 | 281 | { |
72bff2e4 AJ |
282 | struct list *ptr; |
283 | struct async *async; | |
284 | ||
285 | if (!queue) return 0; | |
286 | if (!(ptr = list_head( &queue->queue ))) return 0; | |
287 | async = LIST_ENTRY( ptr, struct async, queue_entry ); | |
288 | return async->status == STATUS_PENDING; | |
06db7052 AJ |
289 | } |
290 | ||
df09ac51 AJ |
291 | /* wake up async operations on the queue */ |
292 | void async_wake_up( struct async_queue *queue, unsigned int status ) | |
06db7052 AJ |
293 | { |
294 | struct list *ptr, *next; | |
295 | ||
df09ac51 AJ |
296 | if (!queue) return; |
297 | ||
298 | LIST_FOR_EACH_SAFE( ptr, next, &queue->queue ) | |
06db7052 AJ |
299 | { |
300 | struct async *async = LIST_ENTRY( ptr, struct async, queue_entry ); | |
301 | async_terminate( async, status ); | |
df09ac51 | 302 | if (status == STATUS_ALERTED) break; /* only wake up the first one */ |
06db7052 AJ |
303 | } |
304 | } |