sync.c 35.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * Synchronization tests
 *
 * Copyright 2005 Mike McCormack for CodeWeavers
 *
 * 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
18
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 20
 */

21
#define _WIN32_WINNT 0x500
22 23 24 25 26 27 28 29
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <windef.h>
#include <winbase.h>

#include "wine/test.h"

30
static BOOL   (WINAPI *pChangeTimerQueueTimer)(HANDLE, HANDLE, ULONG, ULONG);
31 32 33
static HANDLE (WINAPI *pCreateTimerQueue)(void);
static BOOL   (WINAPI *pCreateTimerQueueTimer)(PHANDLE, HANDLE, WAITORTIMERCALLBACK,
                                               PVOID, DWORD, DWORD, ULONG);
34
static HANDLE (WINAPI *pCreateWaitableTimerA)(SECURITY_ATTRIBUTES*,BOOL,LPCSTR);
35
static BOOL   (WINAPI *pDeleteTimerQueueEx)(HANDLE, HANDLE);
36
static BOOL   (WINAPI *pDeleteTimerQueueTimer)(HANDLE, HANDLE, HANDLE);
37 38
static HANDLE (WINAPI *pOpenWaitableTimerA)(DWORD,BOOL,LPCSTR);

39 40 41 42 43
static void test_signalandwait(void)
{
    DWORD (WINAPI *pSignalObjectAndWait)(HANDLE, HANDLE, DWORD, BOOL);
    HMODULE kernel32;
    DWORD r;
44 45
    int i;
    HANDLE event[2], maxevents[MAXIMUM_WAIT_OBJECTS], semaphore[2], file;
46 47 48 49 50 51 52

    kernel32 = GetModuleHandle("kernel32");
    pSignalObjectAndWait = (void*) GetProcAddress(kernel32, "SignalObjectAndWait");

    if (!pSignalObjectAndWait)
        return;

53 54 55 56
    /* invalid parameters */
    r = pSignalObjectAndWait(NULL, NULL, 0, 0);
    if (r == ERROR_INVALID_FUNCTION)
    {
57
        win_skip("SignalObjectAndWait is not implemented\n");
58 59 60 61
        return; /* Win98/ME */
    }
    ok( r == WAIT_FAILED, "should fail\n");

62 63 64 65 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
    event[0] = CreateEvent(NULL, 0, 0, NULL);
    event[1] = CreateEvent(NULL, 1, 1, NULL);

    ok( event[0] && event[1], "failed to create event flags\n");

    r = pSignalObjectAndWait(event[0], NULL, 0, FALSE);
    ok( r == WAIT_FAILED, "should fail\n");

    r = pSignalObjectAndWait(NULL, event[0], 0, FALSE);
    ok( r == WAIT_FAILED, "should fail\n");


    /* valid parameters */
    r = pSignalObjectAndWait(event[0], event[1], 0, FALSE);
    ok( r == WAIT_OBJECT_0, "should succeed\n");

    /* event[0] is now signalled */
    r = pSignalObjectAndWait(event[0], event[0], 0, FALSE);
    ok( r == WAIT_OBJECT_0, "should succeed\n");

    /* event[0] is not signalled */
    r = WaitForSingleObject(event[0], 0);
    ok( r == WAIT_TIMEOUT, "event was signalled\n");

    r = pSignalObjectAndWait(event[0], event[0], 0, FALSE);
    ok( r == WAIT_OBJECT_0, "should succeed\n");

    /* clear event[1] and check for a timeout */
    ok(ResetEvent(event[1]), "failed to clear event[1]\n");
    r = pSignalObjectAndWait(event[0], event[1], 0, FALSE);
    ok( r == WAIT_TIMEOUT, "should timeout\n");

    CloseHandle(event[0]);
    CloseHandle(event[1]);

97 98 99 100 101 102 103 104 105 106 107 108
    /* create the maximum number of events and make sure 
     * we can wait on that many */
    for (i=0; i<MAXIMUM_WAIT_OBJECTS; i++)
    {
        maxevents[i] = CreateEvent(NULL, 1, 1, NULL);
        ok( maxevents[i] != 0, "should create enough events\n");
    }
    r = WaitForMultipleObjects(MAXIMUM_WAIT_OBJECTS, maxevents, 0, 0);
    ok( r != WAIT_FAILED && r != WAIT_TIMEOUT, "should succeed\n");

    for (i=0; i<MAXIMUM_WAIT_OBJECTS; i++)
        if (maxevents[i]) CloseHandle(maxevents[i]);
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138

    /* semaphores */
    semaphore[0] = CreateSemaphore( NULL, 0, 1, NULL );
    semaphore[1] = CreateSemaphore( NULL, 1, 1, NULL );
    ok( semaphore[0] && semaphore[1], "failed to create semaphore\n");

    r = pSignalObjectAndWait(semaphore[0], semaphore[1], 0, FALSE);
    ok( r == WAIT_OBJECT_0, "should succeed\n");

    r = pSignalObjectAndWait(semaphore[0], semaphore[1], 0, FALSE);
    ok( r == WAIT_FAILED, "should fail\n");

    r = ReleaseSemaphore(semaphore[0],1,NULL);
    ok( r == FALSE, "should fail\n");

    r = ReleaseSemaphore(semaphore[1],1,NULL);
    ok( r == TRUE, "should succeed\n");

    CloseHandle(semaphore[0]);
    CloseHandle(semaphore[1]);

    /* try a registry key */
    file = CreateFile("x", GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 
        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL);
    r = pSignalObjectAndWait(file, file, 0, FALSE);
    ok( r == WAIT_FAILED, "should fail\n");
    ok( ERROR_INVALID_HANDLE == GetLastError(), "should return invalid handle error\n");
    CloseHandle(file);
}

