sync.c 50.7 KB
Newer Older
Juergen Schmied's avatar
Juergen Schmied committed
1 2
/*
 *	Process synchronisation
3
 *
4 5
 * Copyright 1996, 1997, 1998 Marcus Meissner
 * Copyright 1997, 1999 Alexandre Julliard
6
 * Copyright 1999, 2000 Juergen Schmied
7
 * Copyright 2003 Eric Pouech
8 9 10 11 12 13 14 15 16 17 18 19 20
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
21
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
Juergen Schmied's avatar
Juergen Schmied committed
22 23
 */

24 25 26 27 28 29 30 31
#include "config.h"

#include <assert.h>
#include <errno.h>
#include <signal.h>
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
32 33 34
#ifdef HAVE_POLL_H
#include <poll.h>
#endif
35 36 37 38 39 40
#ifdef HAVE_SYS_POLL_H
# include <sys/poll.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
41 42 43
#ifdef HAVE_SCHED_H
# include <sched.h>
#endif
44
#include <string.h>
45
#include <stdarg.h>
46
#include <stdio.h>
Juergen Schmied's avatar
Juergen Schmied committed
47 48 49
#include <stdlib.h>
#include <time.h>

50 51 52
#define NONAMELESSUNION
#define NONAMELESSSTRUCT

53 54
#include "ntstatus.h"
#define WIN32_NO_STATUS
55
#include "windef.h"
56
#include "winternl.h"
57 58
#include "wine/server.h"
#include "wine/debug.h"
59
#include "ntdll_misc.h"
Juergen Schmied's avatar
Juergen Schmied committed
60

61
WINE_DEFAULT_DEBUG_CHANNEL(ntdll);
62

63 64 65
/* creates a struct security_descriptor and contained information in one contiguous piece of memory */
NTSTATUS NTDLL_create_struct_sd(PSECURITY_DESCRIPTOR nt_sd, struct security_descriptor **server_sd,
                                data_size_t *server_sd_len)
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
{
    unsigned int len;
    PSID owner, group;
    ACL *dacl, *sacl;
    BOOLEAN owner_present, group_present, dacl_present, sacl_present;
    BOOLEAN defaulted;
    NTSTATUS status;
    unsigned char *ptr;

    if (!nt_sd)
    {
        *server_sd = NULL;
        *server_sd_len = 0;
        return STATUS_SUCCESS;
    }

    len = sizeof(struct security_descriptor);

    status = RtlGetOwnerSecurityDescriptor(nt_sd, &owner, &owner_present);
    if (status != STATUS_SUCCESS) return status;
    status = RtlGetGroupSecurityDescriptor(nt_sd, &group, &group_present);
    if (status != STATUS_SUCCESS) return status;
    status = RtlGetSaclSecurityDescriptor(nt_sd, &sacl_present, &sacl, &defaulted);
    if (status != STATUS_SUCCESS) return status;
    status = RtlGetDaclSecurityDescriptor(nt_sd, &dacl_present, &dacl, &defaulted);
    if (status != STATUS_SUCCESS) return status;

    if (owner_present)
        len += RtlLengthSid(owner);
    if (group_present)
        len += RtlLengthSid(group);
    if (sacl_present && sacl)
        len += sacl->AclSize;
    if (dacl_present && dacl)
        len += dacl->AclSize;

    /* fix alignment for the Unicode name that follows the structure */
    len = (len + sizeof(WCHAR) - 1) & ~(sizeof(WCHAR) - 1);
    *server_sd = RtlAllocateHeap(GetProcessHeap(), 0, len);
    if (!*server_sd) return STATUS_NO_MEMORY;

    (*server_sd)->control = ((SECURITY_DESCRIPTOR *)nt_sd)->Control & ~SE_SELF_RELATIVE;
    (*server_sd)->owner_len = owner_present ? RtlLengthSid(owner) : 0;
    (*server_sd)->group_len = group_present ? RtlLengthSid(group) : 0;
    (*server_sd)->sacl_len = (sacl_present && sacl) ? sacl->AclSize : 0;
    (*server_sd)->dacl_len = (dacl_present && dacl) ? dacl->AclSize : 0;

    ptr = (unsigned char *)(*server_sd + 1);
    memcpy(ptr, owner, (*server_sd)->owner_len);
    ptr += (*server_sd)->owner_len;
    memcpy(ptr, group, (*server_sd)->group_len);
    ptr += (*server_sd)->group_len;
    memcpy(ptr, sacl, (*server_sd)->sacl_len);
    ptr += (*server_sd)->sacl_len;
    memcpy(ptr, dacl, (*server_sd)->dacl_len);

    *server_sd_len = len;

    return STATUS_SUCCESS;
}

127 128
/* frees a struct security_descriptor allocated by NTDLL_create_struct_sd */
void NTDLL_free_struct_sd(struct security_descriptor *server_sd)
129 130 131 132
{
    RtlFreeHeap(GetProcessHeap(), 0, server_sd);
}

Juergen Schmied's avatar
Juergen Schmied committed
133
/*
134
 *	Semaphores
Juergen Schmied's avatar
Juergen Schmied committed
135 136 137
 */

/******************************************************************************
138
 *  NtCreateSemaphore (NTDLL.@)
Juergen Schmied's avatar
Juergen Schmied committed
139
 */
140 141 142
NTSTATUS WINAPI NtCreateSemaphore( OUT PHANDLE SemaphoreHandle,
                                   IN ACCESS_MASK access,
                                   IN const OBJECT_ATTRIBUTES *attr OPTIONAL,
143 144
                                   IN LONG InitialCount,
                                   IN LONG MaximumCount )
Juergen Schmied's avatar
Juergen Schmied committed
145
{
146
    DWORD len = attr && attr->ObjectName ? attr->ObjectName->Length : 0;
147
    NTSTATUS ret;
148 149
    struct object_attributes objattr;
    struct security_descriptor *sd = NULL;
150

151
    if (MaximumCount <= 0 || InitialCount < 0 || InitialCount > MaximumCount)
152
        return STATUS_INVALID_PARAMETER;
153
    if (len >= MAX_PATH * sizeof(WCHAR)) return STATUS_NAME_TOO_LONG;
154

155
    objattr.rootdir = wine_server_obj_handle( attr ? attr->RootDirectory : 0 );
156
    objattr.sd_len = 0;
157
    objattr.name_len = len;
158 159
    if (attr)
    {
160
        ret = NTDLL_create_struct_sd( attr->SecurityDescriptor, &sd, &objattr.sd_len );
161 162 163
        if (ret != STATUS_SUCCESS) return ret;
    }

164
    SERVER_START_REQ( create_semaphore )
165
    {
166
        req->access  = access;
167
        req->attributes = (attr) ? attr->Attributes : 0;
168 169
        req->initial = InitialCount;
        req->max     = MaximumCount;
170 171
        wine_server_add_data( req, &objattr, sizeof(objattr) );
        if (objattr.sd_len) wine_server_add_data( req, sd, objattr.sd_len );
172 173
        if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len );
        ret = wine_server_call( req );
174
        *SemaphoreHandle = wine_server_ptr_handle( reply->handle );
175
    }
176
    SERVER_END_REQ;
177

178
    NTDLL_free_struct_sd( sd );
179

180
    return ret;
Juergen Schmied's avatar
Juergen Schmied committed
181 182 183
}

/******************************************************************************
184
 *  NtOpenSemaphore (NTDLL.@)
Juergen Schmied's avatar
Juergen Schmied committed
185
 */
186 187 188
NTSTATUS WINAPI NtOpenSemaphore( OUT PHANDLE SemaphoreHandle,
                                 IN ACCESS_MASK access,
                                 IN const OBJECT_ATTRIBUTES *attr )
