Commit | Line | Data |
---|---|---|
642d3136 AJ |
1 | /* |
2 | * Server-side thread management | |
3 | * | |
4 | * Copyright (C) 1998 Alexandre Julliard | |
0799c1a7 AJ |
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
642d3136 AJ |
19 | */ |
20 | ||
13277480 | 21 | #include "config.h" |
5769d1de | 22 | #include "wine/port.h" |
13277480 | 23 | |
642d3136 | 24 | #include <assert.h> |
e5dedb19 | 25 | #include <errno.h> |
642d3136 | 26 | #include <fcntl.h> |
767e6f6f | 27 | #include <signal.h> |
ea1afcef | 28 | #include <stdarg.h> |
642d3136 AJ |
29 | #include <stdio.h> |
30 | #include <stdlib.h> | |
31 | #include <string.h> | |
85ed45e3 | 32 | #include <sys/types.h> |
642d3136 | 33 | #include <unistd.h> |
24f4eced | 34 | #include <time.h> |
57279184 SE |
35 | #ifdef HAVE_POLL_H |
36 | #include <poll.h> | |
37 | #endif | |
642d3136 | 38 | |
1a1583a3 GG |
39 | #include "ntstatus.h" |
40 | #define WIN32_NO_STATUS | |
e37c6e18 | 41 | #include "windef.h" |
1a1583a3 | 42 | #include "winternl.h" |
43c190e7 | 43 | |
863637b1 | 44 | #include "file.h" |
43c190e7 | 45 | #include "handle.h" |
43c190e7 AJ |
46 | #include "process.h" |
47 | #include "thread.h" | |
5bc78089 | 48 | #include "request.h" |
1a66d226 | 49 | #include "user.h" |
4bba2164 | 50 | #include "security.h" |
642d3136 AJ |
51 | |
52 | ||
85ed45e3 AJ |
53 | /* thread queues */ |
54 | ||
85ed45e3 AJ |
55 | struct thread_wait |
56 | { | |
e5dedb19 AJ |
57 | struct thread_wait *next; /* next wait structure for this thread */ |
58 | struct thread *thread; /* owner thread */ | |
85ed45e3 AJ |
59 | int count; /* count of objects */ |
60 | int flags; | |
e5dedb19 | 61 | void *cookie; /* magic cookie to return to client */ |
85ed45e3 | 62 | struct timeval timeout; |
57e11313 | 63 | struct timeout_user *user; |
85ed45e3 AJ |
64 | struct wait_queue_entry queues[1]; |
65 | }; | |
66 | ||
62a8b433 AJ |
67 | /* asynchronous procedure calls */ |
68 | ||
69 | struct thread_apc | |
70 | { | |
81b6a1fb | 71 | struct list entry; /* queue linked list */ |
ea1afcef AJ |
72 | struct object *owner; /* object that queued this apc */ |
73 | void *func; /* function to call in client */ | |
74 | enum apc_type type; /* type of apc function */ | |
75 | int nb_args; /* number of arguments */ | |
088bcf9c AJ |
76 | void *arg1; /* function arguments */ |
77 | void *arg2; | |
78 | void *arg3; | |
62a8b433 | 79 | }; |
62a8b433 | 80 | |
85ed45e3 | 81 | |
642d3136 AJ |
82 | /* thread operations */ |
83 | ||
767e6f6f | 84 | static void dump_thread( struct object *obj, int verbose ); |
85ed45e3 | 85 | static int thread_signaled( struct object *obj, struct thread *thread ); |
46d1b3e8 | 86 | static unsigned int thread_map_access( struct object *obj, unsigned int access ); |
cf27a7fa | 87 | static void thread_poll_event( struct fd *fd, int event ); |
642d3136 | 88 | static void destroy_thread( struct object *obj ); |
2362380b | 89 | static struct thread_apc *thread_dequeue_apc( struct thread *thread, int system_only ); |
642d3136 AJ |
90 | |
91 | static const struct object_ops thread_ops = | |
92 | { | |
1dca5e24 AJ |
93 | sizeof(struct thread), /* size */ |
94 | dump_thread, /* dump */ | |
95 | add_queue, /* add_queue */ | |
96 | remove_queue, /* remove_queue */ | |
97 | thread_signaled, /* signaled */ | |
98 | no_satisfied, /* satisfied */ | |
f92fff66 | 99 | no_signal, /* signal */ |
863637b1 | 100 | no_get_fd, /* get_fd */ |
46d1b3e8 | 101 | thread_map_access, /* map_access */ |
baffcb95 | 102 | no_lookup_name, /* lookup_name */ |
b9b1ea9c | 103 | no_close_handle, /* close_handle */ |
863637b1 AJ |
104 | destroy_thread /* destroy */ |
105 | }; | |
106 | ||
107 | static const struct fd_ops thread_fd_ops = | |
108 | { | |
1dca5e24 AJ |
109 | NULL, /* get_poll_events */ |
110 | thread_poll_event, /* poll_event */ | |
1dca5e24 AJ |
111 | no_flush, /* flush */ |
112 | no_get_file_info, /* get_file_info */ | |
4634447d EP |
113 | no_queue_async, /* queue_async */ |
114 | no_cancel_async /* cancel_async */ | |
642d3136 AJ |
115 | }; |
116 | ||
73209eb7 | 117 | static struct list thread_list = LIST_INIT(thread_list); |
767e6f6f | 118 | |
f5242405 AJ |
119 | /* initialize the structure for a newly allocated thread */ |
120 | inline static void init_thread_structure( struct thread *thread ) | |
121 | { | |
122 | int i; | |
123 | ||
02a53c17 | 124 | thread->unix_pid = -1; /* not known yet */ |
a8497bd0 | 125 | thread->unix_tid = -1; /* not known yet */ |
f5242405 | 126 | thread->context = NULL; |
73c72390 | 127 | thread->suspend_context = NULL; |
f5242405 | 128 | thread->teb = NULL; |
f5242405 AJ |
129 | thread->debug_ctx = NULL; |
130 | thread->debug_event = NULL; | |
131 | thread->queue = NULL; | |
f5242405 | 132 | thread->wait = NULL; |
f5242405 | 133 | thread->error = 0; |
9caa71ee AJ |
134 | thread->req_data = NULL; |
135 | thread->req_toread = 0; | |
136 | thread->reply_data = NULL; | |
137 | thread->reply_towrite = 0; | |
e66207eb AJ |
138 | thread->request_fd = NULL; |
139 | thread->reply_fd = NULL; | |
140 | thread->wait_fd = NULL; | |
f5242405 AJ |
141 | thread->state = RUNNING; |
142 | thread->attached = 0; | |
143 | thread->exit_code = 0; | |
f5242405 AJ |
144 | thread->priority = THREAD_PRIORITY_NORMAL; |
145 | thread->affinity = 1; | |
146 | thread->suspend = 0; | |
24f4eced RC |
147 | thread->creation_time = time(NULL); |
148 | thread->exit_time = 0; | |
92fec7b6 | 149 | thread->desktop_users = 0; |
f5242405 | 150 | |
20894e2f | 151 | list_init( &thread->mutex_list ); |
81b6a1fb AJ |
152 | list_init( &thread->system_apc ); |
153 | list_init( &thread->user_apc ); | |
154 | ||
f5242405 AJ |
155 | for (i = 0; i < MAX_INFLIGHT_FDS; i++) |
156 | thread->inflight[i].server = thread->inflight[i].client = -1; | |
157 | } | |
158 | ||
0424f381 AJ |
159 | /* check if address looks valid for a client-side data structure (TEB etc.) */ |
160 | static inline int is_valid_address( void *addr ) | |
161 | { | |
162 | return addr && !((unsigned int)addr % sizeof(int)); | |
163 | } | |
164 | ||
5bc78089 | 165 | /* create a new thread */ |
5b4f3e8d | 166 | struct thread *create_thread( int fd, struct process *process ) |
5bc78089 AJ |
167 | { |
168 | struct thread *thread; | |
5bc78089 | 169 | |
e66207eb | 170 | if (!(thread = alloc_object( &thread_ops ))) return NULL; |
5bc78089 | 171 | |
f5242405 | 172 | init_thread_structure( thread ); |
f692d446 | 173 | |
f5242405 | 174 | thread->process = (struct process *)grab_object( process ); |
78a3e633 | 175 | thread->desktop = process->desktop; |
2fe57779 AJ |
176 | if (!current) current = thread; |
177 | ||
73209eb7 | 178 | list_add_head( &thread_list, &thread->entry ); |
767e6f6f | 179 | |
91befe1d AJ |
180 | if (!(thread->id = alloc_ptid( thread ))) |
181 | { | |
182 | release_object( thread ); | |
183 | return NULL; | |
184 | } | |
580da246 | 185 | if (!(thread->request_fd = create_anonymous_fd( &thread_fd_ops, fd, &thread->obj ))) |
e66207eb AJ |
186 | { |
187 | release_object( thread ); | |
188 | return NULL; | |
189 | } | |
91befe1d | 190 | |
36cd6f5d MM |
191 | thread->token = (struct token *) grab_object( process->token ); |
192 | ||
e66207eb | 193 | set_fd_events( thread->request_fd, POLLIN ); /* start listening to events */ |
8859d772 | 194 | add_process_thread( thread->process, thread ); |
f692d446 | 195 | return thread; |
642d3136 AJ |
196 | } |
197 | ||
1dca5e24 | 198 | /* handle a client event */ |
cf27a7fa | 199 | static void thread_poll_event( struct fd *fd, int event ) |
1dca5e24 | 200 | { |
cf27a7fa AJ |
201 | struct thread *thread = get_fd_user( fd ); |
202 | assert( thread->obj.ops == &thread_ops ); | |
1dca5e24 | 203 | |
12f29b50 | 204 | if (event & (POLLERR | POLLHUP)) kill_thread( thread, 0 ); |
d90e964c | 205 | else if (event & POLLIN) read_request( thread ); |
9caa71ee | 206 | else if (event & POLLOUT) write_reply( thread ); |
f5242405 AJ |
207 | } |
208 | ||
209 | /* cleanup everything that is no longer needed by a dead thread */ | |
210 | /* used by destroy_thread and kill_thread */ | |
211 | static void cleanup_thread( struct thread *thread ) | |
212 | { | |
213 | int i; | |
214 | struct thread_apc *apc; | |
215 | ||
216 | while ((apc = thread_dequeue_apc( thread, 0 ))) free( apc ); | |
9caa71ee AJ |
217 | if (thread->req_data) free( thread->req_data ); |
218 | if (thread->reply_data) free( thread->reply_data ); | |
e66207eb AJ |
219 | if (thread->request_fd) release_object( thread->request_fd ); |
220 | if (thread->reply_fd) release_object( thread->reply_fd ); | |
221 | if (thread->wait_fd) release_object( thread->wait_fd ); | |
73c72390 | 222 | if (thread->suspend_context) free( thread->suspend_context ); |
31022d66 | 223 | free_msg_queue( thread ); |
b2df5f9f | 224 | cleanup_clipboard_thread(thread); |
1a66d226 | 225 | destroy_thread_windows( thread ); |
1bf96e09 | 226 | close_thread_desktop( thread ); |
f5242405 AJ |
227 | for (i = 0; i < MAX_INFLIGHT_FDS; i++) |
228 | { | |
229 | if (thread->inflight[i].client != -1) | |
230 | { | |
231 | close( thread->inflight[i].server ); | |
232 | thread->inflight[i].client = thread->inflight[i].server = -1; | |
233 | } | |
234 | } | |
9caa71ee AJ |
235 | thread->req_data = NULL; |
236 | thread->reply_data = NULL; | |
e66207eb AJ |
237 | thread->request_fd = NULL; |
238 | thread->reply_fd = NULL; | |
239 | thread->wait_fd = NULL; | |
73c72390 AJ |
240 | thread->context = NULL; |
241 | thread->suspend_context = NULL; | |
1bf96e09 | 242 | thread->desktop = 0; |
1dca5e24 AJ |
243 | } |
244 | ||
642d3136 AJ |
245 | /* destroy a thread when its refcount is 0 */ |
246 | static void destroy_thread( struct object *obj ) | |
247 | { | |
248 | struct thread *thread = (struct thread *)obj; | |
249 | assert( obj->ops == &thread_ops ); | |
250 | ||
e712e077 | 251 | assert( !thread->debug_ctx ); /* cannot still be debugging something */ |
73209eb7 | 252 | list_remove( &thread->entry ); |
f5242405 | 253 | cleanup_thread( thread ); |
51ab43bd | 254 | release_object( thread->process ); |
91befe1d | 255 | if (thread->id) free_ptid( thread->id ); |
36cd6f5d | 256 | if (thread->token) release_object( thread->token ); |
642d3136 AJ |
257 | } |
258 | ||
767e6f6f AJ |
259 | /* dump a thread on stdout for debugging purposes */ |
260 | static void dump_thread( struct object *obj, int verbose ) | |
261 | { | |
262 | struct thread *thread = (struct thread *)obj; | |
263 | assert( obj->ops == &thread_ops ); | |
264 | ||
a8497bd0 AJ |
265 | fprintf( stderr, "Thread id=%04x unix pid=%d unix tid=%d teb=%p state=%d\n", |
266 | thread->id, thread->unix_pid, thread->unix_tid, thread->teb, thread->state ); | |
767e6f6f AJ |
267 | } |
268 | ||
85ed45e3 AJ |
269 | static int thread_signaled( struct object *obj, struct thread *thread ) |
270 | { | |
271 | struct thread *mythread = (struct thread *)obj; | |
272 | return (mythread->state == TERMINATED); | |
273 | } | |
274 | ||
46d1b3e8 AJ |
275 | static unsigned int thread_map_access( struct object *obj, unsigned int access ) |
276 | { | |
277 | if (access & GENERIC_READ) access |= STANDARD_RIGHTS_READ | SYNCHRONIZE; | |
278 | if (access & GENERIC_WRITE) access |= STANDARD_RIGHTS_WRITE | SYNCHRONIZE; | |
279 | if (access & GENERIC_EXECUTE) access |= STANDARD_RIGHTS_EXECUTE; | |
280 | if (access & GENERIC_ALL) access |= THREAD_ALL_ACCESS; | |
281 | return access & ~(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL); | |
282 | } | |
283 | ||
767e6f6f | 284 | /* get a thread pointer from a thread id (and increment the refcount) */ |
54f22873 | 285 | struct thread *get_thread_from_id( thread_id_t id ) |
642d3136 | 286 | { |
91befe1d AJ |
287 | struct object *obj = get_ptid_entry( id ); |
288 | ||
289 | if (obj && obj->ops == &thread_ops) return (struct thread *)grab_object( obj ); | |
f895ad18 | 290 | set_error( STATUS_INVALID_CID ); |
91befe1d | 291 | return NULL; |
642d3136 AJ |
292 | } |
293 | ||
767e6f6f | 294 | /* get a thread from a handle (and increment the refcount) */ |
51885749 | 295 | struct thread *get_thread_from_handle( obj_handle_t handle, unsigned int access ) |
642d3136 | 296 | { |
767e6f6f AJ |
297 | return (struct thread *)get_handle_obj( current->process, handle, |
298 | access, &thread_ops ); | |
642d3136 AJ |
299 | } |
300 | ||
578c1009 AJ |
301 | /* find a thread from a Unix pid */ |
302 | struct thread *get_thread_from_pid( int pid ) | |
303 | { | |
73209eb7 | 304 | struct thread *thread; |
a8497bd0 | 305 | |
73209eb7 AJ |
306 | LIST_FOR_EACH_ENTRY( thread, &thread_list, struct thread, entry ) |
307 | { | |
308 | if (thread->unix_tid == pid) return thread; | |
309 | } | |
310 | LIST_FOR_EACH_ENTRY( thread, &thread_list, struct thread, entry ) | |
311 | { | |
312 | if (thread->unix_pid == pid) return thread; | |
313 | } | |
a8497bd0 | 314 | return NULL; |
578c1009 AJ |
315 | } |
316 | ||
62a8b433 | 317 | /* set all information about a thread */ |
43c190e7 | 318 | static void set_thread_info( struct thread *thread, |
9caa71ee | 319 | const struct set_thread_info_request *req ) |
62a8b433 AJ |
320 | { |
321 | if (req->mask & SET_THREAD_INFO_PRIORITY) | |
322 | thread->priority = req->priority; | |
323 | if (req->mask & SET_THREAD_INFO_AFFINITY) | |
324 | { | |
cb1fc735 | 325 | if (req->affinity != 1) set_error( STATUS_INVALID_PARAMETER ); |
62a8b433 AJ |
326 | else thread->affinity = req->affinity; |
327 | } | |
4bba2164 RS |
328 | if (req->mask & SET_THREAD_INFO_TOKEN) |
329 | security_set_thread_token( thread, req->token ); | |
62a8b433 AJ |
330 | } |
331 | ||
d04ccb8e AJ |
332 | /* stop a thread (at the Unix level) */ |
333 | void stop_thread( struct thread *thread ) | |
334 | { | |
ff7795ef | 335 | if (thread->context) return; /* already inside a debug event, no need for a signal */ |
d04ccb8e AJ |
336 | /* can't stop a thread while initialisation is in progress */ |
337 | if (is_process_init_done(thread->process)) send_thread_signal( thread, SIGUSR1 ); | |
338 | } | |
339 | ||
62a8b433 | 340 | /* suspend a thread */ |
d04ccb8e | 341 | static int suspend_thread( struct thread *thread ) |
62a8b433 AJ |
342 | { |
343 | int old_count = thread->suspend; | |
d04ccb8e | 344 | if (thread->suspend < MAXIMUM_SUSPEND_COUNT) |
62a8b433 | 345 | { |
0a70783c | 346 | if (!(thread->process->suspend + thread->suspend++)) stop_thread( thread ); |
62a8b433 | 347 | } |
cb1fc735 | 348 | else set_error( STATUS_SUSPEND_COUNT_EXCEEDED ); |
62a8b433 AJ |
349 | return old_count; |
350 | } | |
351 | ||
352 | /* resume a thread */ | |
d04ccb8e | 353 | static int resume_thread( struct thread *thread ) |
62a8b433 AJ |
354 | { |
355 | int old_count = thread->suspend; | |
356 | if (thread->suspend > 0) | |
357 | { | |
d04ccb8e | 358 | if (!(--thread->suspend + thread->process->suspend)) wake_thread( thread ); |
62a8b433 AJ |
359 | } |
360 | return old_count; | |
85ed45e3 AJ |
361 | } |
362 | ||
85ed45e3 | 363 | /* add a thread to an object wait queue; return 1 if OK, 0 on error */ |
a8b8d9c0 | 364 | int add_queue( struct object *obj, struct wait_queue_entry *entry ) |
85ed45e3 | 365 | { |
c6e45ed5 | 366 | grab_object( obj ); |
aa347686 AJ |
367 | entry->obj = obj; |
368 | list_add_tail( &obj->wait_queue, &entry->entry ); | |
a8b8d9c0 | 369 | return 1; |
85ed45e3 AJ |
370 | } |
371 | ||
372 | /* remove a thread from an object wait queue */ | |
c6e45ed5 | 373 | void remove_queue( struct object *obj, struct wait_queue_entry *entry ) |
85ed45e3 | 374 | { |
aa347686 | 375 | list_remove( &entry->entry ); |
85ed45e3 AJ |
376 | release_object( obj ); |
377 | } | |
378 | ||
379 | /* finish waiting */ | |
380 | static void end_wait( struct thread *thread ) | |
381 | { | |
382 | struct thread_wait *wait = thread->wait; | |
383 | struct wait_queue_entry *entry; | |
384 | int i; | |
385 | ||
386 | assert( wait ); | |
c6e45ed5 AJ |
387 | for (i = 0, entry = wait->queues; i < wait->count; i++, entry++) |
388 | entry->obj->ops->remove_queue( entry->obj, entry ); | |
57e11313 | 389 | if (wait->user) remove_timeout_user( wait->user ); |
e5dedb19 | 390 | thread->wait = wait->next; |
85ed45e3 | 391 | free( wait ); |
85ed45e3 AJ |
392 | } |
393 | ||
394 | /* build the thread wait structure */ | |
462172a3 | 395 | static int wait_on( int count, struct object *objects[], int flags, const abs_time_t *timeout ) |
85ed45e3 AJ |
396 | { |
397 | struct thread_wait *wait; | |
398 | struct wait_queue_entry *entry; | |
85ed45e3 AJ |
399 | int i; |
400 | ||
d30dfd24 | 401 | if (!(wait = mem_alloc( sizeof(*wait) + (count-1) * sizeof(*entry) ))) return 0; |
e5dedb19 AJ |
402 | wait->next = current->wait; |
403 | wait->thread = current; | |
85ed45e3 AJ |
404 | wait->count = count; |
405 | wait->flags = flags; | |
57e11313 | 406 | wait->user = NULL; |
e5dedb19 | 407 | current->wait = wait; |
247b8aee AJ |
408 | if (flags & SELECT_TIMEOUT) |
409 | { | |
462172a3 AJ |
410 | wait->timeout.tv_sec = timeout->sec; |
411 | wait->timeout.tv_usec = timeout->usec; | |
247b8aee | 412 | } |
85ed45e3 AJ |
413 | |
414 | for (i = 0, entry = wait->queues; i < count; i++, entry++) | |
415 | { | |
9de03f4f AJ |
416 | struct object *obj = objects[i]; |
417 | entry->thread = current; | |
a8b8d9c0 AJ |
418 | if (!obj->ops->add_queue( obj, entry )) |
419 | { | |
9de03f4f AJ |
420 | wait->count = i; |
421 | end_wait( current ); | |
a8b8d9c0 AJ |
422 | return 0; |
423 | } | |
85ed45e3 AJ |
424 | } |
425 | return 1; | |
426 | } | |
427 | ||
428 | /* check if the thread waiting condition is satisfied */ | |
d90e964c | 429 | static int check_wait( struct thread *thread ) |
85ed45e3 | 430 | { |
9de03f4f | 431 | int i, signaled; |
85ed45e3 AJ |
432 | struct thread_wait *wait = thread->wait; |
433 | struct wait_queue_entry *entry = wait->queues; | |
85ed45e3 | 434 | |
a3c5ad4e | 435 | /* Suspended threads may not acquire locks */ |
37957093 | 436 | if (thread->process->suspend + thread->suspend > 0) return -1; |
a3c5ad4e | 437 | |
85ed45e3 AJ |
438 | assert( wait ); |
439 | if (wait->flags & SELECT_ALL) | |
440 | { | |
57e11313 AJ |
441 | int not_ok = 0; |
442 | /* Note: we must check them all anyway, as some objects may | |
443 | * want to do something when signaled, even if others are not */ | |
85ed45e3 | 444 | for (i = 0, entry = wait->queues; i < wait->count; i++, entry++) |
57e11313 AJ |
445 | not_ok |= !entry->obj->ops->signaled( entry->obj, thread ); |
446 | if (not_ok) goto other_checks; | |
85ed45e3 | 447 | /* Wait satisfied: tell it to all objects */ |
9de03f4f | 448 | signaled = 0; |
85ed45e3 AJ |
449 | for (i = 0, entry = wait->queues; i < wait->count; i++, entry++) |
450 | if (entry->obj->ops->satisfied( entry->obj, thread )) | |
9de03f4f AJ |
451 | signaled = STATUS_ABANDONED_WAIT_0; |
452 | return signaled; | |
85ed45e3 AJ |
453 | } |
454 | else | |
455 | { | |
456 | for (i = 0, entry = wait->queues; i < wait->count; i++, entry++) | |
457 | { | |
458 | if (!entry->obj->ops->signaled( entry->obj, thread )) continue; | |
459 | /* Wait satisfied: tell it to the object */ | |
9de03f4f | 460 | signaled = i; |
85ed45e3 | 461 | if (entry->obj->ops->satisfied( entry->obj, thread )) |
9de03f4f AJ |
462 | signaled = i + STATUS_ABANDONED_WAIT_0; |
463 | return signaled; | |
85ed45e3 AJ |
464 | } |
465 | } | |
62a8b433 AJ |
466 | |
467 | other_checks: | |
81b6a1fb AJ |
468 | if ((wait->flags & SELECT_INTERRUPTIBLE) && !list_empty(&thread->system_apc)) return STATUS_USER_APC; |
469 | if ((wait->flags & SELECT_ALERTABLE) && !list_empty(&thread->user_apc)) return STATUS_USER_APC; | |
62a8b433 AJ |
470 | if (wait->flags & SELECT_TIMEOUT) |
471 | { | |
472 | struct timeval now; | |
473 | gettimeofday( &now, NULL ); | |
9de03f4f | 474 | if (!time_before( &now, &wait->timeout )) return STATUS_TIMEOUT; |
62a8b433 | 475 | } |
9de03f4f AJ |
476 | return -1; |
477 | } | |
478 | ||
e5dedb19 AJ |
479 | /* send the wakeup signal to a thread */ |
480 | static int send_thread_wakeup( struct thread *thread, void *cookie, int signaled ) | |
481 | { | |
482 | struct wake_up_reply reply; | |
483 | int ret; | |
484 | ||
485 | reply.cookie = cookie; | |
486 | reply.signaled = signaled; | |
e66207eb AJ |
487 | if ((ret = write( get_unix_fd( thread->wait_fd ), &reply, sizeof(reply) )) == sizeof(reply)) |
488 | return 0; | |
e5dedb19 AJ |
489 | if (ret >= 0) |
490 | fatal_protocol_error( thread, "partial wakeup write %d\n", ret ); | |
491 | else if (errno == EPIPE) | |
492 | kill_thread( thread, 0 ); /* normal death */ | |
493 | else | |
494 | fatal_protocol_perror( thread, "write" ); | |
495 | return -1; | |
496 | } | |
497 | ||
85ed45e3 | 498 | /* attempt to wake up a thread */ |
e5dedb19 | 499 | /* return >0 if OK, 0 if the wait condition is still not satisfied */ |
a3c5ad4e | 500 | int wake_thread( struct thread *thread ) |
85ed45e3 | 501 | { |
e5dedb19 AJ |
502 | int signaled, count; |
503 | void *cookie; | |
d90e964c | 504 | |
e5dedb19 AJ |
505 | for (count = 0; thread->wait; count++) |
506 | { | |
507 | if ((signaled = check_wait( thread )) == -1) break; | |
508 | ||
509 | cookie = thread->wait->cookie; | |
91befe1d AJ |
510 | if (debug_level) fprintf( stderr, "%04x: *wakeup* signaled=%d cookie=%p\n", |
511 | thread->id, signaled, cookie ); | |
e5dedb19 | 512 | end_wait( thread ); |
d66130ac AM |
513 | if (send_thread_wakeup( thread, cookie, signaled ) == -1) /* error */ |
514 | break; | |
e5dedb19 AJ |
515 | } |
516 | return count; | |
85ed45e3 AJ |
517 | } |
518 | ||
9de03f4f AJ |
519 | /* thread wait timeout */ |
520 | static void thread_timeout( void *ptr ) | |
521 | { | |
e5dedb19 AJ |
522 | struct thread_wait *wait = ptr; |
523 | struct thread *thread = wait->thread; | |
524 | void *cookie = wait->cookie; | |
d90e964c | 525 | |
e5dedb19 AJ |
526 | wait->user = NULL; |
527 | if (thread->wait != wait) return; /* not the top-level wait, ignore it */ | |
d04ccb8e | 528 | if (thread->suspend + thread->process->suspend > 0) return; /* suspended, ignore it */ |
d90e964c | 529 | |
91befe1d | 530 | if (debug_level) fprintf( stderr, "%04x: *wakeup* signaled=%d cookie=%p\n", |
1a1583a3 | 531 | thread->id, (int)STATUS_TIMEOUT, cookie ); |
9de03f4f | 532 | end_wait( thread ); |
ff732506 | 533 | if (send_thread_wakeup( thread, cookie, STATUS_TIMEOUT ) == -1) return; |
e5dedb19 AJ |
534 | /* check if other objects have become signaled in the meantime */ |
535 | wake_thread( thread ); | |
62a8b433 AJ |
536 | } |
537 | ||
f92fff66 MM |
538 | /* try signaling an event flag, a semaphore or a mutex */ |
539 | static int signal_object( obj_handle_t handle ) | |
540 | { | |
541 | struct object *obj; | |
542 | int ret = 0; | |
543 | ||
544 | obj = get_handle_obj( current->process, handle, 0, NULL ); | |
545 | if (obj) | |
546 | { | |
547 | ret = obj->ops->signal( obj, get_handle_access( current->process, handle )); | |
548 | release_object( obj ); | |
549 | } | |
550 | return ret; | |
551 | } | |
552 | ||
9de03f4f | 553 | /* select on a list of handles */ |
51885749 | 554 | static void select_on( int count, void *cookie, const obj_handle_t *handles, |
f92fff66 | 555 | int flags, const abs_time_t *timeout, obj_handle_t signal_obj ) |
85ed45e3 | 556 | { |
d90e964c | 557 | int ret, i; |
9de03f4f | 558 | struct object *objects[MAXIMUM_WAIT_OBJECTS]; |
ebe29ef3 | 559 | |
9de03f4f AJ |
560 | if ((count < 0) || (count > MAXIMUM_WAIT_OBJECTS)) |
561 | { | |
cb1fc735 | 562 | set_error( STATUS_INVALID_PARAMETER ); |
d90e964c | 563 | return; |
9de03f4f AJ |
564 | } |
565 | for (i = 0; i < count; i++) | |
566 | { | |
567 | if (!(objects[i] = get_handle_obj( current->process, handles[i], SYNCHRONIZE, NULL ))) | |
568 | break; | |
569 | } | |
d90e964c AJ |
570 | |
571 | if (i < count) goto done; | |
462172a3 | 572 | if (!wait_on( count, objects, flags, timeout )) goto done; |
d90e964c | 573 | |
f92fff66 MM |
574 | /* signal the object */ |
575 | if (signal_obj) | |
576 | { | |
577 | if (!signal_object( signal_obj )) | |
578 | { | |
579 | end_wait( current ); | |
580 | goto done; | |
581 | } | |
582 | /* check if we woke ourselves up */ | |
583 | if (!current->wait) goto done; | |
584 | } | |
585 | ||
d90e964c AJ |
586 | if ((ret = check_wait( current )) != -1) |
587 | { | |
588 | /* condition is already satisfied */ | |
589 | end_wait( current ); | |
590 | set_error( ret ); | |
591 | goto done; | |
592 | } | |
593 | ||
594 | /* now we need to wait */ | |
595 | if (flags & SELECT_TIMEOUT) | |
596 | { | |
597 | if (!(current->wait->user = add_timeout_user( ¤t->wait->timeout, | |
e5dedb19 | 598 | thread_timeout, current->wait ))) |
d90e964c AJ |
599 | { |
600 | end_wait( current ); | |
601 | goto done; | |
602 | } | |
603 | } | |
e5dedb19 | 604 | current->wait->cookie = cookie; |
d90e964c AJ |
605 | set_error( STATUS_PENDING ); |
606 | ||
607 | done: | |
9de03f4f | 608 | while (--i >= 0) release_object( objects[i] ); |
85ed45e3 AJ |
609 | } |
610 | ||
611 | /* attempt to wake threads sleeping on the object wait queue */ | |
612 | void wake_up( struct object *obj, int max ) | |
613 | { | |
aa347686 | 614 | struct list *ptr, *next; |
85ed45e3 | 615 | |
aa347686 | 616 | LIST_FOR_EACH_SAFE( ptr, next, &obj->wait_queue ) |
85ed45e3 | 617 | { |
aa347686 AJ |
618 | struct wait_queue_entry *entry = LIST_ENTRY( ptr, struct wait_queue_entry, entry ); |
619 | if (wake_thread( entry->thread )) | |
85ed45e3 AJ |
620 | { |
621 | if (max && !--max) break; | |
622 | } | |
85ed45e3 AJ |
623 | } |
624 | } | |
625 | ||
62a8b433 | 626 | /* queue an async procedure call */ |
ea1afcef | 627 | int thread_queue_apc( struct thread *thread, struct object *owner, void *func, |
088bcf9c | 628 | enum apc_type type, int system, void *arg1, void *arg2, void *arg3 ) |
62a8b433 AJ |
629 | { |
630 | struct thread_apc *apc; | |
81b6a1fb | 631 | struct list *queue = system ? &thread->system_apc : &thread->user_apc; |
ea1afcef AJ |
632 | |
633 | /* cancel a possible previous APC with the same owner */ | |
2362380b | 634 | if (owner) thread_cancel_apc( thread, owner, system ); |
15979fda | 635 | if (thread->state == TERMINATED) return 0; |
ea1afcef | 636 | |
088bcf9c | 637 | if (!(apc = mem_alloc( sizeof(*apc) ))) return 0; |
088bcf9c AJ |
638 | apc->owner = owner; |
639 | apc->func = func; | |
640 | apc->type = type; | |
641 | apc->arg1 = arg1; | |
642 | apc->arg2 = arg2; | |
643 | apc->arg3 = arg3; | |
81b6a1fb AJ |
644 | list_add_tail( queue, &apc->entry ); |
645 | if (!list_prev( queue, &apc->entry )) /* first one */ | |
e5dedb19 | 646 | wake_thread( thread ); |
b1c45b94 | 647 | |
62a8b433 AJ |
648 | return 1; |
649 | } | |
650 | ||
ea1afcef | 651 | /* cancel the async procedure call owned by a specific object */ |
2362380b | 652 | void thread_cancel_apc( struct thread *thread, struct object *owner, int system ) |
ea1afcef AJ |
653 | { |
654 | struct thread_apc *apc; | |
81b6a1fb AJ |
655 | struct list *queue = system ? &thread->system_apc : &thread->user_apc; |
656 | LIST_FOR_EACH_ENTRY( apc, queue, struct thread_apc, entry ) | |
ea1afcef AJ |
657 | { |
658 | if (apc->owner != owner) continue; | |
81b6a1fb | 659 | list_remove( &apc->entry ); |
ea1afcef AJ |
660 | free( apc ); |
661 | return; | |
662 | } | |
663 | } | |
664 | ||
665 | /* remove the head apc from the queue; the returned pointer must be freed by the caller */ | |
2362380b | 666 | static struct thread_apc *thread_dequeue_apc( struct thread *thread, int system_only ) |
ea1afcef | 667 | { |
81b6a1fb AJ |
668 | struct thread_apc *apc = NULL; |
669 | struct list *ptr = list_head( &thread->system_apc ); | |
2362380b | 670 | |
81b6a1fb AJ |
671 | if (!ptr && !system_only) ptr = list_head( &thread->user_apc ); |
672 | if (ptr) | |
ea1afcef | 673 | { |
81b6a1fb AJ |
674 | apc = LIST_ENTRY( ptr, struct thread_apc, entry ); |
675 | list_remove( ptr ); | |
ea1afcef AJ |
676 | } |
677 | return apc; | |
678 | } | |
679 | ||
f5242405 AJ |
680 | /* add an fd to the inflight list */ |
681 | /* return list index, or -1 on error */ | |
682 | int thread_add_inflight_fd( struct thread *thread, int client, int server ) | |
683 | { | |
684 | int i; | |
685 | ||
686 | if (server == -1) return -1; | |
687 | if (client == -1) | |
688 | { | |
689 | close( server ); | |
690 | return -1; | |
691 | } | |
692 | ||
693 | /* first check if we already have an entry for this fd */ | |
694 | for (i = 0; i < MAX_INFLIGHT_FDS; i++) | |
695 | if (thread->inflight[i].client == client) | |
696 | { | |
697 | close( thread->inflight[i].server ); | |
698 | thread->inflight[i].server = server; | |
699 | return i; | |
700 | } | |
701 | ||
702 | /* now find a free spot to store it */ | |
703 | for (i = 0; i < MAX_INFLIGHT_FDS; i++) | |
704 | if (thread->inflight[i].client == -1) | |
705 | { | |
706 | thread->inflight[i].client = client; | |
707 | thread->inflight[i].server = server; | |
708 | return i; | |
709 | } | |
710 | return -1; | |
711 | } | |
712 | ||
713 | /* get an inflight fd and purge it from the list */ | |
714 | /* the fd must be closed when no longer used */ | |
715 | int thread_get_inflight_fd( struct thread *thread, int client ) | |
716 | { | |
717 | int i, ret; | |
718 | ||
719 | if (client == -1) return -1; | |
720 | ||
721 | do | |
722 | { | |
723 | for (i = 0; i < MAX_INFLIGHT_FDS; i++) | |
724 | { | |
725 | if (thread->inflight[i].client == client) | |
726 | { | |
727 | ret = thread->inflight[i].server; | |
728 | thread->inflight[i].server = thread->inflight[i].client = -1; | |
729 | return ret; | |
730 | } | |
731 | } | |
732 | } while (!receive_fd( thread->process )); /* in case it is still in the socket buffer */ | |
733 | return -1; | |
734 | } | |
735 | ||
0a7c1f6c AJ |
736 | /* retrieve an LDT selector entry */ |
737 | static void get_selector_entry( struct thread *thread, int entry, | |
738 | unsigned int *base, unsigned int *limit, | |
739 | unsigned char *flags ) | |
740 | { | |
914406f8 | 741 | if (!thread->process->ldt_copy) |
0a7c1f6c AJ |
742 | { |
743 | set_error( STATUS_ACCESS_DENIED ); | |
744 | return; | |
745 | } | |
746 | if (entry >= 8192) | |
747 | { | |
748 | set_error( STATUS_INVALID_PARAMETER ); /* FIXME */ | |
749 | return; | |
750 | } | |
98aacc77 | 751 | if (suspend_for_ptrace( thread )) |
0a7c1f6c AJ |
752 | { |
753 | unsigned char flags_buf[4]; | |
914406f8 | 754 | int *addr = (int *)thread->process->ldt_copy + entry; |
e659f1e6 MM |
755 | if (read_thread_int( thread, addr, (int *)base ) == -1) goto done; |
756 | if (read_thread_int( thread, addr + 8192, (int *)limit ) == -1) goto done; | |
914406f8 | 757 | addr = (int *)thread->process->ldt_copy + 2*8192 + (entry >> 2); |
0a7c1f6c AJ |
758 | if (read_thread_int( thread, addr, (int *)flags_buf ) == -1) goto done; |
759 | *flags = flags_buf[entry & 3]; | |
98aacc77 | 760 | done: |
d04ccb8e | 761 | resume_after_ptrace( thread ); |
0a7c1f6c | 762 | } |
0a7c1f6c AJ |
763 | } |
764 | ||
767e6f6f | 765 | /* kill a thread on the spot */ |
12f29b50 | 766 | void kill_thread( struct thread *thread, int violent_death ) |
642d3136 | 767 | { |
d30dfd24 | 768 | if (thread->state == TERMINATED) return; /* already killed */ |
85ed45e3 | 769 | thread->state = TERMINATED; |
24f4eced | 770 | thread->exit_time = time(NULL); |
1dca5e24 | 771 | if (current == thread) current = NULL; |
05f0b71b | 772 | if (debug_level) |
91befe1d AJ |
773 | fprintf( stderr,"%04x: *killed* exit_code=%d\n", |
774 | thread->id, thread->exit_code ); | |
12f29b50 AJ |
775 | if (thread->wait) |
776 | { | |
e5dedb19 AJ |
777 | while (thread->wait) end_wait( thread ); |
778 | send_thread_wakeup( thread, NULL, STATUS_PENDING ); | |
12f29b50 AJ |
779 | /* if it is waiting on the socket, we don't need to send a SIGTERM */ |
780 | violent_death = 0; | |
781 | } | |
3940d8a2 | 782 | kill_console_processes( thread, 0 ); |
ff81d787 | 783 | debug_exit_thread( thread ); |
d30dfd24 | 784 | abandon_mutexes( thread ); |
85ed45e3 | 785 | wake_up( &thread->obj, 0 ); |
1b2adad8 | 786 | if (violent_death) send_thread_signal( thread, SIGTERM ); |
f5242405 | 787 | cleanup_thread( thread ); |
867ae269 | 788 | remove_process_thread( thread->process, thread ); |
767e6f6f | 789 | release_object( thread ); |
642d3136 | 790 | } |
43c190e7 | 791 | |
07d84469 AJ |
792 | /* take a snapshot of currently running threads */ |
793 | struct thread_snapshot *thread_snap( int *count ) | |
794 | { | |
795 | struct thread_snapshot *snapshot, *ptr; | |
796 | struct thread *thread; | |
797 | int total = 0; | |
798 | ||
73209eb7 | 799 | LIST_FOR_EACH_ENTRY( thread, &thread_list, struct thread, entry ) |
07d84469 AJ |
800 | if (thread->state != TERMINATED) total++; |
801 | if (!total || !(snapshot = mem_alloc( sizeof(*snapshot) * total ))) return NULL; | |
802 | ptr = snapshot; | |
73209eb7 | 803 | LIST_FOR_EACH_ENTRY( thread, &thread_list, struct thread, entry ) |
07d84469 AJ |
804 | { |
805 | if (thread->state == TERMINATED) continue; | |
806 | ptr->thread = thread; | |
807 | ptr->count = thread->obj.refcount; | |
808 | ptr->priority = thread->priority; | |
809 | grab_object( thread ); | |
810 | ptr++; | |
811 | } | |
812 | *count = total; | |
813 | return snapshot; | |
814 | } | |
815 | ||
d2ea92d1 RS |
816 | /* gets the current impersonation token */ |
817 | struct token *thread_get_impersonation_token( struct thread *thread ) | |
818 | { | |
819 | if (thread->token) | |
820 | return thread->token; | |
821 | else | |
822 | return thread->process->token; | |
823 | } | |
824 | ||
43c190e7 AJ |
825 | /* create a new thread */ |
826 | DECL_HANDLER(new_thread) | |
827 | { | |
5bc78089 | 828 | struct thread *thread; |
8859d772 | 829 | int request_fd = thread_get_inflight_fd( current, req->request_fd ); |
95e7acb9 | 830 | |
9caa71ee | 831 | if (request_fd == -1 || fcntl( request_fd, F_SETFL, O_NONBLOCK ) == -1) |
43c190e7 | 832 | { |
9caa71ee | 833 | if (request_fd != -1) close( request_fd ); |
8859d772 AJ |
834 | set_error( STATUS_INVALID_HANDLE ); |
835 | return; | |
836 | } | |
837 | ||
838 | if ((thread = create_thread( request_fd, current->process ))) | |
839 | { | |
840 | if (req->suspend) thread->suspend++; | |
54f22873 | 841 | reply->tid = get_thread_id( thread ); |
24560e70 | 842 | if ((reply->handle = alloc_handle( current->process, thread, req->access, req->attributes ))) |
5bc78089 | 843 | { |
8859d772 AJ |
844 | /* thread object will be released when the thread gets killed */ |
845 | return; | |
5bc78089 | 846 | } |
8859d772 | 847 | kill_thread( thread, 1 ); |
43c190e7 | 848 | } |
43c190e7 AJ |
849 | } |
850 | ||
851 | /* initialize a new thread */ | |
852 | DECL_HANDLER(init_thread) | |
853 | { | |
0424f381 | 854 | struct process *process = current->process; |
8859d772 AJ |
855 | int reply_fd = thread_get_inflight_fd( current, req->reply_fd ); |
856 | int wait_fd = thread_get_inflight_fd( current, req->wait_fd ); | |
857 | ||
02a53c17 | 858 | if (current->unix_pid != -1) |
43c190e7 | 859 | { |
ebe29ef3 | 860 | fatal_protocol_error( current, "init_thread: already running\n" ); |
8859d772 AJ |
861 | goto error; |
862 | } | |
9caa71ee | 863 | if (reply_fd == -1 || fcntl( reply_fd, F_SETFL, O_NONBLOCK ) == -1) |
8859d772 AJ |
864 | { |
865 | fatal_protocol_error( current, "bad reply fd\n" ); | |
866 | goto error; | |
867 | } | |
868 | if (wait_fd == -1) | |
869 | { | |
870 | fatal_protocol_error( current, "bad wait fd\n" ); | |
871 | goto error; | |
43c190e7 | 872 | } |
3c68ab05 AJ |
873 | if (!(current->reply_fd = create_anonymous_fd( &thread_fd_ops, reply_fd, ¤t->obj ))) |
874 | { | |
875 | reply_fd = -1; | |
876 | fatal_protocol_error( current, "could not allocate reply fd\n" ); | |
877 | goto error; | |
878 | } | |
879 | if (!(current->wait_fd = create_anonymous_fd( &thread_fd_ops, wait_fd, ¤t->obj ))) | |
880 | return; | |
8859d772 | 881 | |
0424f381 AJ |
882 | if (!is_valid_address(req->teb) || !is_valid_address(req->peb) || !is_valid_address(req->ldt_copy)) |
883 | { | |
884 | set_error( STATUS_INVALID_PARAMETER ); | |
885 | return; | |
886 | } | |
887 | ||
43c190e7 | 888 | current->unix_pid = req->unix_pid; |
a8497bd0 | 889 | current->unix_tid = req->unix_tid; |
57e11313 | 890 | current->teb = req->teb; |
8859d772 | 891 | |
0424f381 AJ |
892 | if (!process->peb) /* first thread, initialize the process too */ |
893 | { | |
894 | process->peb = req->peb; | |
895 | process->ldt_copy = req->ldt_copy; | |
11ad6a0a | 896 | reply->info_size = init_process( current ); |
0424f381 AJ |
897 | } |
898 | else | |
899 | { | |
900 | if (current->suspend + process->suspend > 0) stop_thread( current ); | |
b73421dd | 901 | generate_debug_event( current, CREATE_THREAD_DEBUG_EVENT, req->entry ); |
0424f381 AJ |
902 | } |
903 | debug_level = max( debug_level, req->debug_level ); | |
8859d772 | 904 | |
0424f381 | 905 | reply->pid = get_process_id( process ); |
9caa71ee | 906 | reply->tid = get_thread_id( current ); |
9caa71ee | 907 | reply->version = SERVER_PROTOCOL_VERSION; |
9ad56286 | 908 | reply->server_start = server_start_time; |
8859d772 AJ |
909 | return; |
910 | ||
911 | error: | |
912 | if (reply_fd != -1) close( reply_fd ); | |
913 | if (wait_fd != -1) close( wait_fd ); | |
914 | } | |
915 | ||
43c190e7 AJ |
916 | /* terminate a thread */ |
917 | DECL_HANDLER(terminate_thread) | |
918 | { | |
919 | struct thread *thread; | |
920 | ||
9caa71ee AJ |
921 | reply->self = 0; |
922 | reply->last = 0; | |
43c190e7 AJ |
923 | if ((thread = get_thread_from_handle( req->handle, THREAD_TERMINATE ))) |
924 | { | |
12f29b50 AJ |
925 | thread->exit_code = req->exit_code; |
926 | if (thread != current) kill_thread( thread, 1 ); | |
927 | else | |
928 | { | |
9caa71ee AJ |
929 | reply->self = 1; |
930 | reply->last = (thread->process->running_threads == 1); | |
12f29b50 | 931 | } |
43c190e7 AJ |
932 | release_object( thread ); |
933 | } | |
43c190e7 AJ |
934 | } |
935 | ||
417296c4 CM |
936 | /* open a handle to a thread */ |
937 | DECL_HANDLER(open_thread) | |
938 | { | |
939 | struct thread *thread = get_thread_from_id( req->tid ); | |
940 | ||
941 | reply->handle = 0; | |
942 | if (thread) | |
943 | { | |
24560e70 | 944 | reply->handle = alloc_handle( current->process, thread, req->access, req->attributes ); |
417296c4 CM |
945 | release_object( thread ); |
946 | } | |
947 | } | |
948 | ||
43c190e7 AJ |
949 | /* fetch information about a thread */ |
950 | DECL_HANDLER(get_thread_info) | |
951 | { | |
952 | struct thread *thread; | |
51885749 | 953 | obj_handle_t handle = req->handle; |
43c190e7 | 954 | |
8081e5a1 | 955 | if (!handle) thread = get_thread_from_id( req->tid_in ); |
9a0e28f1 AJ |
956 | else thread = get_thread_from_handle( req->handle, THREAD_QUERY_INFORMATION ); |
957 | ||
958 | if (thread) | |
43c190e7 | 959 | { |
4f196ead | 960 | reply->pid = get_process_id( thread->process ); |
24f4eced RC |
961 | reply->tid = get_thread_id( thread ); |
962 | reply->teb = thread->teb; | |
963 | reply->exit_code = (thread->state == TERMINATED) ? thread->exit_code : STILL_ACTIVE; | |
964 | reply->priority = thread->priority; | |
4f196ead | 965 | reply->affinity = thread->affinity; |
24f4eced RC |
966 | reply->creation_time = thread->creation_time; |
967 | reply->exit_time = thread->exit_time; | |
968 | ||
43c190e7 AJ |
969 | release_object( thread ); |
970 | } | |
43c190e7 AJ |
971 | } |
972 | ||
973 | /* set information about a thread */ | |
974 | DECL_HANDLER(set_thread_info) | |
975 | { | |
976 | struct thread *thread; | |
977 | ||
978 | if ((thread = get_thread_from_handle( req->handle, THREAD_SET_INFORMATION ))) | |
979 | { | |
980 | set_thread_info( thread, req ); | |
981 | release_object( thread ); | |
982 | } | |
43c190e7 AJ |
983 | } |
984 | ||
985 | /* suspend a thread */ | |
986 | DECL_HANDLER(suspend_thread) | |
987 | { | |
988 | struct thread *thread; | |
ebe29ef3 | 989 | |
43c190e7 AJ |
990 | if ((thread = get_thread_from_handle( req->handle, THREAD_SUSPEND_RESUME ))) |
991 | { | |
f6662576 | 992 | if (thread->state == TERMINATED) set_error( STATUS_ACCESS_DENIED ); |
d04ccb8e | 993 | else reply->count = suspend_thread( thread ); |
43c190e7 AJ |
994 | release_object( thread ); |
995 | } | |
43c190e7 AJ |
996 | } |
997 | ||
998 | /* resume a thread */ | |
999 | DECL_HANDLER(resume_thread) | |
1000 | { | |
1001 | struct thread *thread; | |
ebe29ef3 | 1002 | |
43c190e7 AJ |
1003 | if ((thread = get_thread_from_handle( req->handle, THREAD_SUSPEND_RESUME ))) |
1004 | { | |
f6662576 FG |
1005 | if (thread->state == TERMINATED) set_error( STATUS_ACCESS_DENIED ); |
1006 | else reply->count = resume_thread( thread ); | |
43c190e7 AJ |
1007 | release_object( thread ); |
1008 | } | |
43c190e7 AJ |
1009 | } |
1010 | ||
1011 | /* select on a handle list */ | |
1012 | DECL_HANDLER(select) | |
1013 | { | |
9caa71ee | 1014 | int count = get_req_data_size() / sizeof(int); |
f92fff66 | 1015 | select_on( count, req->cookie, get_req_data(), req->flags, &req->timeout, req->signal ); |
43c190e7 AJ |
1016 | } |
1017 | ||
1018 | /* queue an APC for a thread */ | |
1019 | DECL_HANDLER(queue_apc) | |
1020 | { | |
1021 | struct thread *thread; | |
1022 | if ((thread = get_thread_from_handle( req->handle, THREAD_SET_CONTEXT ))) | |
1023 | { | |
088bcf9c AJ |
1024 | thread_queue_apc( thread, NULL, req->func, APC_USER, !req->user, |
1025 | req->arg1, req->arg2, req->arg3 ); | |
43c190e7 AJ |
1026 | release_object( thread ); |
1027 | } | |
43c190e7 | 1028 | } |
ebe29ef3 | 1029 | |
ea1afcef AJ |
1030 | /* get next APC to call */ |
1031 | DECL_HANDLER(get_apc) | |
ebe29ef3 | 1032 | { |
ea1afcef AJ |
1033 | struct thread_apc *apc; |
1034 | ||
9c2370bd | 1035 | for (;;) |
ea1afcef | 1036 | { |
2362380b | 1037 | if (!(apc = thread_dequeue_apc( current, !req->alertable ))) |
9c2370bd AJ |
1038 | { |
1039 | /* no more APCs */ | |
9caa71ee AJ |
1040 | reply->func = NULL; |
1041 | reply->type = APC_NONE; | |
9c2370bd AJ |
1042 | return; |
1043 | } | |
1044 | /* Optimization: ignore APCs that have a NULL func; they are only used | |
1045 | * to wake up a thread, but since we got here the thread woke up already. | |
2b47fb3c | 1046 | * Exception: for APC_ASYNC_IO, func == NULL is legal. |
9c2370bd | 1047 | */ |
2b47fb3c | 1048 | if (apc->func || apc->type == APC_ASYNC_IO) break; |
ea1afcef AJ |
1049 | free( apc ); |
1050 | } | |
9caa71ee AJ |
1051 | reply->func = apc->func; |
1052 | reply->type = apc->type; | |
088bcf9c AJ |
1053 | reply->arg1 = apc->arg1; |
1054 | reply->arg2 = apc->arg2; | |
1055 | reply->arg3 = apc->arg3; | |
9c2370bd | 1056 | free( apc ); |
ebe29ef3 | 1057 | } |
0a7c1f6c | 1058 | |
3f85e26a AJ |
1059 | /* retrieve the current context of a thread */ |
1060 | DECL_HANDLER(get_thread_context) | |
1061 | { | |
1062 | struct thread *thread; | |
1063 | void *data; | |
1064 | ||
1065 | if (get_reply_max_size() < sizeof(CONTEXT)) | |
1066 | { | |
1067 | set_error( STATUS_INVALID_PARAMETER ); | |
1068 | return; | |
1069 | } | |
1070 | if (!(thread = get_thread_from_handle( req->handle, THREAD_GET_CONTEXT ))) return; | |
1071 | ||
73c72390 AJ |
1072 | if (req->suspend) |
1073 | { | |
1074 | if (thread != current || !thread->suspend_context) | |
1075 | { | |
1076 | /* not suspended, shouldn't happen */ | |
1077 | set_error( STATUS_INVALID_PARAMETER ); | |
1078 | } | |
1079 | else | |
1080 | { | |
1081 | if (thread->context == thread->suspend_context) thread->context = NULL; | |
1082 | set_reply_data_ptr( thread->suspend_context, sizeof(CONTEXT) ); | |
1083 | thread->suspend_context = NULL; | |
1084 | } | |
1085 | } | |
1086 | else if (thread != current && !thread->context) | |
1087 | { | |
1088 | /* thread is not suspended, retry (if it's still running) */ | |
1089 | if (thread->state != RUNNING) set_error( STATUS_ACCESS_DENIED ); | |
1090 | else set_error( STATUS_PENDING ); | |
1091 | } | |
1092 | else if ((data = set_reply_data_size( sizeof(CONTEXT) ))) | |
3f85e26a AJ |
1093 | { |
1094 | memset( data, 0, sizeof(CONTEXT) ); | |
1095 | get_thread_context( thread, data, req->flags ); | |
1096 | } | |
2878d992 | 1097 | reply->self = (thread == current); |
3f85e26a AJ |
1098 | release_object( thread ); |
1099 | } | |
1100 | ||
1101 | /* set the current context of a thread */ | |
1102 | DECL_HANDLER(set_thread_context) | |
1103 | { | |
1104 | struct thread *thread; | |
1105 | ||
1106 | if (get_req_data_size() < sizeof(CONTEXT)) | |
1107 | { | |
1108 | set_error( STATUS_INVALID_PARAMETER ); | |
1109 | return; | |
1110 | } | |
73c72390 AJ |
1111 | if (!(thread = get_thread_from_handle( req->handle, THREAD_SET_CONTEXT ))) return; |
1112 | ||
1113 | if (req->suspend) | |
1114 | { | |
1115 | if (thread != current || thread->context) | |
1116 | { | |
1117 | /* nested suspend or exception, shouldn't happen */ | |
1118 | set_error( STATUS_INVALID_PARAMETER ); | |
1119 | } | |
1120 | else if ((thread->suspend_context = mem_alloc( sizeof(CONTEXT) ))) | |
1121 | { | |
1122 | memcpy( thread->suspend_context, get_req_data(), sizeof(CONTEXT) ); | |
1123 | thread->context = thread->suspend_context; | |
1124 | } | |
1125 | } | |
1126 | else if (thread != current && !thread->context) | |
1127 | { | |
1128 | /* thread is not suspended, retry (if it's still running) */ | |
1129 | if (thread->state != RUNNING) set_error( STATUS_ACCESS_DENIED ); | |
1130 | else set_error( STATUS_PENDING ); | |
1131 | } | |
1132 | else | |
3f85e26a AJ |
1133 | { |
1134 | set_thread_context( thread, get_req_data(), req->flags ); | |
3f85e26a | 1135 | } |
2654be08 | 1136 | reply->self = (thread == current); |
73c72390 | 1137 | release_object( thread ); |
3f85e26a AJ |
1138 | } |
1139 | ||
0a7c1f6c AJ |
1140 | /* fetch a selector entry for a thread */ |
1141 | DECL_HANDLER(get_selector_entry) | |
1142 | { | |
1143 | struct thread *thread; | |
1144 | if ((thread = get_thread_from_handle( req->handle, THREAD_QUERY_INFORMATION ))) | |
1145 | { | |
9caa71ee | 1146 | get_selector_entry( thread, req->entry, &reply->base, &reply->limit, &reply->flags ); |
0a7c1f6c AJ |
1147 | release_object( thread ); |
1148 | } | |
1149 | } |