winhelp: Rewrite internal files lookup using B+ tree search. Add some sanity checks.
[wine] / server / completion.c
1 /*
2  * Server-side IO completion ports implementation
3  *
4  * Copyright (C) 2007 Andrey Turkin
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
22 /* FIXMEs:
23  *  - built-in wait queues used which means:
24  *    + threads are awaken FIFO and not LIFO as native does
25  *    + "max concurrent active threads" parameter not used
26  *    + completion handle is waitable, while native isn't
27  */
28
29 #include "config.h"
30 #include "wine/port.h"
31
32 #include <stdarg.h>
33 #include <stdio.h>
34
35 #include "ntstatus.h"
36 #define WIN32_NO_STATUS
37 #include "windef.h"
38 #include "winternl.h"
39
40 #include "wine/unicode.h"
41 #include "object.h"
42 #include "file.h"
43 #include "handle.h"
44 #include "request.h"
45
46
47 struct completion
48 {
49     struct object  obj;
50     struct list    queue;
51     unsigned int   depth;
52 };
53
54 static void completion_dump( struct object*, int );
55 static void completion_destroy( struct object * );
56 static int  completion_signaled( struct object *obj, struct thread *thread );
57
58 static const struct object_ops completion_ops =
59 {
60     sizeof(struct completion), /* size */
61     completion_dump,           /* dump */
62     add_queue,                 /* add_queue */
63     remove_queue,              /* remove_queue */
64     completion_signaled,       /* signaled */
65     no_satisfied,              /* satisfied */
66     no_signal,                 /* signal */
67     no_get_fd,                 /* get_fd */
68     no_map_access,             /* map_access */
69     default_get_sd,            /* get_sd */
70     default_set_sd,            /* set_sd */
71     no_lookup_name,            /* lookup_name */
72     no_open_file,              /* open_file */
73     no_close_handle,           /* close_handle */
74     completion_destroy         /* destroy */
75 };
76
77 struct comp_msg
78 {
79     struct   list queue_entry;
80     unsigned long ckey;
81     unsigned long cvalue;
82     unsigned long information;
83     unsigned int  status;
84 };
85
86 static void completion_destroy( struct object *obj)
87 {
88     struct completion *completion = (struct completion *) obj;
89     struct comp_msg *tmp, *next;
90
91     LIST_FOR_EACH_ENTRY_SAFE( tmp, next, &completion->queue, struct comp_msg, queue_entry )
92     {
93         free( tmp );
94     }
95 }
96
97 static void completion_dump( struct object *obj, int verbose )
98 {
99     struct completion *completion = (struct completion *) obj;
100
101     assert( obj->ops == &completion_ops );
102     fprintf( stderr, "Completion " );
103     dump_object_name( &completion->obj );
104     fprintf( stderr, " (%u packets pending)\n", completion->depth );
105 }
106
107 static int completion_signaled( struct object *obj, struct thread *thread )
108 {
109     struct completion *completion = (struct completion *)obj;
110
111     return !list_empty( &completion->queue );
112 }
113
114 static struct completion *create_completion( struct directory *root, const struct unicode_str *name, unsigned int attr, unsigned int concurrent )
115 {
116     struct completion *completion;
117
118     if ((completion = create_named_object_dir( root, name, attr, &completion_ops )))
119     {
120         if (get_error() != STATUS_OBJECT_NAME_EXISTS)
121         {
122             list_init( &completion->queue );
123             completion->depth = 0;
124         }
125     }
126
127     return completion;
128 }
129
130 struct completion *get_completion_obj( struct process *process, obj_handle_t handle, unsigned int access )
131 {
132     return (struct completion *) get_handle_obj( process, handle, access, &completion_ops );
133 }
134
135 void add_completion( struct completion *completion, unsigned long ckey, unsigned long cvalue, unsigned int status, unsigned long information )
136 {
137     struct comp_msg *msg = mem_alloc( sizeof( *msg ) );
138
139     if (!msg)
140         return;
141
142     msg->ckey = ckey;
143     msg->cvalue = cvalue;
144     msg->status = status;
145     msg->information = information;
146
147     list_add_tail( &completion->queue, &msg->queue_entry );
148     completion->depth++;
149     wake_up( &completion->obj, 1 );
150 }
151
152 /* create a completion */
153 DECL_HANDLER(create_completion)
154 {
155     struct completion *completion;
156     struct unicode_str name;
157     struct directory *root = NULL;
158
159     reply->handle = 0;
160
161     get_req_unicode_str( &name );
162     if (req->rootdir && !(root = get_directory_obj( current->process, req->rootdir, 0 )))
163         return;
164
165     if ( (completion = create_completion( root, &name, req->attributes, req->concurrent )) != NULL )
166     {
167         reply->handle = alloc_handle( current->process, completion, req->access, req->attributes );
168         release_object( completion );
169     }
170
171     if (root) release_object( root );
172 }
173
174 /* open a completion */
175 DECL_HANDLER(open_completion)
176 {
177     struct completion *completion;
178     struct unicode_str name;
179     struct directory *root = NULL;
180
181     reply->handle = 0;
182
183     get_req_unicode_str( &name );
184     if (req->rootdir && !(root = get_directory_obj( current->process, req->rootdir, 0 )))
185         return;
186
187     if ( (completion = open_object_dir( root, &name, req->attributes, &completion_ops )) != NULL )
188     {
189         reply->handle = alloc_handle( current->process, completion, req->access, req->attributes );
190         release_object( completion );
191     }
192
193     if (root) release_object( root );
194 }
195
196
197 /* add completion to completion port */
198 DECL_HANDLER(add_completion)
199 {
200     struct completion* completion = get_completion_obj( current->process, req->handle, IO_COMPLETION_MODIFY_STATE );
201
202     if (!completion) return;
203
204     add_completion( completion, req->ckey, req->cvalue, req->status, req->information );
205
206     release_object( completion );
207 }
208
209 /* get completion from completion port */
210 DECL_HANDLER(remove_completion)
211 {
212     struct completion* completion = get_completion_obj( current->process, req->handle, IO_COMPLETION_MODIFY_STATE );
213     struct list *entry;
214     struct comp_msg *msg;
215
216     if (!completion) return;
217
218     entry = list_head( &completion->queue );
219     if (!entry)
220         set_error( STATUS_PENDING );
221     else
222     {
223         list_remove( entry );
224         completion->depth--;
225         msg = LIST_ENTRY( entry, struct comp_msg, queue_entry );
226         reply->ckey = msg->ckey;
227         reply->cvalue = msg->cvalue;
228         reply->status = msg->status;
229         reply->information = msg->information;
230         free( msg );
231     }
232
233     release_object( completion );
234 }
235
236 /* get queue depth for completion port */
237 DECL_HANDLER(query_completion)
238 {
239     struct completion* completion = get_completion_obj( current->process, req->handle, IO_COMPLETION_QUERY_STATE );
240
241     if (!completion) return;
242
243     reply->depth = completion->depth;
244
245     release_object( completion );
246 }