Juergen Schmied's avatar
Juergen Schmied committed
189
{
190
    DWORD len = attr && attr->ObjectName ? attr->ObjectName->Length : 0;
191 192
    NTSTATUS ret;

193 194
    if (len >= MAX_PATH * sizeof(WCHAR)) return STATUS_NAME_TOO_LONG;

195
    SERVER_START_REQ( open_semaphore )
196 197
    {
        req->access  = access;
198
        req->attributes = (attr) ? attr->Attributes : 0;
199
        req->rootdir = wine_server_obj_handle( attr ? attr->RootDirectory : 0 );
200 201
        if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len );
        ret = wine_server_call( req );
202
        *SemaphoreHandle = wine_server_ptr_handle( reply->handle );
203
    }
204
    SERVER_END_REQ;
205
    return ret;
Juergen Schmied's avatar
Juergen Schmied committed
206 207 208
}

/******************************************************************************
209
 *  NtQuerySemaphore (NTDLL.@)
Juergen Schmied's avatar
Juergen Schmied committed
210 211 212
 */
NTSTATUS WINAPI NtQuerySemaphore(
	HANDLE SemaphoreHandle,
213 214
	SEMAPHORE_INFORMATION_CLASS SemaphoreInformationClass,
	PVOID SemaphoreInformation,
Juergen Schmied's avatar
Juergen Schmied committed
215
	ULONG Length,
216
	PULONG ReturnLength)
Juergen Schmied's avatar
Juergen Schmied committed
217
{
218
	FIXME("(%p,%d,%p,0x%08x,%p) stub!\n",
Juergen Schmied's avatar
Juergen Schmied committed
219
	SemaphoreHandle, SemaphoreInformationClass, SemaphoreInformation, Length, ReturnLength);
220
	return STATUS_SUCCESS;
Juergen Schmied's avatar
Juergen Schmied committed
221
}
222

Juergen Schmied's avatar
Juergen Schmied committed
223
/******************************************************************************
224
 *  NtReleaseSemaphore (NTDLL.@)
Juergen Schmied's avatar
Juergen Schmied committed
225
 */
226
NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, PULONG previous )
Juergen Schmied's avatar
Juergen Schmied committed
227
{
228
    NTSTATUS ret;
229
    SERVER_START_REQ( release_semaphore )
230
    {
231
        req->handle = wine_server_obj_handle( handle );
232
        req->count  = count;
233
        if (!(ret = wine_server_call( req )))
234
        {
235
            if (previous) *previous = reply->prev_count;
236
        }
237
    }
238
    SERVER_END_REQ;
239
    return ret;
Juergen Schmied's avatar
Juergen Schmied committed
240 241 242
}

/*
243
 *	Events
Juergen Schmied's avatar
Juergen Schmied committed
244
 */
245

Juergen Schmied's avatar
Juergen Schmied committed
246
/**************************************************************************
247
 * NtCreateEvent (NTDLL.@)
Patrik Stridvall's avatar
Patrik Stridvall committed
248
 * ZwCreateEvent (NTDLL.@)
Juergen Schmied's avatar
Juergen Schmied committed
249
 */
250 251
NTSTATUS WINAPI NtCreateEvent( PHANDLE EventHandle, ACCESS_MASK DesiredAccess,
                               const OBJECT_ATTRIBUTES *attr, EVENT_TYPE type, BOOLEAN InitialState)
Juergen Schmied's avatar
Juergen Schmied committed
252
{
253
    DWORD len = attr && attr->ObjectName ? attr->ObjectName->Length : 0;
254
    NTSTATUS ret;
255 256
    struct security_descriptor *sd = NULL;
    struct object_attributes objattr;
257

258 259
    if (len >= MAX_PATH * sizeof(WCHAR)) return STATUS_NAME_TOO_LONG;

260
    objattr.rootdir = wine_server_obj_handle( attr ? attr->RootDirectory : 0 );
261
    objattr.sd_len = 0;
262
    objattr.name_len = len;
263 264
    if (attr)
    {
265
        ret = NTDLL_create_struct_sd( attr->SecurityDescriptor, &sd, &objattr.sd_len );
266 267 268
        if (ret != STATUS_SUCCESS) return ret;
    }

269
    SERVER_START_REQ( create_event )
270
    {
271
        req->access = DesiredAccess;
272
        req->attributes = (attr) ? attr->Attributes : 0;
273
        req->manual_reset = (type == NotificationEvent);
274
        req->initial_state = InitialState;
275 276
        wine_server_add_data( req, &objattr, sizeof(objattr) );
        if (objattr.sd_len) wine_server_add_data( req, sd, objattr.sd_len );
277 278
        if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len );
        ret = wine_server_call( req );
279
        *EventHandle = wine_server_ptr_handle( reply->handle );
280
    }
281
    SERVER_END_REQ;
282

283
    NTDLL_free_struct_sd( sd );
284

285
    return ret;
Juergen Schmied's avatar
Juergen Schmied committed
286 287 288
}

/******************************************************************************
289
 *  NtOpenEvent (NTDLL.@)
Patrik Stridvall's avatar
Patrik Stridvall committed
290
 *  ZwOpenEvent (NTDLL.@)
Juergen Schmied's avatar
Juergen Schmied committed
291 292 293 294
 */
NTSTATUS WINAPI NtOpenEvent(
	OUT PHANDLE EventHandle,
	IN ACCESS_MASK DesiredAccess,
295
	IN const OBJECT_ATTRIBUTES *attr )
Juergen Schmied's avatar
Juergen Schmied committed
296
{
297
    DWORD len = attr && attr->ObjectName ? attr->ObjectName->Length : 0;
298 299
    NTSTATUS ret;

300 301
    if (len >= MAX_PATH * sizeof(WCHAR)) return STATUS_NAME_TOO_LONG;

302
    SERVER_START_REQ( open_event )
303 304
    {
        req->access  = DesiredAccess;
305
        req->attributes = (attr) ? attr->Attributes : 0;
306
        req->rootdir = wine_server_obj_handle( attr ? attr->RootDirectory : 0 );
307 308
        if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len );
        ret = wine_server_call( req );
309
        *EventHandle = wine_server_ptr_handle( reply->handle );
310
    }
311
    SERVER_END_REQ;
312
    return ret;
313 314
}

Juergen Schmied's avatar
Juergen Schmied committed
315 316

/******************************************************************************
317
 *  NtSetEvent (NTDLL.@)
Patrik Stridvall's avatar
Patrik Stridvall committed
318
 *  ZwSetEvent (NTDLL.@)
Juergen Schmied's avatar
Juergen Schmied committed
319
 */
320
NTSTATUS WINAPI NtSetEvent( HANDLE handle, PULONG NumberOfThreadsReleased )
Juergen Schmied's avatar
Juergen Schmied committed
321
{
322
    NTSTATUS ret;
323 324 325

    /* FIXME: set NumberOfThreadsReleased */

326
    SERVER_START_REQ( event_op )
327
    {
328
        req->handle = wine_server_obj_handle( handle );
329
        req->op     = SET_EVENT;
330
        ret = wine_server_call( req );
331 332 333
    }
    SERVER_END_REQ;
    return ret;
Juergen Schmied's avatar
Juergen Schmied committed
334 335
}

336
/******************************************************************************
337
 *  NtResetEvent (NTDLL.@)
338
 */
339
NTSTATUS WINAPI NtResetEvent( HANDLE handle, PULONG NumberOfThreadsReleased )
340
{
341 342 343 344 345
    NTSTATUS ret;

    /* resetting an event can't release any thread... */
    if (NumberOfThreadsReleased) *NumberOfThreadsReleased = 0;

346
    SERVER_START_REQ( event_op )
347
    {
348
        req->handle = wine_server_obj_handle( handle );
349
        req->op     = RESET_EVENT;
350
        ret = wine_server_call( req );
351 352 353
    }
    SERVER_END_REQ;
    return ret;
354 355 356
}

