server: Make timeout status for async I/O specifiable. Fix mailslots timeout handling.
[wine] / server / async.c
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 */
39     struct list          queue_entry;     /* entry in file descriptor queue */
40     struct timeout_user *timeout;
41     unsigned int         timeout_status;  /* status to report upon timeout */
42     struct event        *event;
43     async_data_t         data;            /* data for async I/O call */
44 };
45
46 static void async_dump( struct object *obj, int verbose );
47 static void async_destroy( struct object *obj );
48
49 static const struct object_ops async_ops =
50 {
51     sizeof(struct async),      /* size */
52     async_dump,                /* dump */
53     no_add_queue,              /* add_queue */
54     NULL,                      /* remove_queue */
55     NULL,                      /* signaled */
56     NULL,                      /* satisfied */
57     no_signal,                 /* signal */
58     no_get_fd,                 /* get_fd */
59     no_map_access,             /* map_access */
60     no_lookup_name,            /* lookup_name */
61     no_open_file,              /* open_file */
62     no_close_handle,           /* close_handle */
63     async_destroy              /* destroy */
64 };
65
66
67 struct async_queue
68 {
69     struct object        obj;             /* object header */
70     struct fd           *fd;              /* file descriptor owning this queue */
71     struct list          queue;           /* queue of async objects */
72 };
73
74 static void async_queue_dump( struct object *obj, int verbose );
75 static void async_queue_destroy( struct object *obj );
76
77 static const struct object_ops async_queue_ops =
78 {
79     sizeof(struct async_queue),      /* size */
80     async_queue_dump,                /* dump */
81     no_add_queue,                    /* add_queue */
82     NULL,                            /* remove_queue */
83     NULL,                            /* signaled */
84     NULL,                            /* satisfied */
85     no_signal,                       /* signal */
86     no_get_fd,                       /* get_fd */
87     no_map_access,                   /* map_access */
88     no_lookup_name,                  /* lookup_name */
89     no_open_file,                    /* open_file */
90     no_close_handle,                 /* close_handle */
91     async_queue_destroy              /* destroy */
92 };
93
94
95 static void async_dump( struct object *obj, int verbose )
96 {
97     struct async *async = (struct async *)obj;
98     assert( obj->ops == &async_ops );
99     fprintf( stderr, "Async thread=%p\n", async->thread );
100 }
101
102 static void async_destroy( struct object *obj )
103 {
104     struct async *async = (struct async *)obj;
105     assert( obj->ops == &async_ops );
106
107     if (async->timeout) remove_timeout_user( async->timeout );
108     if (async->event) release_object( async->event );
109     release_object( async->thread );
110 }
111
112 static void async_queue_dump( struct object *obj, int verbose )
113 {
114     struct async_queue *async_queue = (struct async_queue *)obj;
115     assert( obj->ops == &async_queue_ops );
116     fprintf( stderr, "Async queue fd=%p\n", async_queue->fd );
117 }
118
119 static void async_queue_destroy( struct object *obj )
120 {
121     struct async_queue *async_queue = (struct async_queue *)obj;
122     assert( obj->ops == &async_queue_ops );
123
124     async_wake_up( async_queue, STATUS_HANDLES_CLOSED );
125 }
126
127 /* notifies client thread of new status of its async request */
128 /* destroys the server side of it */
129 static void async_terminate( struct async *async, unsigned int status )
130 {
131     apc_call_t data;
132
133     memset( &data, 0, sizeof(data) );
134     data.type            = APC_ASYNC_IO;
135     data.async_io.func   = async->data.callback;
136     data.async_io.user   = async->data.arg;
137     data.async_io.sb     = async->data.iosb;
138     data.async_io.status = status;
139     thread_queue_apc( async->thread, &async->obj, &data );
140
141     if (async->timeout) remove_timeout_user( async->timeout );
142     async->timeout = NULL;
143     list_remove( &async->queue_entry );
144     release_object( async );
145 }
146
147 /* callback for timeout on an async request */
148 static void async_timeout( void *private )
149 {
150     struct async *async = private;
151
152     async->timeout = NULL;
153     async_terminate( async, async->timeout_status );
154 }
155
156 /* create a new async queue for a given fd */
157 struct async_queue *create_async_queue( struct fd *fd )
158 {
159     struct async_queue *queue = alloc_object( &async_queue_ops );
160
161     if (queue)
162     {
163         queue->fd = fd;
164         list_init( &queue->queue );
165     }
166     return queue;
167 }
168
169 /* create an async on a given queue of a fd */
170 struct async *create_async( struct thread *thread, struct async_queue *queue, const async_data_t *data )
171 {
172     struct event *event = NULL;
173     struct async *async;
174
175     if (data->event && !(event = get_event_obj( thread->process, data->event, EVENT_MODIFY_STATE )))
176         return NULL;
177
178     if (!(async = alloc_object( &async_ops )))
179     {
180         if (event) release_object( event );
181         return NULL;
182     }
183
184     async->thread = (struct thread *)grab_object( thread );
185     async->event = event;
186     async->data = *data;
187     async->timeout = NULL;
188
189     list_add_tail( &queue->queue, &async->queue_entry );
190     grab_object( async );
191
192     if (event) reset_event( event );
193     return async;
194 }
195
196 /* set the timeout of an async operation */
197 void async_set_timeout( struct async *async, const struct timeval *timeout, unsigned int status )
198 {
199     if (async->timeout) remove_timeout_user( async->timeout );
200     if (timeout) async->timeout = add_timeout_user( timeout, async_timeout, async );
201     else async->timeout = NULL;
202     async->timeout_status = status;
203 }
204
205 /* store the result of the client-side async callback */
206 void async_set_result( struct object *obj, unsigned int status )
207 {
208     struct async *async = (struct async *)obj;
209
210     if (obj->ops != &async_ops) return;  /* in case the client messed up the APC results */
211
212     if (status == STATUS_PENDING)
213     {
214         /* FIXME: restart the async operation */
215     }
216     else
217     {
218         if (async->data.apc)
219         {
220             apc_call_t data;
221             data.type         = APC_USER;
222             data.user.func    = async->data.apc;
223             data.user.args[0] = (unsigned long)async->data.apc_arg;
224             data.user.args[1] = (unsigned long)async->data.iosb;
225             data.user.args[2] = 0;
226             thread_queue_apc( async->thread, NULL, &data );
227         }
228         if (async->event) set_event( async->event );
229     }
230 }
231
232 /* check if an async operation is waiting to be alerted */
233 int async_waiting( struct async_queue *queue )
234 {
235     return queue && !list_empty( &queue->queue );
236 }
237
238 /* wake up async operations on the queue */
239 void async_wake_up( struct async_queue *queue, unsigned int status )
240 {
241     struct list *ptr, *next;
242
243     if (!queue) return;
244
245     LIST_FOR_EACH_SAFE( ptr, next, &queue->queue )
246     {
247         struct async *async = LIST_ENTRY( ptr, struct async, queue_entry );
248         async_terminate( async, status );
249         if (status == STATUS_ALERTED) break;  /* only wake up the first one */
250     }
251 }