139 140 141 142 143 144 145 146
static void test_mutex(void)
{
    DWORD wait_ret;
    BOOL ret;
    HANDLE hCreated;
    HANDLE hOpened;

    hCreated = CreateMutex(NULL, FALSE, "WineTestMutex");
147
    ok(hCreated != NULL, "CreateMutex failed with error %d\n", GetLastError());
148
    wait_ret = WaitForSingleObject(hCreated, INFINITE);
149
    ok(wait_ret == WAIT_OBJECT_0, "WaitForSingleObject failed with error 0x%08x\n", wait_ret);
150 151 152 153

    /* yes, opening with just READ_CONTROL access allows us to successfully
     * call ReleaseMutex */
    hOpened = OpenMutex(READ_CONTROL, FALSE, "WineTestMutex");
154
    ok(hOpened != NULL, "OpenMutex failed with error %d\n", GetLastError());
155
    ret = ReleaseMutex(hOpened);
156
    todo_wine ok(ret, "ReleaseMutex failed with error %d\n", GetLastError());
157 158
    ret = ReleaseMutex(hCreated);
    todo_wine ok(!ret && (GetLastError() == ERROR_NOT_OWNER),
159
        "ReleaseMutex should have failed with ERROR_NOT_OWNER instead of %d\n", GetLastError());
160

161 162 163 164 165
    /* test case sensitivity */

    SetLastError(0xdeadbeef);
    hOpened = OpenMutex(READ_CONTROL, FALSE, "WINETESTMUTEX");
    ok(!hOpened, "OpenMutex succeeded\n");
166 167 168
    ok(GetLastError() == ERROR_FILE_NOT_FOUND ||
       GetLastError() == ERROR_INVALID_NAME, /* win9x */
       "wrong error %u\n", GetLastError());
169 170 171 172

    SetLastError(0xdeadbeef);
    hOpened = OpenMutex(READ_CONTROL, FALSE, "winetestmutex");
    ok(!hOpened, "OpenMutex succeeded\n");
173 174 175
    ok(GetLastError() == ERROR_FILE_NOT_FOUND ||
       GetLastError() == ERROR_INVALID_NAME, /* win9x */
       "wrong error %u\n", GetLastError());
176 177 178 179 180

    SetLastError(0xdeadbeef);
    hOpened = CreateMutex(NULL, FALSE, "WineTestMutex");
    ok(hOpened != NULL, "CreateMutex failed with error %d\n", GetLastError());
    ok(GetLastError() == ERROR_ALREADY_EXISTS, "wrong error %u\n", GetLastError());
181
    CloseHandle(hOpened);
182 183 184 185 186 187 188

    SetLastError(0xdeadbeef);
    hOpened = CreateMutex(NULL, FALSE, "WINETESTMUTEX");
    ok(hOpened != NULL, "CreateMutex failed with error %d\n", GetLastError());
    ok(GetLastError() == 0, "wrong error %u\n", GetLastError());
    CloseHandle(hOpened);

189 190 191
    CloseHandle(hCreated);
}

Damjan Jovanovic's avatar
Damjan Jovanovic committed
192 193 194 195 196 197 198 199
static void test_slist(void)
{
    struct item
    {
        SLIST_ENTRY entry;
        int value;
    } item1, item2, item3, *pitem;

200
    SLIST_HEADER slist_header;
Damjan Jovanovic's avatar
Damjan Jovanovic committed
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
    PSLIST_ENTRY entry;
    USHORT size;

    VOID (WINAPI *pInitializeSListHead)(PSLIST_HEADER);
    USHORT (WINAPI *pQueryDepthSList)(PSLIST_HEADER);
    PSLIST_ENTRY (WINAPI *pInterlockedFlushSList)(PSLIST_HEADER);
    PSLIST_ENTRY (WINAPI *pInterlockedPopEntrySList)(PSLIST_HEADER);
    PSLIST_ENTRY (WINAPI *pInterlockedPushEntrySList)(PSLIST_HEADER,PSLIST_ENTRY);
    HMODULE kernel32;

    kernel32 = GetModuleHandle("KERNEL32.DLL");
    pInitializeSListHead = (void*) GetProcAddress(kernel32, "InitializeSListHead");
    pQueryDepthSList = (void*) GetProcAddress(kernel32, "QueryDepthSList");
    pInterlockedFlushSList = (void*) GetProcAddress(kernel32, "InterlockedFlushSList");
    pInterlockedPopEntrySList = (void*) GetProcAddress(kernel32, "InterlockedPopEntrySList");
    pInterlockedPushEntrySList = (void*) GetProcAddress(kernel32, "InterlockedPushEntrySList");
    if (pInitializeSListHead == NULL ||
        pQueryDepthSList == NULL ||
        pInterlockedFlushSList == NULL ||
        pInterlockedPopEntrySList == NULL ||
        pInterlockedPushEntrySList == NULL)
    {
223
        win_skip("some required slist entrypoints were not found, skipping tests\n");
Damjan Jovanovic's avatar
Damjan Jovanovic committed
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
        return;
    }

    memset(&slist_header, 0xFF, sizeof(slist_header));
    pInitializeSListHead(&slist_header);
    size = pQueryDepthSList(&slist_header);
    ok(size == 0, "initially created slist has size %d, expected 0\n", size);

    item1.value = 1;
    ok(pInterlockedPushEntrySList(&slist_header, &item1.entry) == NULL,
        "previous entry in empty slist wasn't NULL\n");
    size = pQueryDepthSList(&slist_header);
    ok(size == 1, "slist with 1 item has size %d\n", size);

    item2.value = 2;
    entry = pInterlockedPushEntrySList(&slist_header, &item2.entry);
    ok(entry != NULL, "previous entry in non-empty slist was NULL\n");
    if (entry != NULL)
    {
        pitem = (struct item*) entry;
        ok(pitem->value == 1, "previous entry in slist wasn't the one added\n");
    }
    size = pQueryDepthSList(&slist_header);
    ok(size == 2, "slist with 2 items has size %d\n", size);

    item3.value = 3;
    entry = pInterlockedPushEntrySList(&slist_header, &item3.entry);
    ok(entry != NULL, "previous entry in non-empty slist was NULL\n");
    if (entry != NULL)
    {
        pitem = (struct item*) entry;
        ok(pitem->value == 2, "previous entry in slist wasn't the one added\n");
    }
    size = pQueryDepthSList(&slist_header);
    ok(size == 3, "slist with 3 items has size %d\n", size);

    entry = pInterlockedPopEntrySList(&slist_header);
    ok(entry != NULL, "entry shouldn't be NULL\n");
    if (entry != NULL)
    {
        pitem = (struct item*) entry;
        ok(pitem->value == 3, "unexpected entry removed\n");
    }
    size = pQueryDepthSList(&slist_header);
    ok(size == 2, "slist with 2 items has size %d\n", size);

    entry = pInterlockedFlushSList(&slist_header);
    size = pQueryDepthSList(&slist_header);
    ok(size == 0, "flushed slist should be empty, size is %d\n", size);
    if (size == 0)
    {
        ok(pInterlockedPopEntrySList(&slist_header) == NULL,
            "popping empty slist didn't return NULL\n");
    }
    ok(((struct item*)entry)->value == 2, "item 2 not in front of list\n");
    ok(((struct item*)entry->Next)->value == 1, "item 1 not at the back of list\n");
}