/******************************************************************************
357
 *  NtClearEvent (NTDLL.@)
358 359 360 361
 *
 * FIXME
 *   same as NtResetEvent ???
 */
362
NTSTATUS WINAPI NtClearEvent ( HANDLE handle )
363
{
364
    return NtResetEvent( handle, NULL );
365 366 367
}

/******************************************************************************
368
 *  NtPulseEvent (NTDLL.@)
369 370 371 372
 *
 * FIXME
 *   PulseCount
 */
373
NTSTATUS WINAPI NtPulseEvent( HANDLE handle, PULONG PulseCount )
374
{
375
    NTSTATUS ret;
376 377

    if (PulseCount)
378
      FIXME("(%p,%d)\n", handle, *PulseCount);
379

380
    SERVER_START_REQ( event_op )
381
    {
382
        req->handle = wine_server_obj_handle( handle );
383
        req->op     = PULSE_EVENT;
384
        ret = wine_server_call( req );
385 386 387
    }
    SERVER_END_REQ;
    return ret;
388 389 390
}

/******************************************************************************
391
 *  NtQueryEvent (NTDLL.@)
392 393 394
 */
NTSTATUS WINAPI NtQueryEvent (
	IN  HANDLE EventHandle,
395
	IN  EVENT_INFORMATION_CLASS EventInformationClass,
396 397 398 399
	OUT PVOID EventInformation,
	IN  ULONG EventInformationLength,
	OUT PULONG  ReturnLength)
{
Patrik Stridvall's avatar
Patrik Stridvall committed
400
	FIXME("(%p)\n", EventHandle);
401
	return STATUS_NOT_IMPLEMENTED;
402
}
403

404 405 406 407 408 409 410 411 412 413 414 415 416
/*
 *	Mutants (known as Mutexes in Kernel32)
 */

/******************************************************************************
 *              NtCreateMutant                          [NTDLL.@]
 *              ZwCreateMutant                          [NTDLL.@]
 */
NTSTATUS WINAPI NtCreateMutant(OUT HANDLE* MutantHandle,
                               IN ACCESS_MASK access,
                               IN const OBJECT_ATTRIBUTES* attr OPTIONAL,
                               IN BOOLEAN InitialOwner)
{
417 418 419 420
    NTSTATUS status;
    DWORD len = attr && attr->ObjectName ? attr->ObjectName->Length : 0;
    struct security_descriptor *sd = NULL;
    struct object_attributes objattr;
421 422 423

    if (len >= MAX_PATH * sizeof(WCHAR)) return STATUS_NAME_TOO_LONG;

424
    objattr.rootdir = wine_server_obj_handle( attr ? attr->RootDirectory : 0 );
425
    objattr.sd_len = 0;
426
    objattr.name_len = len;
427 428
    if (attr)
    {
429
        status = NTDLL_create_struct_sd( attr->SecurityDescriptor, &sd, &objattr.sd_len );
430 431 432
        if (status != STATUS_SUCCESS) return status;
    }

433 434 435
    SERVER_START_REQ( create_mutex )
    {
        req->access  = access;
436
        req->attributes = (attr) ? attr->Attributes : 0;
437
        req->owned   = InitialOwner;
438 439
        wine_server_add_data( req, &objattr, sizeof(objattr) );
        if (objattr.sd_len) wine_server_add_data( req, sd, objattr.sd_len );
440 441
        if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len );
        status = wine_server_call( req );
442
        *MutantHandle = wine_server_ptr_handle( reply->handle );
443 444
    }
    SERVER_END_REQ;
445

446
    NTDLL_free_struct_sd( sd );
447

448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466
    return status;
}

/**************************************************************************
 *		NtOpenMutant				[NTDLL.@]
 *		ZwOpenMutant				[NTDLL.@]
 */
NTSTATUS WINAPI NtOpenMutant(OUT HANDLE* MutantHandle, 
                             IN ACCESS_MASK access, 
                             IN const OBJECT_ATTRIBUTES* attr )
{
    NTSTATUS    status;
    DWORD       len = attr && attr->ObjectName ? attr->ObjectName->Length : 0;

    if (len >= MAX_PATH * sizeof(WCHAR)) return STATUS_NAME_TOO_LONG;

    SERVER_START_REQ( open_mutex )
    {
        req->access  = access;
467
        req->attributes = (attr) ? attr->Attributes : 0;
468
        req->rootdir = wine_server_obj_handle( attr ? attr->RootDirectory : 0 );
469 470
        if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len );
        status = wine_server_call( req );
471
        *MutantHandle = wine_server_ptr_handle( reply->handle );
472 473 474 475 476 477 478 479 480 481 482 483 484 485 486
    }
    SERVER_END_REQ;
    return status;
}

/**************************************************************************
 *		NtReleaseMutant				[NTDLL.@]
 *		ZwReleaseMutant				[NTDLL.@]
 */
NTSTATUS WINAPI NtReleaseMutant( IN HANDLE handle, OUT PLONG prev_count OPTIONAL)
{
    NTSTATUS    status;

    SERVER_START_REQ( release_mutex )
    {
487
        req->handle = wine_server_obj_handle( handle );
488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504
        status = wine_server_call( req );
        if (prev_count) *prev_count = reply->prev_count;
    }
    SERVER_END_REQ;
    return status;
}

/******************************************************************
 *		NtQueryMutant                   [NTDLL.@]
 *		ZwQueryMutant                   [NTDLL.@]
 */
NTSTATUS WINAPI NtQueryMutant(IN HANDLE handle, 
                              IN MUTANT_INFORMATION_CLASS MutantInformationClass, 
                              OUT PVOID MutantInformation, 
                              IN ULONG MutantInformationLength, 
                              OUT PULONG ResultLength OPTIONAL )
{
505
    FIXME("(%p %u %p %u %p): stub!\n", 
506 507 508
          handle, MutantInformationClass, MutantInformation, MutantInformationLength, ResultLength);
    return STATUS_NOT_IMPLEMENTED;
}
509

510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585
/*
 *	Jobs
 */

/******************************************************************************
 *              NtCreateJobObject   [NTDLL.@]
 *              ZwCreateJobObject   [NTDLL.@]
 */
NTSTATUS WINAPI NtCreateJobObject( PHANDLE handle, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr )
{
    FIXME( "stub: %p %x %s\n", handle, access, attr ? debugstr_us(attr->ObjectName) : "" );
    *handle = (HANDLE)0xdead;
    return STATUS_SUCCESS;
}

/******************************************************************************
 *              NtOpenJobObject   [NTDLL.@]
 *              ZwOpenJobObject   [NTDLL.@]
 */
NTSTATUS WINAPI NtOpenJobObject( PHANDLE handle, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr )
{
    FIXME( "stub: %p %x %s\n", handle, access, attr ? debugstr_us(attr->ObjectName) : "" );
    return STATUS_NOT_IMPLEMENTED;
}

/******************************************************************************
 *              NtTerminateJobObject   [NTDLL.@]
 *              ZwTerminateJobObject   [NTDLL.@]
 */
NTSTATUS WINAPI NtTerminateJobObject( HANDLE handle, NTSTATUS status )
{
    FIXME( "stub: %p %x\n", handle, status );
    return STATUS_SUCCESS;
}

/******************************************************************************
 *              NtQueryInformationJobObject   [NTDLL.@]
 *              ZwQueryInformationJobObject   [NTDLL.@]
 */
NTSTATUS WINAPI NtQueryInformationJobObject( HANDLE handle, JOBOBJECTINFOCLASS class, PVOID info,
                                             ULONG len, PULONG ret_len )
{
    FIXME( "stub: %p %u %p %u %p\n", handle, class, info, len, ret_len );
    return STATUS_NOT_IMPLEMENTED;
}

/******************************************************************************
 *              NtSetInformationJobObject   [NTDLL.@]
 *              ZwSetInformationJobObject   [NTDLL.@]
 */
NTSTATUS WINAPI NtSetInformationJobObject( HANDLE handle, JOBOBJECTINFOCLASS class, PVOID info, ULONG len )
{
    FIXME( "stub: %p %u %p %u\n", handle, class, info, len );
    return STATUS_SUCCESS;
}

/******************************************************************************
 *              NtIsProcessInJob   [NTDLL.@]
 *              ZwIsProcessInJob   [NTDLL.@]
 */
NTSTATUS WINAPI NtIsProcessInJob( HANDLE process, HANDLE job )
{
    FIXME( "stub: %p %p\n", process, job );
    return STATUS_PROCESS_NOT_IN_JOB;
}

/******************************************************************************
 *              NtAssignProcessToJobObject   [NTDLL.@]
 *              ZwAssignProcessToJobObject   [NTDLL.@]
 */
NTSTATUS WINAPI NtAssignProcessToJobObject( HANDLE job, HANDLE process )
{
    FIXME( "stub: %p %p\n", job, process );
    return STATUS_SUCCESS;
}

586 587 588 589 590 591 592 593 594 595
/*
 *	Timers
 */

/**************************************************************************
 *		NtCreateTimer				[NTDLL.@]
 *		ZwCreateTimer				[NTDLL.@]
 */
NTSTATUS WINAPI NtCreateTimer(OUT HANDLE *handle,
                              IN ACCESS_MASK access,
596
                              IN const OBJECT_ATTRIBUTES *attr OPTIONAL,
597 598
                              IN TIMER_TYPE timer_type)
{
599
    DWORD       len = (attr && attr->ObjectName) ? attr->ObjectName->Length : 0;
600 601
    NTSTATUS    status;

602 603
    if (len >= MAX_PATH * sizeof(WCHAR)) return STATUS_NAME_TOO_LONG;

604 605 606 607 608
    if (timer_type != NotificationTimer && timer_type != SynchronizationTimer)
        return STATUS_INVALID_PARAMETER;

    SERVER_START_REQ( create_timer )
    {
609
        req->access  = access;
610
        req->attributes = (attr) ? attr->Attributes : 0;
611
        req->rootdir = wine_server_obj_handle( attr ? attr->RootDirectory : 0 );
612
        req->manual  = (timer_type == NotificationTimer) ? TRUE : FALSE;
613
        if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len );
614
        status = wine_server_call( req );
615
        *handle = wine_server_ptr_handle( reply->handle );
616 617 618 619 620 621 622 623 624 625 626 627
    }
    SERVER_END_REQ;
    return status;

}

/**************************************************************************
 *		NtOpenTimer				[NTDLL.@]
 *		ZwOpenTimer				[NTDLL.@]
 */
NTSTATUS WINAPI NtOpenTimer(OUT PHANDLE handle,
                            IN ACCESS_MASK access,
628
                            IN const OBJECT_ATTRIBUTES* attr )
629
{
630 631
    DWORD       len = (attr && attr->ObjectName) ? attr->ObjectName->Length : 0;
    NTSTATUS    status;
632

633
    if (len >= MAX_PATH * sizeof(WCHAR)) return STATUS_NAME_TOO_LONG;
634 635 636 637

    SERVER_START_REQ( open_timer )
    {
        req->access  = access;
638
        req->attributes = (attr) ? attr->Attributes : 0;
639
        req->rootdir = wine_server_obj_handle( attr ? attr->RootDirectory : 0 );
640
        if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len );
641
        status = wine_server_call( req );
642
        *handle = wine_server_ptr_handle( reply->handle );
643 644 645 646 647 648 649 650 651 652 653
    }
    SERVER_END_REQ;
    return status;
}

/**************************************************************************
 *		NtSetTimer				[NTDLL.@]
 *		ZwSetTimer				[NTDLL.@]
 */
NTSTATUS WINAPI NtSetTimer(IN HANDLE handle,
                           IN const LARGE_INTEGER* when,
654
                           IN PTIMER_APC_ROUTINE callback,
655 656 657 658 659 660 661
                           IN PVOID callback_arg,
                           IN BOOLEAN resume,
                           IN ULONG period OPTIONAL,
                           OUT PBOOLEAN state OPTIONAL)
{
    NTSTATUS    status = STATUS_SUCCESS;

662
    TRACE("(%p,%p,%p,%p,%08x,0x%08x,%p) stub\n",
663 664 665 666
          handle, when, callback, callback_arg, resume, period, state);

    SERVER_START_REQ( set_timer )
    {
667
        req->handle   = wine_server_obj_handle( handle );
668
        req->period   = period;
669
        req->expire   = when->QuadPart;
670 671
        req->callback = wine_server_client_ptr( callback );
        req->arg      = wine_server_client_ptr( callback_arg );
672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691
        status = wine_server_call( req );
        if (state) *state = reply->signaled;
    }
    SERVER_END_REQ;

    /* set error but can still succeed */
    if (resume && status == STATUS_SUCCESS) return STATUS_TIMER_RESUME_IGNORED;
    return status;
}

/**************************************************************************
 *		NtCancelTimer				[NTDLL.@]
 *		ZwCancelTimer				[NTDLL.@]
 */
NTSTATUS WINAPI NtCancelTimer(IN HANDLE handle, OUT BOOLEAN* state)
{
    NTSTATUS    status;

    SERVER_START_REQ( cancel_timer )
    {
692
        req->handle = wine_server_obj_handle( handle );
693 694 695 696 697 698 699
        status = wine_server_call( req );
        if (state) *state = reply->signaled;
    }
    SERVER_END_REQ;
    return status;
}

700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726
/******************************************************************************
 *  NtQueryTimer (NTDLL.@)
 *
 * Retrieves information about a timer.
 *
 * PARAMS
 *  TimerHandle           [I] The timer to retrieve information about.
 *  TimerInformationClass [I] The type of information to retrieve.
 *  TimerInformation      [O] Pointer to buffer to store information in.
 *  Length                [I] The length of the buffer pointed to by TimerInformation.
 *  ReturnLength          [O] Optional. The size of buffer actually used.
 *
 * RETURNS
 *  Success: STATUS_SUCCESS
 *  Failure: STATUS_INFO_LENGTH_MISMATCH, if Length doesn't match the required data
 *           size for the class specified.
 *           STATUS_INVALID_INFO_CLASS, if an invalid TimerInformationClass was specified.
 *           STATUS_ACCESS_DENIED, if TimerHandle does not have TIMER_QUERY_STATE access
 *           to the timer.
 */
NTSTATUS WINAPI NtQueryTimer(
    HANDLE TimerHandle,
    TIMER_INFORMATION_CLASS TimerInformationClass,
    PVOID TimerInformation,
    ULONG Length,
    PULONG ReturnLength)
{
727
    TIMER_BASIC_INFORMATION * basic_info = TimerInformation;
728 729 730
    NTSTATUS status;
    LARGE_INTEGER now;

731
    TRACE("(%p,%d,%p,0x%08x,%p)\n", TimerHandle, TimerInformationClass,
732 733 734 735 736 737 738 739 740 741
       TimerInformation, Length, ReturnLength);

    switch (TimerInformationClass)
    {
    case TimerBasicInformation:
        if (Length < sizeof(TIMER_BASIC_INFORMATION))
            return STATUS_INFO_LENGTH_MISMATCH;

        SERVER_START_REQ(get_timer_info)
        {
742
            req->handle = wine_server_obj_handle( TimerHandle );
743 744 745
            status = wine_server_call(req);

            /* convert server time to absolute NTDLL time */
746
            basic_info->RemainingTime.QuadPart = reply->when;
747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767
            basic_info->TimerState = reply->signaled;
        }
        SERVER_END_REQ;

        /* convert from absolute into relative time */
        NtQuerySystemTime(&now);
        if (now.QuadPart > basic_info->RemainingTime.QuadPart)
            basic_info->RemainingTime.QuadPart = 0;
        else
            basic_info->RemainingTime.QuadPart -= now.QuadPart;

        if (ReturnLength) *ReturnLength = sizeof(TIMER_BASIC_INFORMATION);

        return status;
    }

    FIXME("Unhandled class %d\n", TimerInformationClass);
    return STATUS_INVALID_INFO_CLASS;
}