282
static void test_event(void)
283
{
284
    HANDLE handle, handle2;
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
    SECURITY_ATTRIBUTES sa;
    SECURITY_DESCRIPTOR sd;
    ACL acl;

    /* no sd */
    handle = CreateEventA(NULL, FALSE, FALSE, __FILE__ ": Test Event");
    ok(handle != NULL, "CreateEventW with blank sd failed with error %d\n", GetLastError());
    CloseHandle(handle);

    sa.nLength = sizeof(sa);
    sa.lpSecurityDescriptor = &sd;
    sa.bInheritHandle = FALSE;

    InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);

    /* blank sd */
    handle = CreateEventA(&sa, FALSE, FALSE, __FILE__ ": Test Event");
    ok(handle != NULL, "CreateEventW with blank sd failed with error %d\n", GetLastError());
    CloseHandle(handle);

    /* sd with NULL dacl */
    SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE);
    handle = CreateEventA(&sa, FALSE, FALSE, __FILE__ ": Test Event");
    ok(handle != NULL, "CreateEventW with blank sd failed with error %d\n", GetLastError());
    CloseHandle(handle);

    /* sd with empty dacl */
    InitializeAcl(&acl, sizeof(acl), ACL_REVISION);
    SetSecurityDescriptorDacl(&sd, TRUE, &acl, FALSE);
    handle = CreateEventA(&sa, FALSE, FALSE, __FILE__ ": Test Event");
    ok(handle != NULL, "CreateEventW with blank sd failed with error %d\n", GetLastError());
    CloseHandle(handle);
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344

    /* test case sensitivity */

    SetLastError(0xdeadbeef);
    handle = CreateEventA(NULL, FALSE, FALSE, __FILE__ ": Test Event");
    ok( handle != NULL, "CreateEvent failed with error %u\n", GetLastError());
    ok( GetLastError() == 0, "wrong error %u\n", GetLastError());

    SetLastError(0xdeadbeef);
    handle2 = CreateEventA(NULL, FALSE, FALSE, __FILE__ ": Test Event");
    ok( handle2 != NULL, "CreateEvent failed with error %d\n", GetLastError());
    ok( GetLastError() == ERROR_ALREADY_EXISTS, "wrong error %u\n", GetLastError());
    CloseHandle( handle2 );

    SetLastError(0xdeadbeef);
    handle2 = CreateEventA(NULL, FALSE, FALSE, __FILE__ ": TEST EVENT");
    ok( handle2 != NULL, "CreateEvent failed with error %d\n", GetLastError());
    ok( GetLastError() == 0, "wrong error %u\n", GetLastError());
    CloseHandle( handle2 );

    SetLastError(0xdeadbeef);
    handle2 = OpenEventA( EVENT_ALL_ACCESS, FALSE, __FILE__ ": Test Event");
    ok( handle2 != NULL, "OpenEvent failed with error %d\n", GetLastError());
    CloseHandle( handle2 );

    SetLastError(0xdeadbeef);
    handle2 = OpenEventA( EVENT_ALL_ACCESS, FALSE, __FILE__ ": TEST EVENT");
    ok( !handle2, "OpenEvent succeeded\n");
345 346 347
    ok( GetLastError() == ERROR_FILE_NOT_FOUND ||
        GetLastError() == ERROR_INVALID_NAME, /* win9x */
        "wrong error %u\n", GetLastError());
348 349

    CloseHandle( handle );
350 351
}

352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382
static void test_semaphore(void)
{
    HANDLE handle, handle2;

    /* test case sensitivity */

    SetLastError(0xdeadbeef);
    handle = CreateSemaphoreA(NULL, 0, 1, __FILE__ ": Test Semaphore");
    ok(handle != NULL, "CreateSemaphore failed with error %u\n", GetLastError());
    ok(GetLastError() == 0, "wrong error %u\n", GetLastError());

    SetLastError(0xdeadbeef);
    handle2 = CreateSemaphoreA(NULL, 0, 1, __FILE__ ": Test Semaphore");
    ok( handle2 != NULL, "CreateSemaphore failed with error %d\n", GetLastError());
    ok( GetLastError() == ERROR_ALREADY_EXISTS, "wrong error %u\n", GetLastError());
    CloseHandle( handle2 );

    SetLastError(0xdeadbeef);
    handle2 = CreateSemaphoreA(NULL, 0, 1, __FILE__ ": TEST SEMAPHORE");
    ok( handle2 != NULL, "CreateSemaphore failed with error %d\n", GetLastError());
    ok( GetLastError() == 0, "wrong error %u\n", GetLastError());
    CloseHandle( handle2 );

    SetLastError(0xdeadbeef);
    handle2 = OpenSemaphoreA( SEMAPHORE_ALL_ACCESS, FALSE, __FILE__ ": Test Semaphore");
    ok( handle2 != NULL, "OpenSemaphore failed with error %d\n", GetLastError());
    CloseHandle( handle2 );

    SetLastError(0xdeadbeef);
    handle2 = OpenSemaphoreA( SEMAPHORE_ALL_ACCESS, FALSE, __FILE__ ": TEST SEMAPHORE");
    ok( !handle2, "OpenSemaphore succeeded\n");
383 384 385
    ok( GetLastError() == ERROR_FILE_NOT_FOUND ||
        GetLastError() == ERROR_INVALID_NAME, /* win9x */
        "wrong error %u\n", GetLastError());
386 387 388 389

    CloseHandle( handle );
}

390 391 392 393
static void test_waitable_timer(void)
{
    HANDLE handle, handle2;

394 395
    if (!pCreateWaitableTimerA || !pOpenWaitableTimerA)
    {
396
        win_skip("{Create,Open}WaitableTimerA() is not available\n");
397 398 399
        return;
    }

400 401 402
    /* test case sensitivity */

    SetLastError(0xdeadbeef);
403
    handle = pCreateWaitableTimerA(NULL, FALSE, __FILE__ ": Test WaitableTimer");
404 405 406 407
    ok(handle != NULL, "CreateWaitableTimer failed with error %u\n", GetLastError());
    ok(GetLastError() == 0, "wrong error %u\n", GetLastError());

    SetLastError(0xdeadbeef);
408
    handle2 = pCreateWaitableTimerA(NULL, FALSE, __FILE__ ": Test WaitableTimer");
409 410 411 412 413
    ok( handle2 != NULL, "CreateWaitableTimer failed with error %d\n", GetLastError());
    ok( GetLastError() == ERROR_ALREADY_EXISTS, "wrong error %u\n", GetLastError());
    CloseHandle( handle2 );

    SetLastError(0xdeadbeef);
414
    handle2 = pCreateWaitableTimerA(NULL, FALSE, __FILE__ ": TEST WAITABLETIMER");
415 416 417 418 419
    ok( handle2 != NULL, "CreateWaitableTimer failed with error %d\n", GetLastError());
    ok( GetLastError() == 0, "wrong error %u\n", GetLastError());
    CloseHandle( handle2 );

    SetLastError(0xdeadbeef);
420
    handle2 = pOpenWaitableTimerA( TIMER_ALL_ACCESS, FALSE, __FILE__ ": Test WaitableTimer");
421 422 423 424
    ok( handle2 != NULL, "OpenWaitableTimer failed with error %d\n", GetLastError());
    CloseHandle( handle2 );

    SetLastError(0xdeadbeef);
425
    handle2 = pOpenWaitableTimerA( TIMER_ALL_ACCESS, FALSE, __FILE__ ": TEST WAITABLETIMER");
426
    ok( !handle2, "OpenWaitableTimer succeeded\n");
427 428 429
    ok( GetLastError() == ERROR_FILE_NOT_FOUND ||
        GetLastError() == ERROR_INVALID_NAME, /* win98 */
        "wrong error %u\n", GetLastError());
430 431 432 433

    CloseHandle( handle );
}

434 435
static HANDLE sem = 0;

436
static void CALLBACK iocp_callback(DWORD dwErrorCode, DWORD dwNumberOfBytesTransferred, LPOVERLAPPED lpOverlapped)
437 438 439 440
{
    ReleaseSemaphore(sem, 1, NULL);
}

441
static BOOL (WINAPI *p_BindIoCompletionCallback)( HANDLE FileHandle, LPOVERLAPPED_COMPLETION_ROUTINE Function, ULONG Flags) = NULL;
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457