768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787
/******************************************************************************
 * NtQueryTimerResolution [NTDLL.@]
 */
NTSTATUS WINAPI NtQueryTimerResolution(OUT ULONG* min_resolution,
                                       OUT ULONG* max_resolution,
                                       OUT ULONG* current_resolution)
{
    FIXME("(%p,%p,%p), stub!\n",
          min_resolution, max_resolution, current_resolution);

    return STATUS_NOT_IMPLEMENTED;
}

/******************************************************************************
 * NtSetTimerResolution [NTDLL.@]
 */
NTSTATUS WINAPI NtSetTimerResolution(IN ULONG resolution,
                                     IN BOOLEAN set_resolution,
                                     OUT ULONG* current_resolution )
{
788
    FIXME("(%u,%u,%p), stub!\n",
789 790 791 792 793 794
          resolution, set_resolution, current_resolution);

    return STATUS_NOT_IMPLEMENTED;
}


795 796 797 798 799 800 801 802 803 804 805 806
/***********************************************************************
 *              wait_reply
 *
 * Wait for a reply on the waiting pipe of the current thread.
 */
static int wait_reply( void *cookie )
{
    int signaled;
    struct wake_up_reply reply;
    for (;;)
    {
        int ret;
807
        ret = read( ntdll_get_thread_data()->wait_fd[0], &reply, sizeof(reply) );
808 809
        if (ret == sizeof(reply))
        {
810
            if (!reply.cookie) abort_thread( reply.signaled );  /* thread got killed */
811
            if (wine_server_get_ptr(reply.cookie) == cookie) return reply.signaled;
812 813 814 815 816
            /* we stole another reply, wait for the real one */
            signaled = wait_reply( cookie );
            /* and now put the wrong one back in the pipe */
            for (;;)
            {
817
                ret = write( ntdll_get_thread_data()->wait_fd[1], &reply, sizeof(reply) );
818 819 820 821 822 823 824 825 826 827 828 829 830 831
                if (ret == sizeof(reply)) break;
                if (ret >= 0) server_protocol_error( "partial wakeup write %d\n", ret );
                if (errno == EINTR) continue;
                server_protocol_perror("wakeup write");
            }
            return signaled;
        }
        if (ret >= 0) server_protocol_error( "partial wakeup read %d\n", ret );
        if (errno == EINTR) continue;
        server_protocol_perror("wakeup read");
    }
}


832 833 834 835 836 837 838 839
/***********************************************************************
 *              invoke_apc
 *
 * Invoke a single APC. Return TRUE if a user APC has been run.
 */
static BOOL invoke_apc( const apc_call_t *call, apc_result_t *result )
{
    BOOL user_apc = FALSE;
840
    SIZE_T size;
841
    void *addr;
842 843 844 845 846

    memset( result, 0, sizeof(*result) );

    switch (call->type)
    {
847 848
    case APC_NONE:
        break;
849
    case APC_USER:
850 851 852
    {
        void (WINAPI *func)(ULONG_PTR,ULONG_PTR,ULONG_PTR) = wine_server_get_ptr( call->user.func );
        func( call->user.args[0], call->user.args[1], call->user.args[2] );
853 854
        user_apc = TRUE;
        break;
855
    }
856
    case APC_TIMER:
857 858 859 860
    {
        void (WINAPI *func)(void*, unsigned int, unsigned int) = wine_server_get_ptr( call->timer.func );
        func( wine_server_get_ptr( call->timer.arg ),
              (DWORD)call->timer.time, (DWORD)(call->timer.time >> 32) );
861 862
        user_apc = TRUE;
        break;
863
    }
864
    case APC_ASYNC_IO:
865 866
    {
        void *apc = NULL;
867 868
        IO_STATUS_BLOCK *iosb = wine_server_get_ptr( call->async_io.sb );
        NTSTATUS (*func)(void *, IO_STATUS_BLOCK *, NTSTATUS, void **) = wine_server_get_ptr( call->async_io.func );
869
        result->type = call->type;
870 871
        result->async_io.status = func( wine_server_get_ptr( call->async_io.user ),
                                        iosb, call->async_io.status, &apc );
872 873 874
        if (result->async_io.status != STATUS_PENDING)
        {
            result->async_io.total = iosb->Information;
875
            result->async_io.apc   = wine_server_client_ptr( apc );
876
        }
877
        break;
878
    }
879 880
    case APC_VIRTUAL_ALLOC:
        result->type = call->type;
881
        addr = wine_server_get_ptr( call->virtual_alloc.addr );
882
        size = call->virtual_alloc.size;
883
        if ((ULONG_PTR)addr == call->virtual_alloc.addr && size == call->virtual_alloc.size)
884
        {
885 886
            result->virtual_alloc.status = NtAllocateVirtualMemory( NtCurrentProcess(), &addr,
                                                                    call->virtual_alloc.zero_bits, &size,
887 888
                                                                    call->virtual_alloc.op_type,
                                                                    call->virtual_alloc.prot );
889
            result->virtual_alloc.addr = wine_server_client_ptr( addr );
890 891 892
            result->virtual_alloc.size = size;
        }
        else result->virtual_alloc.status = STATUS_WORKING_SET_LIMIT_RANGE;
893 894 895
        break;
    case APC_VIRTUAL_FREE:
        result->type = call->type;
896
        addr = wine_server_get_ptr( call->virtual_free.addr );
897
        size = call->virtual_free.size;
898
        if ((ULONG_PTR)addr == call->virtual_free.addr && size == call->virtual_free.size)
899
        {
900
            result->virtual_free.status = NtFreeVirtualMemory( NtCurrentProcess(), &addr, &size,
901
                                                               call->virtual_free.op_type );
902
            result->virtual_free.addr = wine_server_client_ptr( addr );
903 904 905
            result->virtual_free.size = size;
        }
        else result->virtual_free.status = STATUS_INVALID_PARAMETER;
906 907 908 909 910
        break;
    case APC_VIRTUAL_QUERY:
    {
        MEMORY_BASIC_INFORMATION info;
        result->type = call->type;
911 912 913 914 915 916 917 918
        addr = wine_server_get_ptr( call->virtual_query.addr );
        if ((ULONG_PTR)addr == call->virtual_query.addr)
            result->virtual_query.status = NtQueryVirtualMemory( NtCurrentProcess(),
                                                                 addr, MemoryBasicInformation, &info,
                                                                 sizeof(info), NULL );
        else
            result->virtual_query.status = STATUS_WORKING_SET_LIMIT_RANGE;

919 920
        if (result->virtual_query.status == STATUS_SUCCESS)
        {
921 922
            result->virtual_query.base       = wine_server_client_ptr( info.BaseAddress );
            result->virtual_query.alloc_base = wine_server_client_ptr( info.AllocationBase );
923 924 925
            result->virtual_query.size       = info.RegionSize;
            result->virtual_query.prot       = info.Protect;
            result->virtual_query.alloc_prot = info.AllocationProtect;
926 927
            result->virtual_query.state      = info.State >> 12;
            result->virtual_query.alloc_type = info.Type >> 16;
928 929 930 931 932
        }
        break;
    }
    case APC_VIRTUAL_PROTECT:
        result->type = call->type;
933
        addr = wine_server_get_ptr( call->virtual_protect.addr );
934
        size = call->virtual_protect.size;
935
        if ((ULONG_PTR)addr == call->virtual_protect.addr && size == call->virtual_protect.size)
936
        {
937
            result->virtual_protect.status = NtProtectVirtualMemory( NtCurrentProcess(), &addr, &size,
938 939
                                                                     call->virtual_protect.prot,
                                                                     &result->virtual_protect.prot );
940
            result->virtual_protect.addr = wine_server_client_ptr( addr );
941 942 943
            result->virtual_protect.size = size;
        }
        else result->virtual_protect.status = STATUS_INVALID_PARAMETER;
944 945 946
        break;
    case APC_VIRTUAL_FLUSH:
        result->type = call->type;
947
        addr = wine_server_get_ptr( call->virtual_flush.addr );
948
        size = call->virtual_flush.size;
949
        if ((ULONG_PTR)addr == call->virtual_flush.addr && size == call->virtual_flush.size)
950 951
        {
            result->virtual_flush.status = NtFlushVirtualMemory( NtCurrentProcess(),
952 953
                                                                 (const void **)&addr, &size, 0 );
            result->virtual_flush.addr = wine_server_client_ptr( addr );
954 955 956
            result->virtual_flush.size = size;
        }
        else result->virtual_flush.status = STATUS_INVALID_PARAMETER;
957 958 959
        break;
    case APC_VIRTUAL_LOCK:
        result->type = call->type;
960
        addr = wine_server_get_ptr( call->virtual_lock.addr );
961
        size = call->virtual_lock.size;
962
        if ((ULONG_PTR)addr == call->virtual_lock.addr && size == call->virtual_lock.size)
963
        {
964 965
            result->virtual_lock.status = NtLockVirtualMemory( NtCurrentProcess(), &addr, &size, 0 );
            result->virtual_lock.addr = wine_server_client_ptr( addr );
966 967 968
            result->virtual_lock.size = size;
        }
        else result->virtual_lock.status = STATUS_INVALID_PARAMETER;
969 970 971
        break;
    case APC_VIRTUAL_UNLOCK:
        result->type = call->type;
972
        addr = wine_server_get_ptr( call->virtual_unlock.addr );
973
        size = call->virtual_unlock.size;
974
        if ((ULONG_PTR)addr == call->virtual_unlock.addr && size == call->virtual_unlock.size)
975
        {
976 977
            result->virtual_unlock.status = NtUnlockVirtualMemory( NtCurrentProcess(), &addr, &size, 0 );
            result->virtual_unlock.addr = wine_server_client_ptr( addr );
978 979 980
            result->virtual_unlock.size = size;
        }
        else result->virtual_unlock.status = STATUS_INVALID_PARAMETER;
981 982 983
        break;
    case APC_MAP_VIEW:
        result->type = call->type;
984
        addr = wine_server_get_ptr( call->map_view.addr );
985
        size = call->map_view.size;
986
        if ((ULONG_PTR)addr == call->map_view.addr && size == call->map_view.size)
987
        {
988 989
            LARGE_INTEGER offset;
            offset.QuadPart = call->map_view.offset;
990
            result->map_view.status = NtMapViewOfSection( wine_server_ptr_handle(call->map_view.handle),
991
                                                          NtCurrentProcess(), &addr,
992 993 994
                                                          call->map_view.zero_bits, 0,
                                                          &offset, &size, ViewShare,
                                                          call->map_view.alloc_type, call->map_view.prot );
995 996
            result->map_view.addr = wine_server_client_ptr( addr );
            result->map_view.size = size;
997 998
        }
        else result->map_view.status = STATUS_INVALID_PARAMETER;
999
        NtClose( wine_server_ptr_handle(call->map_view.handle) );
1000 1001 1002
        break;
    case APC_UNMAP_VIEW:
        result->type = call->type;
1003 1004 1005 1006 1007
        addr = wine_server_get_ptr( call->unmap_view.addr );
        if ((ULONG_PTR)addr == call->unmap_view.addr)
            result->unmap_view.status = NtUnmapViewOfSection( NtCurrentProcess(), addr );
        else
            result->unmap_view.status = STATUS_INVALID_PARAMETER;
1008 1009 1010 1011
        break;
    case APC_CREATE_THREAD:
    {
        CLIENT_ID id;
1012
        HANDLE handle;
1013 1014
        SIZE_T reserve = call->create_thread.reserve;
        SIZE_T commit = call->create_thread.commit;
1015 1016
        void *func = wine_server_get_ptr( call->create_thread.func );
        void *arg  = wine_server_get_ptr( call->create_thread.arg );
1017

1018
        result->type = call->type;
1019 1020
        if (reserve == call->create_thread.reserve && commit == call->create_thread.commit &&
            (ULONG_PTR)func == call->create_thread.func && (ULONG_PTR)arg == call->create_thread.arg)
1021 1022 1023
        {
            result->create_thread.status = RtlCreateUserThread( NtCurrentProcess(), NULL,
                                                                call->create_thread.suspend, NULL,
1024
                                                                reserve, commit, func, arg, &handle, &id );
1025 1026 1027 1028
            result->create_thread.handle = wine_server_obj_handle( handle );
            result->create_thread.tid = HandleToULong(id.UniqueThread);
        }
        else result->create_thread.status = STATUS_INVALID_PARAMETER;
1029 1030 1031 1032 1033 1034 1035 1036 1037
        break;
    }
    default:
        server_protocol_error( "get_apc_request: bad type %d\n", call->type );
        break;
    }
    return user_apc;
}

1038 1039 1040 1041 1042 1043 1044 1045 1046
/***********************************************************************
 *           NTDLL_queue_process_apc
 */
NTSTATUS NTDLL_queue_process_apc( HANDLE process, const apc_call_t *call, apc_result_t *result )
{
    for (;;)
    {
        NTSTATUS ret;
        HANDLE handle = 0;
1047
        BOOL self = FALSE;
1048 1049 1050

        SERVER_START_REQ( queue_apc )
        {
1051
            req->handle = wine_server_obj_handle( process );
1052
            req->call = *call;
1053 1054
            if (!(ret = wine_server_call( req )))
            {
1055
                handle = wine_server_ptr_handle( reply->handle );
1056 1057
                self = reply->self;
            }
1058 1059
        }
        SERVER_END_REQ;
1060
        if (ret != STATUS_SUCCESS) return ret;
1061

1062
        if (self)
1063
        {
1064
            invoke_apc( call, result );
1065
        }
1066 1067 1068
        else
        {
            NtWaitForSingleObject( handle, FALSE, NULL );
1069

1070 1071
            SERVER_START_REQ( get_apc_result )
            {
1072
                req->handle = wine_server_obj_handle( handle );
1073 1074 1075 1076 1077 1078 1079
                if (!(ret = wine_server_call( req ))) *result = reply->result;
            }
            SERVER_END_REQ;

            if (!ret && result->type == APC_NONE) continue;  /* APC didn't run, try again */
            if (ret) NtClose( handle );
        }
1080 1081 1082 1083 1084
        return ret;
    }
}


1085 1086 1087 1088
/***********************************************************************
 *              NTDLL_wait_for_multiple_objects
 *
 * Implementation of NtWaitForMultipleObjects
1089
 */
1090
NTSTATUS NTDLL_wait_for_multiple_objects( UINT count, const HANDLE *handles, UINT flags,
1091
                                          const LARGE_INTEGER *timeout, HANDLE signal_object )