static void test_iocp_callback(void)
{
    char temp_path[MAX_PATH];
    char filename[MAX_PATH];
    DWORD ret;
    BOOL retb;
    static const char prefix[] = "pfx";
    HANDLE hFile;
    HMODULE hmod = GetModuleHandleA("kernel32.dll");
    DWORD bytesWritten;
    const char *buffer = "12345678123456781234567812345678";
    OVERLAPPED overlapped;

    p_BindIoCompletionCallback = (void*)GetProcAddress(hmod, "BindIoCompletionCallback");
    if(!p_BindIoCompletionCallback) {
458
        win_skip("BindIoCompletionCallback not found in this DLL\n");
459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492
        return;
    }

    sem = CreateSemaphore(NULL, 0, 1, NULL);
    ok(sem != INVALID_HANDLE_VALUE, "Creating a semaphore failed\n");

    ret = GetTempPathA(MAX_PATH, temp_path);
    ok(ret != 0, "GetTempPathA error %d\n", GetLastError());
    ok(ret < MAX_PATH, "temp path should fit into MAX_PATH\n");

    ret = GetTempFileNameA(temp_path, prefix, 0, filename);
    ok(ret != 0, "GetTempFileNameA error %d\n", GetLastError());

    hFile = CreateFileA(filename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
                        CREATE_ALWAYS, FILE_FLAG_RANDOM_ACCESS, 0);
    ok(hFile != INVALID_HANDLE_VALUE, "CreateFileA: error %d\n", GetLastError());

    retb = p_BindIoCompletionCallback(hFile, iocp_callback, 0);
    ok(retb == FALSE, "BindIoCompletionCallback succeeded on a file that wasn't created with FILE_FLAG_OVERLAPPED\n");
    ok(GetLastError() == ERROR_INVALID_PARAMETER, "Last error is %d\n", GetLastError());

    ret = CloseHandle(hFile);
    ok( ret, "CloseHandle: error %d\n", GetLastError());
    ret = DeleteFileA(filename);
    ok( ret, "DeleteFileA: error %d\n", GetLastError());

    hFile = CreateFileA(filename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
                        CREATE_ALWAYS, FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_OVERLAPPED, 0);
    ok(hFile != INVALID_HANDLE_VALUE, "CreateFileA: error %d\n", GetLastError());

    retb = p_BindIoCompletionCallback(hFile, iocp_callback, 0);
    ok(retb == TRUE, "BindIoCompletionCallback failed\n");

    memset(&overlapped, 0, sizeof(overlapped));
493
    retb = WriteFile(hFile, buffer, 4, &bytesWritten, &overlapped);
494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511
    ok(retb == TRUE || GetLastError() == ERROR_IO_PENDING, "WriteFile failed, lastError = %d\n", GetLastError());

    ret = WaitForSingleObject(sem, 5000);
    ok(ret == WAIT_OBJECT_0, "Wait for the IO completion callback failed\n");
    CloseHandle(sem);

    retb = p_BindIoCompletionCallback(hFile, iocp_callback, 0);
    ok(retb == FALSE, "BindIoCompletionCallback succeeded when setting the same callback on the file again\n");
    ok(GetLastError() == ERROR_INVALID_PARAMETER, "Last error is %d\n", GetLastError());
    retb = p_BindIoCompletionCallback(hFile, NULL, 0);
    ok(retb == FALSE, "BindIoCompletionCallback succeeded when setting the callback to NULL\n");
    ok(GetLastError() == ERROR_INVALID_PARAMETER, "Last error is %d\n", GetLastError());

    ret = CloseHandle(hFile);
    ok( ret, "CloseHandle: error %d\n", GetLastError());
    ret = DeleteFileA(filename);
    ok( ret, "DeleteFileA: error %d\n", GetLastError());

512 513
    /* win2k3 requires the Flags parameter to be zero */
    SetLastError(0xdeadbeef);
514 515 516 517
    hFile = CreateFileA(filename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
                        CREATE_ALWAYS, FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_OVERLAPPED, 0);
    ok(hFile != INVALID_HANDLE_VALUE, "CreateFileA: error %d\n", GetLastError());
    retb = p_BindIoCompletionCallback(hFile, iocp_callback, 12345);
518 519 520 521 522
    if (!retb)
        ok(GetLastError() == ERROR_INVALID_PARAMETER,
           "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
    else
        ok(retb == TRUE, "BindIoCompletionCallback failed with Flags != 0\n");
523 524 525 526 527 528 529
    ret = CloseHandle(hFile);
    ok( ret, "CloseHandle: error %d\n", GetLastError());
    ret = DeleteFileA(filename);
    ok( ret, "DeleteFileA: error %d\n", GetLastError());

    retb = p_BindIoCompletionCallback(NULL, iocp_callback, 0);
    ok(retb == FALSE, "BindIoCompletionCallback succeeded on a NULL file\n");
530 531 532
    ok(GetLastError() == ERROR_INVALID_HANDLE ||
       GetLastError() == ERROR_INVALID_PARAMETER, /* vista */
       "Last error is %d\n", GetLastError());
533 534
}

535 536
static void CALLBACK timer_queue_cb1(PVOID p, BOOLEAN timedOut)
{
537
    int *pn = p;
538 539 540 541
    ok(timedOut, "Timer callbacks should always time out\n");
    ++*pn;
}

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
struct timer_queue_data1
{
    int num_calls;
    int max_calls;
    HANDLE q, t;
};

static void CALLBACK timer_queue_cb2(PVOID p, BOOLEAN timedOut)
{
    struct timer_queue_data1 *d = p;
    ok(timedOut, "Timer callbacks should always time out\n");
    if (d->t && ++d->num_calls == d->max_calls)
    {
        BOOL ret;
        SetLastError(0xdeadbeef);
        /* Note, XP SP2 does *not* do any deadlock checking, so passing
           INVALID_HANDLE_VALUE here will just hang.  */
        ret = pDeleteTimerQueueTimer(d->q, d->t, NULL);
        ok(!ret, "DeleteTimerQueueTimer\n");
        ok(GetLastError() == ERROR_IO_PENDING, "DeleteTimerQueueTimer\n");
    }
}

static void CALLBACK timer_queue_cb3(PVOID p, BOOLEAN timedOut)
{
    struct timer_queue_data1 *d = p;
    ok(timedOut, "Timer callbacks should always time out\n");
    if (d->t && ++d->num_calls == d->max_calls)
    {
        /* Basically kill the timer since it won't have time to run
           again.  */
        BOOL ret = pChangeTimerQueueTimer(d->q, d->t, 10000, 0);
        ok(ret, "ChangeTimerQueueTimer\n");
    }
}

578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594
static void CALLBACK timer_queue_cb4(PVOID p, BOOLEAN timedOut)
{
    struct timer_queue_data1 *d = p;
    ok(timedOut, "Timer callbacks should always time out\n");
    if (d->t)
    {
        /* This tests whether a timer gets flagged for deletion before
           or after the callback runs.  If we start this timer with a
           period of zero (run once), then ChangeTimerQueueTimer will
           fail if the timer is already flagged.  Hence we really run
           only once.  Otherwise we will run multiple times.  */
        BOOL ret = pChangeTimerQueueTimer(d->q, d->t, 50, 50);
        ok(ret, "ChangeTimerQueueTimer\n");
        ++d->num_calls;
    }
}

595 596
static void CALLBACK timer_queue_cb5(PVOID p, BOOLEAN timedOut)
{
597
    DWORD_PTR delay = (DWORD_PTR) p;
598 599 600 601 602
    ok(timedOut, "Timer callbacks should always time out\n");
    if (delay)
        Sleep(delay);
}

603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636
static void CALLBACK timer_queue_cb6(PVOID p, BOOLEAN timedOut)
{
    struct timer_queue_data1 *d = p;
    ok(timedOut, "Timer callbacks should always time out\n");
    /* This tests an original implementation bug where a deleted timer may get
       to run, but it is tricky to set up.  */
    if (d->q && d->num_calls++ == 0)
    {
        /* First run: delete ourselves, then insert and remove a timer
           that goes in front of us in the sorted timeout list.  Once
           removed, we will still timeout at the faster timer's due time,
           but this should be a no-op if we are bug-free.  There should
           not be a second run.  We can test the value of num_calls later.  */
        BOOL ret;
        HANDLE t;

        /* The delete will pend while we are in this callback.  */
        SetLastError(0xdeadbeef);
        ret = pDeleteTimerQueueTimer(d->q, d->t, NULL);
        ok(!ret, "DeleteTimerQueueTimer\n");
        ok(GetLastError() == ERROR_IO_PENDING, "DeleteTimerQueueTimer\n");

        ret = pCreateTimerQueueTimer(&t, d->q, timer_queue_cb1, NULL, 100, 0, 0);
        ok(ret, "CreateTimerQueueTimer\n");
        ok(t != NULL, "CreateTimerQueueTimer\n");

        ret = pDeleteTimerQueueTimer(d->q, t, INVALID_HANDLE_VALUE);
        ok(ret, "DeleteTimerQueueTimer\n");

        /* Now we stay alive by hanging around in the callback.  */
        Sleep(500);
    }
}

637 638 639 640
static void test_timer_queue(void)
{
    HANDLE q, t1, t2, t3, t4, t5;
    int n1, n2, n3, n4, n5;
641
    struct timer_queue_data1 d1, d2, d3, d4;
642
    HANDLE e, et1, et2;
643 644
    BOOL ret;

645 646 647
    if (!pChangeTimerQueueTimer || !pCreateTimerQueue || !pCreateTimerQueueTimer
        || !pDeleteTimerQueueEx || !pDeleteTimerQueueTimer)
    {
648
        win_skip("TimerQueue API not present\n");
649 650 651
        return;
    }

652
    /* Test asynchronous deletion of the queue. */
653
    q = pCreateTimerQueue();
654 655 656
    ok(q != NULL, "CreateTimerQueue\n");

    SetLastError(0xdeadbeef);
657
    ret = pDeleteTimerQueueEx(q, NULL);
658
    ok(ret /* vista */ || GetLastError() == ERROR_IO_PENDING,
659 660
       "DeleteTimerQueueEx, GetLastError: expected ERROR_IO_PENDING, got %d\n",
       GetLastError());
661

662
    /* Test synchronous deletion of the queue and running timers. */
663
    q = pCreateTimerQueue();
664 665 666 667 668
    ok(q != NULL, "CreateTimerQueue\n");

    /* Called once.  */
    t1 = NULL;
    n1 = 0;
669 670
    ret = pCreateTimerQueueTimer(&t1, q, timer_queue_cb1, &n1, 0,
                                 0, 0);
671
    ok(ret, "CreateTimerQueueTimer\n");
672
    ok(t1 != NULL, "CreateTimerQueueTimer\n");
673 674 675 676

    /* A slow one.  */
    t2 = NULL;
    n2 = 0;
677 678
    ret = pCreateTimerQueueTimer(&t2, q, timer_queue_cb1, &n2, 0,
                                 100, 0);
679
    ok(ret, "CreateTimerQueueTimer\n");
680
    ok(t2 != NULL, "CreateTimerQueueTimer\n");
681 682 683 684

    /* A fast one.  */
    t3 = NULL;
    n3 = 0;
685 686
    ret = pCreateTimerQueueTimer(&t3, q, timer_queue_cb1, &n3, 0,
                                 10, 0);
687
    ok(ret, "CreateTimerQueueTimer\n");
688
    ok(t3 != NULL, "CreateTimerQueueTimer\n");
689 690 691 692

    /* Start really late (it won't start).  */
    t4 = NULL;
    n4 = 0;
693 694
    ret = pCreateTimerQueueTimer(&t4, q, timer_queue_cb1, &n4, 10000,
                                 10, 0);
695
    ok(ret, "CreateTimerQueueTimer\n");
696
    ok(t4 != NULL, "CreateTimerQueueTimer\n");
697 698 699 700

    /* Start soon, but delay so long it won't run again.  */
    t5 = NULL;
    n5 = 0;
701 702
    ret = pCreateTimerQueueTimer(&t5, q, timer_queue_cb1, &n5, 0,
                                 10000, 0);
703
    ok(ret, "CreateTimerQueueTimer\n");
704
    ok(t5 != NULL, "CreateTimerQueueTimer\n");
705 706 707 708

    /* Give them a chance to do some work.  */
    Sleep(500);

709 710 711 712 713 714 715 716
    /* Test deleting a once-only timer.  */
    ret = pDeleteTimerQueueTimer(q, t1, INVALID_HANDLE_VALUE);
    ok(ret, "DeleteTimerQueueTimer\n");

    /* A periodic timer.  */
    ret = pDeleteTimerQueueTimer(q, t2, INVALID_HANDLE_VALUE);
    ok(ret, "DeleteTimerQueueTimer\n");

717
    ret = pDeleteTimerQueueEx(q, INVALID_HANDLE_VALUE);
718
    ok(ret, "DeleteTimerQueueEx\n");
719 720 721 722 723
    ok(n1 == 1, "Timer callback 1\n");
    ok(n2 < n3, "Timer callback 2 should be much slower than 3\n");
    ok(n4 == 0, "Timer callback 4\n");
    ok(n5 == 1, "Timer callback 5\n");

724
    /* Test synchronous deletion of the timer/queue with event trigger. */
725
    e = CreateEvent(NULL, TRUE, FALSE, NULL);
726 727 728
    et1 = CreateEvent(NULL, TRUE, FALSE, NULL);
    et2 = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (!e || !et1 || !et2)
729 730 731 732 733
    {
        skip("Failed to create timer queue descruction event\n");
        return;
    }

734
    q = pCreateTimerQueue();
735 736
    ok(q != NULL, "CreateTimerQueue\n");

737 738
    /* Run once and finish quickly (should be done when we delete it).  */
    t1 = NULL;
739
    ret = pCreateTimerQueueTimer(&t1, q, timer_queue_cb5, NULL, 0, 0, 0);
740 741 742 743 744 745 746 747 748 749 750 751
    ok(ret, "CreateTimerQueueTimer\n");
    ok(t1 != NULL, "CreateTimerQueueTimer\n");

    /* Run once and finish slowly (shouldn't be done when we delete it).  */
    t2 = NULL;
    ret = pCreateTimerQueueTimer(&t2, q, timer_queue_cb5, (PVOID) 1000, 0,
                                 0, 0);
    ok(ret, "CreateTimerQueueTimer\n");
    ok(t2 != NULL, "CreateTimerQueueTimer\n");

    /* Run once and finish quickly (should be done when we delete it).  */
    t3 = NULL;
752
    ret = pCreateTimerQueueTimer(&t3, q, timer_queue_cb5, NULL, 0, 0, 0);
753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769
    ok(ret, "CreateTimerQueueTimer\n");
    ok(t3 != NULL, "CreateTimerQueueTimer\n");

    /* Run once and finish slowly (shouldn't be done when we delete it).  */
    t4 = NULL;
    ret = pCreateTimerQueueTimer(&t4, q, timer_queue_cb5, (PVOID) 1000, 0,
                                 0, 0);
    ok(ret, "CreateTimerQueueTimer\n");
    ok(t4 != NULL, "CreateTimerQueueTimer\n");

    /* Give them a chance to start.  */
    Sleep(400);

    /* DeleteTimerQueueTimer always returns PENDING with a NULL event,
       even if the timer is finished.  */
    SetLastError(0xdeadbeef);
    ret = pDeleteTimerQueueTimer(q, t1, NULL);
770
    ok(ret /* vista */ || GetLastError() == ERROR_IO_PENDING,
771 772
       "DeleteTimerQueueTimer, GetLastError: expected ERROR_IO_PENDING, got %d\n",
       GetLastError());
773 774 775

    SetLastError(0xdeadbeef);
    ret = pDeleteTimerQueueTimer(q, t2, NULL);
776 777 778 779
    ok(!ret, "DeleteTimerQueueTimer call was expected to fail\n");
    ok(GetLastError() == ERROR_IO_PENDING,
       "DeleteTimerQueueTimer, GetLastError: expected ERROR_IO_PENDING, got %d\n",
       GetLastError());
780 781 782

    SetLastError(0xdeadbeef);
    ret = pDeleteTimerQueueTimer(q, t3, et1);
783 784 785 786
    ok(ret, "DeleteTimerQueueTimer call was expected to fail\n");
    ok(GetLastError() == 0xdeadbeef,
       "DeleteTimerQueueTimer, GetLastError: expected 0xdeadbeef, got %d\n",
       GetLastError());
787 788 789 790 791
    ok(WaitForSingleObject(et1, 250) == WAIT_OBJECT_0,
       "Timer destruction event not triggered\n");

    SetLastError(0xdeadbeef);
    ret = pDeleteTimerQueueTimer(q, t4, et2);
792 793 794 795
    ok(!ret, "DeleteTimerQueueTimer call was expected to fail\n");
    ok(GetLastError() == ERROR_IO_PENDING,
       "DeleteTimerQueueTimer, GetLastError: expected ERROR_IO_PENDING, got %d\n",
       GetLastError());
796 797 798
    ok(WaitForSingleObject(et2, 1000) == WAIT_OBJECT_0,
       "Timer destruction event not triggered\n");

799
    SetLastError(0xdeadbeef);
800
    ret = pDeleteTimerQueueEx(q, e);
801
    ok(ret /* vista */ || GetLastError() == ERROR_IO_PENDING,
802 803
       "DeleteTimerQueueEx, GetLastError: expected ERROR_IO_PENDING, got %d\n",
       GetLastError());
804
    ok(WaitForSingleObject(e, 250) == WAIT_OBJECT_0,
805
       "Queue destruction event not triggered\n");
806
    CloseHandle(e);
807 808 809 810 811

    /* Test deleting/changing a timer in execution.  */
    q = pCreateTimerQueue();
    ok(q != NULL, "CreateTimerQueue\n");

812 813 814 815 816 817 818 819 820 821
    /* Test changing a once-only timer before it fires (this is allowed,
       whereas after it fires you cannot).  */
    n1 = 0;
    ret = pCreateTimerQueueTimer(&t1, q, timer_queue_cb1, &n1, 10000,
                                 0, 0);
    ok(ret, "CreateTimerQueueTimer\n");
    ok(t1 != NULL, "CreateTimerQueueTimer\n");
    ret = pChangeTimerQueueTimer(q, t1, 0, 0);
    ok(ret, "ChangeTimerQueueTimer\n");

822 823 824 825 826 827 828 829
    d2.t = t2 = NULL;
    d2.num_calls = 0;
    d2.max_calls = 3;
    d2.q = q;
    ret = pCreateTimerQueueTimer(&t2, q, timer_queue_cb2, &d2, 10,
                                 10, 0);
    d2.t = t2;
    ok(ret, "CreateTimerQueueTimer\n");
830
    ok(t2 != NULL, "CreateTimerQueueTimer\n");
831 832 833 834 835 836 837 838 839

    d3.t = t3 = NULL;
    d3.num_calls = 0;
    d3.max_calls = 4;
    d3.q = q;
    ret = pCreateTimerQueueTimer(&t3, q, timer_queue_cb3, &d3, 10,
                                 10, 0);
    d3.t = t3;
    ok(ret, "CreateTimerQueueTimer\n");
840
    ok(t3 != NULL, "CreateTimerQueueTimer\n");
841

842 843 844 845 846 847 848 849 850
    d4.t = t4 = NULL;
    d4.num_calls = 0;
    d4.q = q;
    ret = pCreateTimerQueueTimer(&t4, q, timer_queue_cb4, &d4, 10,
                                 0, 0);
    d4.t = t4;
    ok(ret, "CreateTimerQueueTimer\n");
    ok(t4 != NULL, "CreateTimerQueueTimer\n");

851
    Sleep(500);
852 853

    ret = pDeleteTimerQueueEx(q, INVALID_HANDLE_VALUE);
854
    ok(ret, "DeleteTimerQueueEx\n");
855
    ok(n1 == 1, "ChangeTimerQueueTimer\n");
856
    ok(d2.num_calls == d2.max_calls, "DeleteTimerQueueTimer\n");
857
    ok(d3.num_calls == d3.max_calls, "ChangeTimerQueueTimer\n");
858
    ok(d4.num_calls == 1, "Timer flagged for deletion incorrectly\n");
859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877

    /* Test an obscure bug that was in the original implementation.  */
    q = pCreateTimerQueue();
    ok(q != NULL, "CreateTimerQueue\n");

    /* All the work is done in the callback.  */
    d1.t = t1 = NULL;
    d1.num_calls = 0;
    d1.q = q;
    ret = pCreateTimerQueueTimer(&t1, q, timer_queue_cb6, &d1, 100,
                                 100, WT_EXECUTELONGFUNCTION);
    d1.t = t1;
    ok(ret, "CreateTimerQueueTimer\n");
    ok(t1 != NULL, "CreateTimerQueueTimer\n");

    Sleep(750);

    SetLastError(0xdeadbeef);
    ret = pDeleteTimerQueueEx(q, NULL);
878
    ok(ret /* vista */ || GetLastError() == ERROR_IO_PENDING,
879 880
       "DeleteTimerQueueEx, GetLastError: expected ERROR_IO_PENDING, got %d\n",
       GetLastError());
881
    ok(d1.num_calls == 1, "DeleteTimerQueueTimer\n");
882 883 884 885 886 887 888 889 890 891 892 893 894 895

    /* Test functions on the default timer queue.  */
    t1 = NULL;
    n1 = 0;
    ret = pCreateTimerQueueTimer(&t1, NULL, timer_queue_cb1, &n1, 1000,
                                 1000, 0);
    ok(ret, "CreateTimerQueueTimer, default queue\n");
    ok(t1 != NULL, "CreateTimerQueueTimer, default queue\n");

    ret = pChangeTimerQueueTimer(NULL, t1, 2000, 2000);
    ok(ret, "ChangeTimerQueueTimer, default queue\n");

    ret = pDeleteTimerQueueTimer(NULL, t1, INVALID_HANDLE_VALUE);
    ok(ret, "DeleteTimerQueueTimer, default queue\n");
896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929

    /* Try mixing default and non-default queues.  Apparently this works.  */
    q = pCreateTimerQueue();
    ok(q != NULL, "CreateTimerQueue\n");

    t1 = NULL;
    n1 = 0;
    ret = pCreateTimerQueueTimer(&t1, q, timer_queue_cb1, &n1, 1000,
                                 1000, 0);
    ok(ret, "CreateTimerQueueTimer\n");
    ok(t1 != NULL, "CreateTimerQueueTimer\n");

    t2 = NULL;
    n2 = 0;
    ret = pCreateTimerQueueTimer(&t2, NULL, timer_queue_cb1, &n2, 1000,
                                 1000, 0);
    ok(ret, "CreateTimerQueueTimer\n");
    ok(t2 != NULL, "CreateTimerQueueTimer\n");

    ret = pChangeTimerQueueTimer(NULL, t1, 2000, 2000);
    ok(ret, "ChangeTimerQueueTimer\n");

    ret = pChangeTimerQueueTimer(q, t2, 2000, 2000);
    ok(ret, "ChangeTimerQueueTimer\n");

    ret = pDeleteTimerQueueTimer(NULL, t1, INVALID_HANDLE_VALUE);
    ok(ret, "DeleteTimerQueueTimer\n");

    ret = pDeleteTimerQueueTimer(q, t2, INVALID_HANDLE_VALUE);
    ok(ret, "DeleteTimerQueueTimer\n");

    /* Try to delete the default queue?  In any case: not allowed.  */
    SetLastError(0xdeadbeef);
    ret = pDeleteTimerQueueEx(NULL, NULL);
930 931 932 933
    ok(!ret, "DeleteTimerQueueEx call was expected to fail\n");
    ok(GetLastError() == ERROR_INVALID_HANDLE,
       "DeleteTimerQueueEx, GetLastError: expected ERROR_INVALID_HANDLE, got %d\n",
       GetLastError());
934 935 936

    SetLastError(0xdeadbeef);
    ret = pDeleteTimerQueueEx(q, NULL);
937
    ok(ret /* vista */ || GetLastError() == ERROR_IO_PENDING,
938 939
       "DeleteTimerQueueEx, GetLastError: expected ERROR_IO_PENDING, got %d\n",
       GetLastError());
940 941
}

942 943
START_TEST(sync)
{
944
    HMODULE hdll = GetModuleHandle("kernel32");
945
    pChangeTimerQueueTimer = (void*)GetProcAddress(hdll, "ChangeTimerQueueTimer");
946 947
    pCreateTimerQueue = (void*)GetProcAddress(hdll, "CreateTimerQueue");
    pCreateTimerQueueTimer = (void*)GetProcAddress(hdll, "CreateTimerQueueTimer");
948
    pCreateWaitableTimerA = (void*)GetProcAddress(hdll, "CreateWaitableTimerA");
949
    pDeleteTimerQueueEx = (void*)GetProcAddress(hdll, "DeleteTimerQueueEx");
950
    pDeleteTimerQueueTimer = (void*)GetProcAddress(hdll, "DeleteTimerQueueTimer");
951 952
    pOpenWaitableTimerA = (void*)GetProcAddress(hdll, "OpenWaitableTimerA");

953
    test_signalandwait();
954
    test_mutex();
Damjan Jovanovic's avatar
Damjan Jovanovic committed
955
    test_slist();
956
    test_event();
957
    test_semaphore();
958
    test_waitable_timer();
959
    test_iocp_callback();
960
    test_timer_queue();
961
}