1092
{
1093
    NTSTATUS ret;
1094
    int i, cookie;
1095
    BOOL user_apc = FALSE;
1096
    obj_handle_t obj_handles[MAXIMUM_WAIT_OBJECTS];
1097 1098 1099
    obj_handle_t apc_handle = 0;
    apc_call_t call;
    apc_result_t result;
1100
    timeout_t abs_timeout = timeout ? timeout->QuadPart : TIMEOUT_INFINITE;
1101

1102
    memset( &result, 0, sizeof(result) );
1103
    for (i = 0; i < count; i++) obj_handles[i] = wine_server_obj_handle( handles[i] );
1104

1105 1106 1107 1108
    for (;;)
    {
        SERVER_START_REQ( select )
        {
1109
            req->flags    = flags;
1110
            req->cookie   = wine_server_client_ptr( &cookie );
1111
            req->signal   = wine_server_obj_handle( signal_object );
1112 1113 1114
            req->prev_apc = apc_handle;
            req->timeout  = abs_timeout;
            wine_server_add_data( req, &result, sizeof(result) );
1115
            wine_server_add_data( req, obj_handles, count * sizeof(*obj_handles) );
1116
            ret = wine_server_call( req );
1117
            abs_timeout = reply->timeout;
1118 1119
            apc_handle  = reply->apc_handle;
            call        = reply->call;
1120 1121 1122 1123
        }
        SERVER_END_REQ;
        if (ret == STATUS_PENDING) ret = wait_reply( &cookie );
        if (ret != STATUS_USER_APC) break;
1124 1125 1126 1127 1128 1129 1130
        if (invoke_apc( &call, &result ))
        {
            /* if we ran a user apc we have to check once more if an object got signaled,
             * but we don't want to wait */
            abs_timeout = 0;
            user_apc = TRUE;
        }
1131
        signal_object = 0;  /* don't signal it multiple times */
1132
    }
1133

1134 1135
    if (ret == STATUS_TIMEOUT && user_apc) ret = STATUS_USER_APC;

1136 1137 1138
    /* A test on Windows 2000 shows that Windows always yields during
       a wait, but a wait that is hit by an event gets a priority
       boost as well.  This seems to model that behavior the closest.  */
1139
    if (ret == STATUS_TIMEOUT) NtYieldExecution();
1140

1141 1142 1143 1144
    return ret;
}


1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159
/* wait operations */

/******************************************************************
 *		NtWaitForMultipleObjects (NTDLL.@)
 */
NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles,
                                          BOOLEAN wait_all, BOOLEAN alertable,
                                          const LARGE_INTEGER *timeout )
{
    UINT flags = SELECT_INTERRUPTIBLE;

    if (!count || count > MAXIMUM_WAIT_OBJECTS) return STATUS_INVALID_PARAMETER_1;

    if (wait_all) flags |= SELECT_ALL;
    if (alertable) flags |= SELECT_ALERTABLE;
1160
    return NTDLL_wait_for_multiple_objects( count, handles, flags, timeout, 0 );
1161 1162 1163
}


1164 1165 1166
/******************************************************************
 *		NtWaitForSingleObject (NTDLL.@)
 */
1167
NTSTATUS WINAPI NtWaitForSingleObject(HANDLE handle, BOOLEAN alertable, const LARGE_INTEGER *timeout )
1168 1169 1170
{
    return NtWaitForMultipleObjects( 1, &handle, FALSE, alertable, timeout );
}
1171 1172


1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186
/******************************************************************
 *		NtSignalAndWaitForSingleObject (NTDLL.@)
 */
NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE hSignalObject, HANDLE hWaitObject,
                                                BOOLEAN alertable, const LARGE_INTEGER *timeout )
{
    UINT flags = SELECT_INTERRUPTIBLE;

    if (!hSignalObject) return STATUS_INVALID_HANDLE;
    if (alertable) flags |= SELECT_ALERTABLE;
    return NTDLL_wait_for_multiple_objects( 1, &hWaitObject, flags, timeout, hSignalObject );
}


1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200
/******************************************************************
 *		NtYieldExecution (NTDLL.@)
 */
NTSTATUS WINAPI NtYieldExecution(void)
{
#ifdef HAVE_SCHED_YIELD
    sched_yield();
    return STATUS_SUCCESS;
#else
    return STATUS_NO_YIELD_PERFORMED;
#endif
}


1201 1202 1203 1204 1205
/******************************************************************
 *		NtDelayExecution (NTDLL.@)
 */
NTSTATUS WINAPI NtDelayExecution( BOOLEAN alertable, const LARGE_INTEGER *timeout )
{
1206 1207 1208 1209
    /* if alertable, we need to query the server */
    if (alertable)
        return NTDLL_wait_for_multiple_objects( 0, NULL, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE,
                                                timeout, 0 );
1210

1211
    if (!timeout || timeout->QuadPart == TIMEOUT_INFINITE)  /* sleep forever */
1212 1213 1214 1215 1216
    {
        for (;;) select( 0, NULL, NULL, NULL, NULL );
    }
    else
    {
1217 1218
        LARGE_INTEGER now;
        timeout_t when, diff;
1219

1220 1221 1222 1223 1224
        if ((when = timeout->QuadPart) < 0)
        {
            NtQuerySystemTime( &now );
            when = now.QuadPart - when;
        }
1225 1226 1227

        /* Note that we yield after establishing the desired timeout */
        NtYieldExecution();
1228
        if (!when) return STATUS_SUCCESS;
1229

1230 1231 1232
        for (;;)
        {
            struct timeval tv;
1233 1234 1235 1236 1237
            NtQuerySystemTime( &now );
            diff = (when - now.QuadPart + 9) / 10;
            if (diff <= 0) break;
            tv.tv_sec  = diff / 1000000;
            tv.tv_usec = diff % 1000000;
1238 1239 1240 1241 1242
            if (select( 0, NULL, NULL, NULL, &tv ) != -1) break;
        }
    }
    return STATUS_SUCCESS;
}
1243

1244
/******************************************************************
1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255
 *              NtCreateIoCompletion (NTDLL.@)
 *              ZwCreateIoCompletion (NTDLL.@)
 *
 * Creates I/O completion object.
 *
 * PARAMS
 *      CompletionPort            [O] created completion object handle will be placed there
 *      DesiredAccess             [I] desired access to a handle (combination of IO_COMPLETION_*)
 *      ObjectAttributes          [I] completion object attributes
 *      NumberOfConcurrentThreads [I] desired number of concurrent active worker threads
 *
1256
 */
1257 1258 1259
NTSTATUS WINAPI NtCreateIoCompletion( PHANDLE CompletionPort, ACCESS_MASK DesiredAccess,
                                      POBJECT_ATTRIBUTES ObjectAttributes, ULONG NumberOfConcurrentThreads )
{
1260 1261 1262
    NTSTATUS status;

    TRACE("(%p, %x, %p, %d)\n", CompletionPort, DesiredAccess,
1263
          ObjectAttributes, NumberOfConcurrentThreads);
1264 1265 1266 1267 1268 1269 1270 1271

    if (!CompletionPort)
        return STATUS_INVALID_PARAMETER;

    SERVER_START_REQ( create_completion )
    {
        req->access     = DesiredAccess;
        req->attributes = ObjectAttributes ? ObjectAttributes->Attributes : 0;
1272
        req->rootdir    = wine_server_obj_handle( ObjectAttributes ? ObjectAttributes->RootDirectory : 0 );
1273 1274 1275 1276 1277
        req->concurrent = NumberOfConcurrentThreads;
        if (ObjectAttributes && ObjectAttributes->ObjectName)
            wine_server_add_data( req, ObjectAttributes->ObjectName->Buffer,
                                       ObjectAttributes->ObjectName->Length );
        if (!(status = wine_server_call( req )))
1278
            *CompletionPort = wine_server_ptr_handle( reply->handle );
1279 1280 1281
    }
    SERVER_END_REQ;
    return status;
1282 1283
}

1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296
/******************************************************************
 *              NtSetIoCompletion (NTDLL.@)
 *              ZwSetIoCompletion (NTDLL.@)
 *
 * Inserts completion message into queue
 *
 * PARAMS
 *      CompletionPort           [I] HANDLE to completion object
 *      CompletionKey            [I] completion key
 *      CompletionValue          [I] completion value (usually pointer to OVERLAPPED)
 *      Status                   [I] operation status
 *      NumberOfBytesTransferred [I] number of bytes transferred
 */
1297
NTSTATUS WINAPI NtSetIoCompletion( HANDLE CompletionPort, ULONG_PTR CompletionKey,
1298
                                   ULONG_PTR CompletionValue, NTSTATUS Status,
1299
                                   ULONG NumberOfBytesTransferred )
1300
{
1301 1302 1303 1304 1305 1306 1307
    NTSTATUS status;

    TRACE("(%p, %lx, %lx, %x, %d)\n", CompletionPort, CompletionKey,
          CompletionValue, Status, NumberOfBytesTransferred);

    SERVER_START_REQ( add_completion )
    {
1308
        req->handle      = wine_server_obj_handle( CompletionPort );
1309 1310 1311 1312 1313 1314 1315 1316
        req->ckey        = CompletionKey;
        req->cvalue      = CompletionValue;
        req->status      = Status;
        req->information = NumberOfBytesTransferred;
        status = wine_server_call( req );
    }
    SERVER_END_REQ;
    return status;
1317 1318
}

1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332
/******************************************************************
 *              NtRemoveIoCompletion (NTDLL.@)
 *              ZwRemoveIoCompletion (NTDLL.@)
 *
 * (Wait for and) retrieve first completion message from completion object's queue
 *
 * PARAMS
 *      CompletionPort  [I] HANDLE to I/O completion object
 *      CompletionKey   [O] completion key
 *      CompletionValue [O] Completion value given in NtSetIoCompletion or in async operation
 *      iosb            [O] IO_STATUS_BLOCK of completed asynchronous operation
 *      WaitTime        [I] optional wait time in NTDLL format
 *
 */
1333
NTSTATUS WINAPI NtRemoveIoCompletion( HANDLE CompletionPort, PULONG_PTR CompletionKey,
1334
                                      PULONG_PTR CompletionValue, PIO_STATUS_BLOCK iosb,
1335 1336
                                      PLARGE_INTEGER WaitTime )
{
1337 1338 1339
    NTSTATUS status;

    TRACE("(%p, %p, %p, %p, %p)\n", CompletionPort, CompletionKey,
1340
          CompletionValue, iosb, WaitTime);
1341 1342 1343 1344 1345

    for(;;)
    {
        SERVER_START_REQ( remove_completion )
        {
1346
            req->handle = wine_server_obj_handle( CompletionPort );
1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361
            if (!(status = wine_server_call( req )))
            {
                *CompletionKey    = reply->ckey;
                *CompletionValue  = reply->cvalue;
                iosb->Information = reply->information;
                iosb->u.Status    = reply->status;
            }
        }
        SERVER_END_REQ;
        if (status != STATUS_PENDING) break;

        status = NtWaitForSingleObject( CompletionPort, FALSE, WaitTime );
        if (status != WAIT_OBJECT_0) break;
    }
    return status;
1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378
}

/******************************************************************
 *              NtOpenIoCompletion (NTDLL.@)
 *              ZwOpenIoCompletion (NTDLL.@)
 *
 * Opens I/O completion object
 *
 * PARAMS
 *      CompletionPort     [O] completion object handle will be placed there
 *      DesiredAccess      [I] desired access to a handle (combination of IO_COMPLETION_*)
 *      ObjectAttributes   [I] completion object name
 *
 */
NTSTATUS WINAPI NtOpenIoCompletion( PHANDLE CompletionPort, ACCESS_MASK DesiredAccess,
                                    POBJECT_ATTRIBUTES ObjectAttributes )
{
1379 1380 1381 1382 1383 1384 1385 1386 1387 1388
    NTSTATUS status;

    TRACE("(%p, 0x%x, %p)\n", CompletionPort, DesiredAccess, ObjectAttributes);

    if (!CompletionPort || !ObjectAttributes || !ObjectAttributes->ObjectName)
        return STATUS_INVALID_PARAMETER;

    SERVER_START_REQ( open_completion )
    {
        req->access     = DesiredAccess;
1389
        req->rootdir    = wine_server_obj_handle( ObjectAttributes->RootDirectory );
1390 1391 1392
        wine_server_add_data( req, ObjectAttributes->ObjectName->Buffer,
                                   ObjectAttributes->ObjectName->Length );
        if (!(status = wine_server_call( req )))
1393
            *CompletionPort = wine_server_ptr_handle( reply->handle );
1394 1395 1396
    }
    SERVER_END_REQ;
    return status;
1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415
}

/******************************************************************
 *              NtQueryIoCompletion (NTDLL.@)
 *              ZwQueryIoCompletion (NTDLL.@)
 *
 * Requests information about given I/O completion object
 *
 * PARAMS
 *      CompletionPort        [I] HANDLE to completion port to request
 *      InformationClass      [I] information class
 *      CompletionInformation [O] user-provided buffer for data
 *      BufferLength          [I] buffer length
 *      RequiredLength        [O] required buffer length
 *
 */
NTSTATUS WINAPI NtQueryIoCompletion( HANDLE CompletionPort, IO_COMPLETION_INFORMATION_CLASS InformationClass,
                                     PVOID CompletionInformation, ULONG BufferLength, PULONG RequiredLength )
{
1416 1417 1418 1419 1420 1421 1422 1423 1424 1425
    NTSTATUS status;

    TRACE("(%p, %d, %p, 0x%x, %p)\n", CompletionPort, InformationClass, CompletionInformation,
          BufferLength, RequiredLength);

    if (!CompletionInformation) return STATUS_INVALID_PARAMETER;
    switch( InformationClass )
    {
        case IoCompletionBasicInformation:
            {
1426
                ULONG *info = CompletionInformation;
1427 1428 1429 1430 1431 1432 1433 1434

                if (RequiredLength) *RequiredLength = sizeof(*info);
                if (BufferLength != sizeof(*info))
                    status = STATUS_INFO_LENGTH_MISMATCH;
                else
                {
                    SERVER_START_REQ( query_completion )
                    {
1435
                        req->handle = wine_server_obj_handle( CompletionPort );
1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447
                        if (!(status = wine_server_call( req )))
                            *info = reply->depth;
                    }
                    SERVER_END_REQ;
                }
            }
            break;
        default:
            status = STATUS_INVALID_PARAMETER;
            break;
    }
    return status;
1448
}
1449

1450 1451
NTSTATUS NTDLL_AddCompletion( HANDLE hFile, ULONG_PTR CompletionValue,
                              NTSTATUS CompletionStatus, ULONG Information )
1452 1453 1454 1455 1456
{
    NTSTATUS status;

    SERVER_START_REQ( add_fd_completion )
    {
1457
        req->handle      = wine_server_obj_handle( hFile );
1458 1459 1460 1461 1462 1463 1464 1465
        req->cvalue      = CompletionValue;
        req->status      = CompletionStatus;
        req->information = Information;
        status = wine_server_call( req );
    }
    SERVER_END_REQ;
    return status;
}
1466 1467 1468 1469 1470

VOID NTAPI RtlRunOnceInitialize(PRTL_RUN_ONCE initonce)
{
    initonce->Ptr = NULL;
}