Reactos
at master 1118 lines 40 kB view raw
1/* 2 * Unit tests for fiber functions 3 * 4 * Copyright (c) 2010 André Hentschel 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 19 */ 20 21#include <stdarg.h> 22 23#include <ntstatus.h> 24#define WIN32_NO_STATUS 25#include <winternl.h> 26#include "wine/test.h" 27#include <winuser.h> 28 29static LPVOID (WINAPI *pCreateFiber)(SIZE_T,LPFIBER_START_ROUTINE,LPVOID); 30static LPVOID (WINAPI *pConvertThreadToFiber)(LPVOID); 31static BOOL (WINAPI *pConvertFiberToThread)(void); 32static void (WINAPI *pSwitchToFiber)(LPVOID); 33static void (WINAPI *pDeleteFiber)(LPVOID); 34static LPVOID (WINAPI *pConvertThreadToFiberEx)(LPVOID,DWORD); 35static LPVOID (WINAPI *pCreateFiberEx)(SIZE_T,SIZE_T,DWORD,LPFIBER_START_ROUTINE,LPVOID); 36static BOOL (WINAPI *pIsThreadAFiber)(void); 37static DWORD (WINAPI *pFlsAlloc)(PFLS_CALLBACK_FUNCTION); 38static BOOL (WINAPI *pFlsFree)(DWORD); 39static PVOID (WINAPI *pFlsGetValue)(DWORD); 40static BOOL (WINAPI *pFlsSetValue)(DWORD,PVOID); 41static void (WINAPI *pRtlAcquirePebLock)(void); 42static void (WINAPI *pRtlReleasePebLock)(void); 43static NTSTATUS (WINAPI *pRtlFlsAlloc)(PFLS_CALLBACK_FUNCTION,DWORD*); 44static NTSTATUS (WINAPI *pRtlFlsFree)(ULONG); 45static NTSTATUS (WINAPI *pRtlFlsSetValue)(ULONG,void *); 46static NTSTATUS (WINAPI *pRtlFlsGetValue)(ULONG,void **); 47static void (WINAPI *pRtlProcessFlsData)(void *fls_data, ULONG flags); 48static void *fibers[3]; 49static BYTE testparam = 185; 50static DWORD fls_index_to_set = FLS_OUT_OF_INDEXES; 51static void* fls_value_to_set; 52 53static int fiberCount = 0; 54static int cbCount = 0; 55 56static VOID init_funcs(void) 57{ 58 HMODULE hKernel32 = GetModuleHandleA("kernel32.dll"); 59 HMODULE hntdll = GetModuleHandleA("ntdll.dll"); 60 61#define X(f) p##f = (void*)GetProcAddress(hKernel32, #f); 62 X(CreateFiber); 63 X(ConvertThreadToFiber); 64 X(ConvertFiberToThread); 65 X(SwitchToFiber); 66 X(DeleteFiber); 67 X(ConvertThreadToFiberEx); 68 X(CreateFiberEx); 69 X(IsThreadAFiber); 70 X(FlsAlloc); 71 X(FlsFree); 72 X(FlsGetValue); 73 X(FlsSetValue); 74#undef X 75 76#define X(f) p##f = (void*)GetProcAddress(hntdll, #f); 77 X(RtlFlsAlloc); 78 X(RtlFlsFree); 79 X(RtlFlsSetValue); 80 X(RtlFlsGetValue); 81 X(RtlProcessFlsData); 82 X(RtlAcquirePebLock); 83 X(RtlReleasePebLock); 84#undef X 85 86} 87 88static VOID WINAPI FiberLocalStorageProc(PVOID lpFlsData) 89{ 90 ok(lpFlsData == fls_value_to_set, 91 "FlsData expected not to be changed, value is %p, expected %p\n", 92 lpFlsData, fls_value_to_set); 93 cbCount++; 94} 95 96static VOID WINAPI FiberMainProc(LPVOID lpFiberParameter) 97{ 98 BYTE *tparam = (BYTE *)lpFiberParameter; 99 TEB *teb = NtCurrentTeb(); 100 101 ok(!teb->FlsSlots, "Got unexpected FlsSlots %p.\n", teb->FlsSlots); 102 103 fiberCount++; 104 ok(*tparam == 185, "Parameterdata expected not to be changed\n"); 105 if (fls_index_to_set != FLS_OUT_OF_INDEXES) 106 { 107 void* ret; 108 BOOL bret; 109 110 SetLastError( 0xdeadbeef ); 111 ret = pFlsGetValue(fls_index_to_set); 112 ok(ret == NULL, "FlsGetValue returned %p, expected NULL\n", ret); 113 ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got unexpected error %lu.\n", GetLastError()); 114 115 /* Set the FLS value */ 116 bret = pFlsSetValue(fls_index_to_set, fls_value_to_set); 117 ok(bret, "FlsSetValue failed with error %lu\n", GetLastError()); 118 119 ok(!!teb->FlsSlots, "Got unexpected FlsSlots %p.\n", teb->FlsSlots); 120 121 /* Verify that FlsGetValue retrieves the value set by FlsSetValue */ 122 SetLastError( 0xdeadbeef ); 123 ret = pFlsGetValue(fls_index_to_set); 124 ok(ret == fls_value_to_set, "FlsGetValue returned %p, expected %p\n", ret, fls_value_to_set); 125 ok(GetLastError() == ERROR_SUCCESS, "FlsGetValue error %lu\n", GetLastError()); 126 } 127 pSwitchToFiber(fibers[0]); 128} 129 130static void test_ConvertThreadToFiber(void) 131{ 132 void *ret; 133 134 if (pConvertThreadToFiber) 135 { 136 fibers[0] = pConvertThreadToFiber(&testparam); 137 ok(fibers[0] != NULL, "ConvertThreadToFiber failed with error %lu\n", GetLastError()); 138 139 SetLastError(0xdeadbeef); 140 ret = pConvertThreadToFiber(&testparam); 141 ok(!ret, "Got non NULL ret.\n"); 142 ok(GetLastError() == ERROR_ALREADY_FIBER, "Got unexpected error %lu.\n", GetLastError()); 143 } 144 else 145 { 146 win_skip( "ConvertThreadToFiber not present\n" ); 147 } 148} 149 150static void test_ConvertThreadToFiberEx(void) 151{ 152 void *ret; 153 154 if (pConvertThreadToFiberEx) 155 { 156 fibers[0] = pConvertThreadToFiberEx(&testparam, 0); 157 ok(fibers[0] != NULL, "ConvertThreadToFiberEx failed with error %lu\n", GetLastError()); 158 159 SetLastError(0xdeadbeef); 160 ret = pConvertThreadToFiberEx(&testparam, 0); 161 ok(!ret, "Got non NULL ret.\n"); 162 ok(GetLastError() == ERROR_ALREADY_FIBER, "Got unexpected error %lu.\n", GetLastError()); 163 } 164 else 165 { 166 win_skip( "ConvertThreadToFiberEx not present\n" ); 167 } 168} 169 170static void test_ConvertFiberToThread(void) 171{ 172 if (pConvertFiberToThread) 173 { 174 BOOL ret = pConvertFiberToThread(); 175 ok(ret, "ConvertFiberToThread failed with error %lu\n", GetLastError()); 176 } 177 else 178 { 179 win_skip( "ConvertFiberToThread not present\n" ); 180 } 181} 182 183static void test_FiberHandling(void) 184{ 185 fiberCount = 0; 186 fibers[0] = pCreateFiber(0,FiberMainProc,&testparam); 187 ok(fibers[0] != NULL, "CreateFiber failed with error %lu\n", GetLastError()); 188 pDeleteFiber(fibers[0]); 189 190 test_ConvertThreadToFiber(); 191 test_ConvertFiberToThread(); 192 if (pConvertThreadToFiberEx) 193 test_ConvertThreadToFiberEx(); 194 else 195 test_ConvertThreadToFiber(); 196 197 fibers[1] = pCreateFiber(0,FiberMainProc,&testparam); 198 ok(fibers[1] != NULL, "CreateFiber failed with error %lu\n", GetLastError()); 199 200 pSwitchToFiber(fibers[1]); 201 ok(fiberCount == 1, "Wrong fiber count: %d\n", fiberCount); 202 pDeleteFiber(fibers[1]); 203 204 if (pCreateFiberEx) 205 { 206 fibers[1] = pCreateFiberEx(0,0,0,FiberMainProc,&testparam); 207 ok(fibers[1] != NULL, "CreateFiberEx failed with error %lu\n", GetLastError()); 208 209 pSwitchToFiber(fibers[1]); 210 ok(fiberCount == 2, "Wrong fiber count: %d\n", fiberCount); 211 pDeleteFiber(fibers[1]); 212 } 213 else win_skip( "CreateFiberEx not present\n" ); 214 215 if (pIsThreadAFiber) ok(pIsThreadAFiber(), "IsThreadAFiber reported FALSE\n"); 216 test_ConvertFiberToThread(); 217 if (pIsThreadAFiber) ok(!pIsThreadAFiber(), "IsThreadAFiber reported TRUE\n"); 218} 219 220#define FLS_TEST_INDEX_COUNT 4096 221 222static unsigned int check_linked_list(const LIST_ENTRY *le, const LIST_ENTRY *search_entry, unsigned int *index_found) 223{ 224 unsigned int count = 0; 225 LIST_ENTRY *entry; 226 227 *index_found = ~0; 228 229 for (entry = le->Flink; entry != le; entry = entry->Flink) 230 { 231 if (entry == search_entry) 232 { 233 ok(*index_found == ~0, "Duplicate list entry.\n"); 234 *index_found = count; 235 } 236 ++count; 237 } 238 return count; 239} 240 241static unsigned int test_fls_callback_call_count; 242 243static void WINAPI test_fls_callback(void *data) 244{ 245 ++test_fls_callback_call_count; 246} 247 248static unsigned int test_fls_chunk_size(unsigned int chunk_index) 249{ 250 return 0x10 << chunk_index; 251} 252 253static unsigned int test_fls_chunk_index_from_index(unsigned int index, unsigned int *index_in_chunk) 254{ 255 unsigned int chunk_index = 0; 256 257 while (index >= test_fls_chunk_size(chunk_index)) 258 index -= test_fls_chunk_size(chunk_index++); 259 260 *index_in_chunk = index; 261 return chunk_index; 262} 263 264static HANDLE test_fiberlocalstorage_peb_locked_event; 265static HANDLE test_fiberlocalstorage_done_event; 266 267 268static DWORD WINAPI test_FiberLocalStorage_thread(void *arg) 269{ 270 pRtlAcquirePebLock(); 271 SetEvent(test_fiberlocalstorage_peb_locked_event); 272 WaitForSingleObject(test_fiberlocalstorage_done_event, INFINITE); 273 pRtlReleasePebLock(); 274 return 0; 275} 276 277static void test_FiberLocalStorage(void) 278{ 279 static DWORD fls_indices[FLS_TEST_INDEX_COUNT]; 280 unsigned int i, j, count, entry_count, index; 281 LIST_ENTRY *fls_list_head, saved_entry; 282 TEB_FLS_DATA *fls_data, *new_fls_data; 283 GLOBAL_FLS_DATA *g_fls_data; 284 DWORD fls, fls_2, result; 285 TEB *teb = NtCurrentTeb(); 286 PEB *peb = teb->Peb; 287 NTSTATUS status; 288 HANDLE hthread; 289 ULONG index2; 290 SIZE_T size; 291 void* val; 292 BOOL ret; 293 294 if (!pFlsAlloc || !pFlsSetValue || !pFlsGetValue || !pFlsFree) 295 { 296 win_skip( "Fiber Local Storage not supported\n" ); 297 return; 298 } 299 300 if (pRtlFlsAlloc) 301 { 302 if (pRtlFlsGetValue) 303 { 304 status = pRtlFlsGetValue(0, NULL); 305 ok(status == STATUS_INVALID_PARAMETER, "Got unexpected status %#lx.\n", status); 306 } 307 else 308 { 309 win_skip("RtlFlsGetValue is not available.\n"); 310 } 311 312 for (i = 0; i < FLS_TEST_INDEX_COUNT; ++i) 313 { 314 fls_indices[i] = 0xdeadbeef; 315 status = pRtlFlsAlloc(test_fls_callback, &fls_indices[i]); 316 ok(!status || status == STATUS_NO_MEMORY, "Got unexpected status %#lx.\n", status); 317 if (status) 318 { 319 ok(fls_indices[i] == 0xdeadbeef, "Got unexpected index %#lx.\n", fls_indices[i]); 320 break; 321 } 322 if (pRtlFlsSetValue) 323 { 324 status = pRtlFlsSetValue(fls_indices[i], (void *)(ULONG_PTR)(i + 1)); 325 ok(!status, "Got unexpected status %#lx.\n", status); 326 } 327 } 328 count = i; 329 330 fls_data = teb->FlsSlots; 331 332 /* FLS limits are increased since Win10 18312. */ 333 ok(count && (count <= 127 || (count > 4000 && count < 4096)), "Got unexpected count %u.\n", count); 334 335#if defined(__REACTOS__) && defined(_WIN64) 336 /* peb layout is different on ReactOS x64 */ 337 if (!is_reactos() && !peb->FlsCallback) 338#else 339 if (!peb->FlsCallback) 340#endif 341 { 342 ok(pRtlFlsSetValue && pRtlFlsGetValue, "Missing RtlFlsGetValue / RtlFlsSetValue.\n"); 343 ok(!peb->FlsBitmap, "Got unexpected FlsBitmap %p.\n", peb->FlsBitmap); 344 ok(!peb->FlsListHead.Flink && !peb->FlsListHead.Blink, "Got nonzero FlsListHead.\n"); 345 ok(!peb->FlsHighIndex, "Got unexpected FlsHighIndex %lu.\n", peb->FlsHighIndex); 346 347 fls_list_head = fls_data->fls_list_entry.Flink; 348 349 entry_count = check_linked_list(fls_list_head, &fls_data->fls_list_entry, &index); 350 ok(entry_count == 1, "Got unexpected count %u.\n", entry_count); 351 ok(!index, "Got unexpected index %u.\n", index); 352 353 g_fls_data = CONTAINING_RECORD(fls_list_head, GLOBAL_FLS_DATA, fls_list_head); 354 355 ok(g_fls_data->fls_high_index == 0xfef, "Got unexpected fls_high_index %#lx.\n", g_fls_data->fls_high_index); 356 357 for (i = 0; i < 8; ++i) 358 { 359 ok(!!g_fls_data->fls_callback_chunks[i], "Got zero fls_callback_chunks[%u].\n", i); 360 ok(g_fls_data->fls_callback_chunks[i]->count == test_fls_chunk_size(i), 361 "Got unexpected g_fls_data->fls_callback_chunks[%u]->count %lu.\n", 362 i, g_fls_data->fls_callback_chunks[i]->count); 363 364 size = HeapSize(GetProcessHeap(), 0, g_fls_data->fls_callback_chunks[i]); 365 ok(size == sizeof(ULONG_PTR) + sizeof(FLS_CALLBACK) * test_fls_chunk_size(i), 366 "Got unexpected size %p.\n", (void *)size); 367 368 ok(!!fls_data->fls_data_chunks[i], "Got zero fls_data->fls_data_chunks[%u].\n", i); 369 ok(!fls_data->fls_data_chunks[i][0], "Got unexpected fls_data->fls_data_chunks[%u][0] %p.\n", 370 i, fls_data->fls_data_chunks[i][0]); 371 size = HeapSize(GetProcessHeap(), 0, fls_data->fls_data_chunks[i]); 372 ok(size == sizeof(void *) * (test_fls_chunk_size(i) + 1), "Got unexpected size %p.\n", (void *)size); 373 374 if (!i) 375 { 376 ok(g_fls_data->fls_callback_chunks[0]->callbacks[0].callback == (void *)~(ULONG_PTR)0, 377 "Got unexpected callback %p.\n", 378 g_fls_data->fls_callback_chunks[0]->callbacks[0].callback); 379 } 380 381 for (j = i ? 0 : fls_indices[0]; j < test_fls_chunk_size(i); ++j) 382 { 383 ok(!g_fls_data->fls_callback_chunks[i]->callbacks[j].unknown, 384 "Got unexpected unknown %p, i %u, j %u.\n", 385 g_fls_data->fls_callback_chunks[i]->callbacks[j].unknown, i, j); 386 ok(g_fls_data->fls_callback_chunks[i]->callbacks[j].callback == test_fls_callback, 387 "Got unexpected callback %p, i %u, j %u.\n", 388 g_fls_data->fls_callback_chunks[i]->callbacks[j].callback, i, j); 389 } 390 } 391 for (i = 0; i < count; ++i) 392 { 393 j = test_fls_chunk_index_from_index(fls_indices[i], &index); 394 ok(fls_data->fls_data_chunks[j][index + 1] == (void *)(ULONG_PTR)(i + 1), 395 "Got unexpected FLS value %p, i %u, j %u, index %u.\n", 396 fls_data->fls_data_chunks[j][index + 1], i, j, index); 397 } 398 j = test_fls_chunk_index_from_index(fls_indices[0x10], &index); 399 g_fls_data->fls_callback_chunks[j]->callbacks[index].callback = NULL; 400 status = pRtlFlsFree(fls_indices[0x10]); 401 ok(status == STATUS_INVALID_PARAMETER, "Got unexpected status %#lx.\n", status); 402 403 g_fls_data->fls_callback_chunks[j]->callbacks[index].callback = test_fls_callback; 404 test_fls_callback_call_count = 0; 405 status = pRtlFlsFree(fls_indices[0x10]); 406 ok(!status, "Got unexpected status %#lx.\n", status); 407 ok(test_fls_callback_call_count == 1, "Got unexpected callback call count %u.\n", 408 test_fls_callback_call_count); 409 410 ok(!fls_data->fls_data_chunks[j][0], "Got unexpected fls_data->fls_data_chunks[%u][0] %p.\n", 411 j, fls_data->fls_data_chunks[j][0]); 412 ok(!g_fls_data->fls_callback_chunks[j]->callbacks[index].callback, 413 "Got unexpected callback %p.\n", 414 g_fls_data->fls_callback_chunks[j]->callbacks[index].callback); 415 416 fls_data->fls_data_chunks[j][index + 1] = (void *)(ULONG_PTR)0x28; 417 status = pRtlFlsAlloc(test_fls_callback, &index2); 418 ok(!status, "Got unexpected status %#lx.\n", status); 419 ok(index2 == fls_indices[0x10], "Got unexpected index %lu.\n", index2); 420 ok(fls_data->fls_data_chunks[j][index + 1] == (void *)(ULONG_PTR)0x28, "Got unexpected data %p.\n", 421 fls_data->fls_data_chunks[j][index + 1]); 422 423 status = pRtlFlsSetValue(index2, (void *)(ULONG_PTR)0x11); 424 ok(!status, "Got unexpected status %#lx.\n", status); 425 426 teb->FlsSlots = NULL; 427 428 val = (void *)0xdeadbeef; 429 status = pRtlFlsGetValue(fls_indices[1], &val); 430 new_fls_data = teb->FlsSlots; 431 ok(status == STATUS_INVALID_PARAMETER, "Got unexpected status %#lx.\n", status); 432 ok(val == (void *)0xdeadbeef, "Got unexpected val %p.\n", val); 433 ok(!new_fls_data, "Got unexpected teb->FlsSlots %p.\n", new_fls_data); 434 435 status = pRtlFlsSetValue(fls_indices[1], (void *)(ULONG_PTR)0x28); 436 new_fls_data = teb->FlsSlots; 437 ok(!status, "Got unexpected status %#lx.\n", status); 438 ok(!!new_fls_data, "Got unexpected teb->FlsSlots %p.\n", new_fls_data); 439 440 entry_count = check_linked_list(fls_list_head, &fls_data->fls_list_entry, &index); 441 ok(entry_count == 2, "Got unexpected count %u.\n", entry_count); 442 ok(!index, "Got unexpected index %u.\n", index); 443 check_linked_list(fls_list_head, &new_fls_data->fls_list_entry, &index); 444 ok(index == 1, "Got unexpected index %u.\n", index); 445 446 val = (void *)0xdeadbeef; 447 status = pRtlFlsGetValue(fls_indices[2], &val); 448 ok(!status, "Got unexpected status %#lx.\n", status); 449 ok(!val, "Got unexpected val %p.\n", val); 450 451 452 /* With bit 0 of flags set RtlProcessFlsData is removing FLS data from the linked list 453 * and calls FLS callbacks. With bit 1 set the memory is freed. The remaining bits do not seem 454 * to have any obvious effect. */ 455 for (i = 2; i < 32; ++i) 456 { 457 pRtlProcessFlsData(new_fls_data, 1 << i); 458 size = HeapSize(GetProcessHeap(), 0, new_fls_data); 459 ok(size == sizeof(*new_fls_data), "Got unexpected size %p.\n", (void *)size); 460 } 461 462 if (0) 463 { 464 pRtlProcessFlsData(new_fls_data, 2); 465 entry_count = check_linked_list(fls_list_head, &fls_data->fls_list_entry, &index); 466 ok(entry_count == 2, "Got unexpected count %u.\n", entry_count); 467 468 /* Crashes on Windows. */ 469 HeapSize(GetProcessHeap(), 0, new_fls_data); 470 } 471 472 test_fiberlocalstorage_peb_locked_event = CreateEventA(NULL, FALSE, FALSE, NULL); 473 test_fiberlocalstorage_done_event = CreateEventA(NULL, FALSE, FALSE, NULL); 474 hthread = CreateThread(NULL, 0, test_FiberLocalStorage_thread, NULL, 0, NULL); 475 ok(!!hthread, "CreateThread failed.\n"); 476 result = WaitForSingleObject(test_fiberlocalstorage_peb_locked_event, INFINITE); 477 ok(result == WAIT_OBJECT_0, "Got unexpected result %lu.\n", result); 478 teb->FlsSlots = NULL; 479 480 test_fls_callback_call_count = 0; 481 saved_entry = new_fls_data->fls_list_entry; 482 pRtlProcessFlsData(new_fls_data, 1); 483 ok(!teb->FlsSlots, "Got unexpected teb->FlsSlots %p.\n", teb->FlsSlots); 484 485 teb->FlsSlots = fls_data; 486 ok(test_fls_callback_call_count == 1, "Got unexpected callback call count %u.\n", 487 test_fls_callback_call_count); 488 489 SetEvent(test_fiberlocalstorage_done_event); 490 WaitForSingleObject(hthread, INFINITE); 491 CloseHandle(hthread); 492 CloseHandle(test_fiberlocalstorage_peb_locked_event); 493 CloseHandle(test_fiberlocalstorage_done_event); 494 495 ok(new_fls_data->fls_list_entry.Flink == saved_entry.Flink, "Got unexpected Flink %p.\n", 496 saved_entry.Flink); 497 ok(new_fls_data->fls_list_entry.Blink == saved_entry.Blink, "Got unexpected Flink %p.\n", 498 saved_entry.Blink); 499 size = HeapSize(GetProcessHeap(), 0, new_fls_data); 500 ok(size == sizeof(*new_fls_data), "Got unexpected size %p.\n", (void *)size); 501 test_fls_callback_call_count = 0; 502 i = test_fls_chunk_index_from_index(fls_indices[1], &index); 503 new_fls_data->fls_data_chunks[i][index + 1] = (void *)(ULONG_PTR)0x28; 504 pRtlProcessFlsData(new_fls_data, 2); 505 ok(!test_fls_callback_call_count, "Got unexpected callback call count %u.\n", 506 test_fls_callback_call_count); 507 508 if (0) 509 { 510 /* crashes on Windows. */ 511 HeapSize(GetProcessHeap(), 0, new_fls_data); 512 } 513 514 entry_count = check_linked_list(fls_list_head, &fls_data->fls_list_entry, &index); 515 ok(entry_count == 1, "Got unexpected count %u.\n", entry_count); 516 ok(!index, "Got unexpected index %u.\n", index); 517 } 518 else 519 { 520 win_skip("Old FLS data storage layout, skipping test.\n"); 521 g_fls_data = NULL; 522 } 523 524 if (0) 525 { 526 /* crashes on Windows. */ 527 pRtlFlsGetValue(fls_indices[0], NULL); 528 } 529 530 for (i = 0; i < count; ++i) 531 { 532 if (pRtlFlsGetValue) 533 { 534 status = pRtlFlsGetValue(fls_indices[i], &val); 535 ok(!status, "Got unexpected status %#lx.\n", status); 536 ok(val == (void *)(ULONG_PTR)(i + 1), "Got unexpected val %p, i %u.\n", val, i); 537 } 538 539 status = pRtlFlsFree(fls_indices[i]); 540 ok(!status, "Got unexpected status %#lx, i %u.\n", status, i); 541 } 542 543#if defined(__REACTOS__) && defined(_WIN64) 544 /* peb layout is different on ReactOS x64 */ 545 if (!is_reactos() && !peb->FlsCallback) 546#else 547 if (!peb->FlsCallback) 548#endif 549 { 550 ok(g_fls_data->fls_high_index == 0xfef, "Got unexpected fls_high_index %#lx.\n", 551 g_fls_data->fls_high_index); 552 553 for (i = 0; i < 8; ++i) 554 { 555 ok(!!g_fls_data->fls_callback_chunks[i], "Got zero fls_callback_chunks[%u].\n", i); 556 ok(!!fls_data->fls_data_chunks[i], "Got zero fls_data->fls_data_chunks[%u].\n", i); 557 } 558 } 559 } 560 else 561 { 562 win_skip("RtlFlsAlloc is not available.\n"); 563 } 564 565 /* Test an unallocated index 566 * FlsFree should fail 567 * FlsGetValue and FlsSetValue should succeed 568 */ 569 SetLastError( 0xdeadbeef ); 570 ret = pFlsFree( 127 ); 571 ok( !ret, "freeing fls index 127 (unallocated) succeeded\n" ); 572 ok( GetLastError() == ERROR_INVALID_PARAMETER, 573 "freeing fls index 127 (unallocated) wrong error %lu\n", GetLastError() ); 574 575 val = pFlsGetValue( 127 ); 576 ok( val == NULL, 577 "getting fls index 127 (unallocated) failed with error %lu\n", GetLastError() ); 578 579 if (pRtlFlsGetValue) 580 { 581 val = (void *)0xdeadbeef; 582 status = pRtlFlsGetValue(127, &val); 583 ok( !status, "Got unexpected status %#lx.\n", status ); 584 ok( !val, "Got unexpected val %p.\n", val ); 585 } 586 587 ret = pFlsSetValue( 127, (void*) 0x217 ); 588 ok( ret, "setting fls index 127 (unallocated) failed with error %lu\n", GetLastError() ); 589 590 SetLastError( 0xdeadbeef ); 591 val = pFlsGetValue( 127 ); 592 ok( val == (void*) 0x217, "fls index 127 (unallocated) wrong value %p\n", val ); 593 ok( GetLastError() == ERROR_SUCCESS, 594 "getting fls index 127 (unallocated) failed with error %lu\n", GetLastError() ); 595 596 if (pRtlFlsGetValue) 597 { 598 val = (void *)0xdeadbeef; 599 status = pRtlFlsGetValue(127, &val); 600 ok( !status, "Got unexpected status %#lx.\n", status ); 601 ok( val == (void*)0x217, "Got unexpected val %p.\n", val ); 602 } 603 604 /* FlsFree, FlsGetValue, and FlsSetValue out of bounds should return 605 * ERROR_INVALID_PARAMETER 606 */ 607 SetLastError( 0xdeadbeef ); 608 ret = pFlsFree( 128 ); 609 ok( !ret, "freeing fls index 128 (out of bounds) succeeded\n" ); 610 ok( GetLastError() == ERROR_INVALID_PARAMETER, 611 "freeing fls index 128 (out of bounds) wrong error %lu\n", GetLastError() ); 612 613 SetLastError( 0xdeadbeef ); 614 ret = pFlsSetValue( 128, (void*) 0x217 ); 615 ok( ret || GetLastError() == ERROR_INVALID_PARAMETER, 616 "setting fls index 128 (out of bounds) wrong error %lu\n", GetLastError() ); 617 618 SetLastError( 0xdeadbeef ); 619 val = pFlsGetValue( 128 ); 620 ok( GetLastError() == ERROR_INVALID_PARAMETER || val == (void *)0x217, 621 "getting fls index 128 (out of bounds) wrong error %lu\n", GetLastError() ); 622 623 /* Test index 0 */ 624 SetLastError( 0xdeadbeef ); 625 val = pFlsGetValue( 0 ); 626 ok( !val, "fls index 0 set to %p\n", val ); 627 ok( GetLastError() == ERROR_INVALID_PARAMETER, "setting fls index wrong error %lu\n", GetLastError() ); 628 if (pRtlFlsGetValue) 629 { 630 val = (void *)0xdeadbeef; 631 status = pRtlFlsGetValue(0, &val); 632 ok( status == STATUS_INVALID_PARAMETER, "Got unexpected status %#lx.\n", status ); 633 ok( val == (void*)0xdeadbeef, "Got unexpected val %p.\n", val ); 634 } 635 636 SetLastError( 0xdeadbeef ); 637 ret = pFlsSetValue( 0, (void *)0xdeadbeef ); 638 ok( !ret, "setting fls index 0 succeeded\n" ); 639 ok( GetLastError() == ERROR_INVALID_PARAMETER, "setting fls index wrong error %lu\n", GetLastError() ); 640 if (pRtlFlsSetValue) 641 { 642 status = pRtlFlsSetValue( 0, (void *)0xdeadbeef ); 643 ok( status == STATUS_INVALID_PARAMETER, "Got unexpected status %#lx.\n", status ); 644 } 645 SetLastError( 0xdeadbeef ); 646 val = pFlsGetValue( 0 ); 647 ok( !val, "fls index 0 wrong value %p\n", val ); 648 ok( GetLastError() == ERROR_INVALID_PARAMETER, "setting fls index wrong error %lu\n", GetLastError() ); 649 650 /* Test creating an FLS index */ 651 fls = pFlsAlloc( NULL ); 652 ok( fls != FLS_OUT_OF_INDEXES, "FlsAlloc failed\n" ); 653 ok( fls != 0, "fls index 0 allocated\n" ); 654 val = pFlsGetValue( fls ); 655 ok( !val, "fls index %lu wrong value %p\n", fls, val ); 656 SetLastError( 0xdeadbeef ); 657 ret = pFlsSetValue( fls, (void *)0xdeadbeef ); 658 ok( ret, "setting fls index %lu failed\n", fls ); 659 ok( GetLastError() == 0xdeadbeef, "setting fls index wrong error %lu\n", GetLastError() ); 660 SetLastError( 0xdeadbeef ); 661 val = pFlsGetValue( fls ); 662 ok( val == (void *)0xdeadbeef, "fls index %lu wrong value %p\n", fls, val ); 663 ok( GetLastError() == ERROR_SUCCESS, 664 "getting fls index %lu failed with error %lu\n", fls, GetLastError() ); 665 pFlsFree( fls ); 666 667 /* Undefined behavior: verify the value is NULL after it the slot is freed */ 668 SetLastError( 0xdeadbeef ); 669 val = pFlsGetValue( fls ); 670 ok( val == NULL, "fls index %lu wrong value %p\n", fls, val ); 671 ok( GetLastError() == ERROR_SUCCESS, 672 "getting fls index %lu failed with error %lu\n", fls, GetLastError() ); 673 674 /* Undefined behavior: verify the value is settable after the slot is freed */ 675 ret = pFlsSetValue( fls, (void *)0xdeadbabe ); 676 ok( ret, "setting fls index %lu failed\n", fls ); 677 val = pFlsGetValue( fls ); 678 ok( val == (void *)0xdeadbabe, "fls index %lu wrong value %p\n", fls, val ); 679 680 /* Try to create the same FLS index again, and verify that is initialized to NULL */ 681 fls_2 = pFlsAlloc( NULL ); 682 ok( fls != FLS_OUT_OF_INDEXES, "FlsAlloc failed with error %lu\n", GetLastError() ); 683 /* If this fails it is not an API error, but the test will be inconclusive */ 684 ok( fls_2 == fls, "different FLS index allocated, was %lu, now %lu\n", fls, fls_2 ); 685 686 SetLastError( 0xdeadbeef ); 687 val = pFlsGetValue( fls_2 ); 688 ok( val == NULL || val == (void *)0xdeadbabe, "fls index %lu wrong value %p\n", fls, val ); 689 ok( GetLastError() == ERROR_SUCCESS, 690 "getting fls index %lu failed with error %lu\n", fls_2, GetLastError() ); 691 pFlsFree( fls_2 ); 692} 693 694static void test_FiberLocalStorageCallback(PFLS_CALLBACK_FUNCTION cbfunc) 695{ 696 DWORD fls; 697 BOOL ret; 698 void* val, *val2; 699 700 if (!pFlsAlloc || !pFlsSetValue || !pFlsGetValue || !pFlsFree) 701 { 702 win_skip( "Fiber Local Storage not supported\n" ); 703 return; 704 } 705 706 /* Test that the callback is executed */ 707 cbCount = 0; 708 fls = pFlsAlloc( cbfunc ); 709 ok( fls != FLS_OUT_OF_INDEXES, "FlsAlloc failed with error %lu\n", GetLastError() ); 710 711 val = (void*) 0x1587; 712 fls_value_to_set = val; 713 ret = pFlsSetValue( fls, val ); 714 ok(ret, "FlsSetValue failed with error %lu\n", GetLastError() ); 715 716 val2 = pFlsGetValue( fls ); 717 ok(val == val2, "FlsGetValue returned %p, expected %p\n", val2, val); 718 719 ret = pFlsFree( fls ); 720 ok(ret, "FlsFree failed with error %lu\n", GetLastError() ); 721 ok( cbCount == 1, "Wrong callback count: %d\n", cbCount ); 722 723 /* Test that callback is not executed if value is NULL */ 724 cbCount = 0; 725 fls = pFlsAlloc( cbfunc ); 726 ok( fls != FLS_OUT_OF_INDEXES, "FlsAlloc failed with error %lu\n", GetLastError() ); 727 728 ret = pFlsSetValue( fls, NULL ); 729 ok( ret, "FlsSetValue failed with error %lu\n", GetLastError() ); 730 731 pFlsFree( fls ); 732 ok( ret, "FlsFree failed with error %lu\n", GetLastError() ); 733 ok( cbCount == 0, "Wrong callback count: %d\n", cbCount ); 734} 735 736static void test_FiberLocalStorageWithFibers(PFLS_CALLBACK_FUNCTION cbfunc) 737{ 738 void* val1 = (void*) 0x314; 739 void* val2 = (void*) 0x152; 740 BOOL ret; 741 742 if (!pFlsAlloc || !pFlsFree || !pFlsSetValue || !pFlsGetValue) 743 { 744 win_skip( "Fiber Local Storage not supported\n" ); 745 return; 746 } 747 748 fls_index_to_set = pFlsAlloc(cbfunc); 749 ok(fls_index_to_set != FLS_OUT_OF_INDEXES, "FlsAlloc failed with error %lu\n", GetLastError()); 750 751 test_ConvertThreadToFiber(); 752 753 fiberCount = 0; 754 cbCount = 0; 755 fibers[1] = pCreateFiber(0,FiberMainProc,&testparam); 756 fibers[2] = pCreateFiber(0,FiberMainProc,&testparam); 757 ok(fibers[1] != NULL, "CreateFiber failed with error %lu\n", GetLastError()); 758 ok(fibers[2] != NULL, "CreateFiber failed with error %lu\n", GetLastError()); 759 ok(fiberCount == 0, "Wrong fiber count: %d\n", fiberCount); 760 ok(cbCount == 0, "Wrong callback count: %d\n", cbCount); 761 762 fiberCount = 0; 763 cbCount = 0; 764 fls_value_to_set = val1; 765 pSwitchToFiber(fibers[1]); 766 ok(fiberCount == 1, "Wrong fiber count: %d\n", fiberCount); 767 ok(cbCount == 0, "Wrong callback count: %d\n", cbCount); 768 769 fiberCount = 0; 770 cbCount = 0; 771 fls_value_to_set = val2; 772 pSwitchToFiber(fibers[2]); 773 ok(fiberCount == 1, "Wrong fiber count: %d\n", fiberCount); 774 ok(cbCount == 0, "Wrong callback count: %d\n", cbCount); 775 776 fls_value_to_set = val2; 777 ret = pFlsSetValue(fls_index_to_set, fls_value_to_set); 778 ok(ret, "FlsSetValue failed\n"); 779 ok(val2 == pFlsGetValue(fls_index_to_set), "FlsGetValue failed\n"); 780 781 fiberCount = 0; 782 cbCount = 0; 783 fls_value_to_set = val1; 784 pDeleteFiber(fibers[1]); 785 ok(fiberCount == 0, "Wrong fiber count: %d\n", fiberCount); 786 ok(cbCount == 1, "Wrong callback count: %d\n", cbCount); 787 788 fiberCount = 0; 789 cbCount = 0; 790 fls_value_to_set = val2; 791 pFlsFree(fls_index_to_set); 792 ok(fiberCount == 0, "Wrong fiber count: %d\n", fiberCount); 793 ok(cbCount == 2, "Wrong callback count: %d\n", cbCount); 794 795 fiberCount = 0; 796 cbCount = 0; 797 fls_value_to_set = val1; 798 pDeleteFiber(fibers[2]); 799 ok(fiberCount == 0, "Wrong fiber count: %d\n", fiberCount); 800 ok(cbCount == 0, "Wrong callback count: %d\n", cbCount); 801 802 test_ConvertFiberToThread(); 803} 804 805#define check_current_actctx_is(e,t) check_current_actctx_is_(__LINE__, e, t) 806static void check_current_actctx_is_(int line, HANDLE expected_actctx, BOOL todo) 807{ 808 HANDLE cur_actctx; 809 BOOL ret; 810 811 cur_actctx = (void*)0xdeadbeef; 812 ret = GetCurrentActCtx(&cur_actctx); 813 ok_(__FILE__, line)(ret, "thread GetCurrentActCtx failed, %lu\n", GetLastError()); 814 815 todo_wine_if(todo) 816 ok_(__FILE__, line)(cur_actctx == expected_actctx, "got %p, expected %p\n", cur_actctx, expected_actctx); 817 818 ReleaseActCtx(cur_actctx); 819} 820 821static DWORD WINAPI subthread_actctx_func(void *actctx) 822{ 823 HANDLE fiber; 824 BOOL ret; 825 826 check_current_actctx_is(actctx, FALSE); 827 828 fiber = pConvertThreadToFiber(NULL); 829 ok(fiber != NULL, "ConvertThreadToFiber returned error %lu\n", GetLastError()); 830 check_current_actctx_is(actctx, FALSE); 831 fibers[2] = fiber; 832 833 SwitchToFiber(fibers[0]); 834 check_current_actctx_is(actctx, FALSE); 835 836 ok(fibers[2] == fiber, "fibers[2]: expected %p, got %p\n", fiber, fibers[2]); 837 fibers[2] = NULL; 838 ret = pConvertFiberToThread(); 839 ok(ret, "ConvertFiberToThread returned error %lu\n", GetLastError()); 840 check_current_actctx_is(actctx, FALSE); 841 842 return 0; 843} 844 845static void WINAPI fiber_actctx_func(void *actctx) 846{ 847 ULONG_PTR cookie; 848 DWORD tid, wait; 849 HANDLE thread; 850 BOOL ret; 851 852 check_current_actctx_is(NULL, FALSE); 853 854 ret = ActivateActCtx(actctx, &cookie); 855 ok(ret, "ActivateActCtx returned error %lu\n", GetLastError()); 856 check_current_actctx_is(actctx, FALSE); 857 858 SwitchToFiber(fibers[0]); 859 check_current_actctx_is(actctx, FALSE); 860 861 thread = CreateThread(NULL, 0, subthread_actctx_func, actctx, 0, &tid); 862 ok(thread != NULL, "CreateThread returned error %lu\n", GetLastError()); 863 864 wait = WaitForSingleObject(thread, INFINITE); 865 ok(wait == WAIT_OBJECT_0, "WaitForSingleObject returned %lu (last error: %lu)\n", 866 wait, GetLastError()); 867 CloseHandle(thread); 868 869 ret = DeactivateActCtx(0, cookie); 870 ok(ret, "DeactivateActCtx returned error %lu\n", GetLastError()); 871 check_current_actctx_is(NULL, FALSE); 872 873 SwitchToFiber(fibers[0]); 874 ok(0, "unreachable\n"); 875} 876 877/* Test that activation context is switched on SwitchToFiber() call */ 878static void subtest_fiber_actctx_switch(HANDLE current_actctx, HANDLE child_actctx) 879{ 880 fibers[1] = pCreateFiber(0, fiber_actctx_func, child_actctx); 881 ok(fibers[1] != NULL, "CreateFiber returned error %lu\n", GetLastError()); 882 check_current_actctx_is(current_actctx, FALSE); 883 884 SwitchToFiber(fibers[1]); 885 check_current_actctx_is(current_actctx, FALSE); 886 887 SwitchToFiber(fibers[1]); 888 check_current_actctx_is(current_actctx, FALSE); 889 890 SwitchToFiber(fibers[2]); 891 check_current_actctx_is(current_actctx, FALSE); 892 ok(fibers[2] == NULL, "expected fiber to be deleted (got %p)\n", fibers[2]); 893 894 DeleteFiber(fibers[1]); 895 fibers[1] = NULL; 896} 897 898static void WINAPI exit_thread_fiber_func(void *unused) 899{ 900 BOOL ret; 901 902 ret = pConvertFiberToThread(); 903 ok(ret, "ConvertFiberToThread returned error %lu\n", GetLastError()); 904 905 ExitThread(16); 906} 907 908static DWORD WINAPI thread_actctx_func_early_exit(void *actctx) 909{ 910 HANDLE exit_thread_fiber; 911 912 check_current_actctx_is(actctx, FALSE); 913 914 fibers[1] = pConvertThreadToFiber(NULL); 915 ok(fibers[1] != NULL, "ConvertThreadToFiber returned error %lu\n", GetLastError()); 916 check_current_actctx_is(actctx, FALSE); 917 918 exit_thread_fiber = pCreateFiber(0, exit_thread_fiber_func, NULL); 919 ok(exit_thread_fiber != NULL, "CreateFiber returned error %lu\n", GetLastError()); 920 921 /* exit thread, but keep current fiber */ 922 SwitchToFiber(exit_thread_fiber); 923 check_current_actctx_is(actctx, FALSE); 924 925 SwitchToFiber(fibers[0]); 926 ok(0, "unreachable\n"); 927 return 17; 928} 929 930/* Test that fiber activation context stack is preserved regardless of creator thread's lifetime */ 931static void subtest_fiber_actctx_preservation(HANDLE current_actctx, HANDLE child_actctx) 932{ 933 ULONG_PTR cookie; 934 DWORD tid, wait; 935 HANDLE thread; 936 BOOL ret; 937 938 ret = ActivateActCtx(child_actctx, &cookie); 939 ok(ret, "ActivateActCtx returned error %lu\n", GetLastError()); 940 check_current_actctx_is(child_actctx, FALSE); 941 942 thread = CreateThread(NULL, 0, thread_actctx_func_early_exit, child_actctx, 0, &tid); 943 ok(thread != NULL, "CreateThread returned error %lu\n", GetLastError()); 944 945 ret = DeactivateActCtx(0, cookie); 946 ok(ret, "DeactivateActCtx returned error %lu\n", GetLastError()); 947 check_current_actctx_is(current_actctx, FALSE); 948 949 wait = WaitForSingleObject(thread, INFINITE); 950 ok(wait == WAIT_OBJECT_0, "WaitForSingleObject returned %lu (last error: %lu)\n", 951 wait, GetLastError()); 952 CloseHandle(thread); 953 954 /* The exited thread has been converted to a fiber */ 955#if defined(__REACTOS__) && defined(_M_AMD64) 956 skip("FIXME: SwitchToFiber() is unimplemented on AMD64 ReactOS\n"); 957#else 958 SwitchToFiber(fibers[1]); 959 check_current_actctx_is(current_actctx, FALSE); 960 961 DeleteFiber(fibers[1]); 962 fibers[1] = NULL; 963#endif 964} 965 966static HANDLE create_actctx_from_module_manifest(void) 967{ 968 ACTCTXW actctx; 969 970 memset(&actctx, 0, sizeof(ACTCTXW)); 971 actctx.cbSize = sizeof(actctx); 972 actctx.dwFlags = ACTCTX_FLAG_HMODULE_VALID | ACTCTX_FLAG_RESOURCE_NAME_VALID; 973 actctx.lpResourceName = MAKEINTRESOURCEW(124); 974 actctx.hModule = GetModuleHandleW(NULL); 975 976 return CreateActCtxW(&actctx); 977} 978 979static void test_fiber_actctx(void) 980{ 981 ULONG_PTR cookies[3]; 982 HANDLE actctxs[3]; 983 size_t i, j; 984 BOOL ret; 985 986#ifdef __REACTOS__ 987 if (GetNTVersion() < _WIN32_WINNT_VISTA && !is_reactos()) { 988 skip("test_fiber_actctx() crashes and isn't valid on WS03.\n"); 989 return; 990 } 991#endif 992 for (i = 0; i < ARRAY_SIZE(actctxs); i++) 993 { 994 actctxs[i] = create_actctx_from_module_manifest(); 995 ok(actctxs[i] != INVALID_HANDLE_VALUE, "failed to create context, error %lu\n", GetLastError()); 996 for (j = 0; j < i; j++) 997 { 998 ok(actctxs[i] != actctxs[j], 999 "actctxs[%Iu] (%p) and actctxs[%Iu] (%p) should not alias\n", 1000 i, actctxs[i], j, actctxs[j]); 1001 } 1002 } 1003 1004 ret = ActivateActCtx(actctxs[0], &cookies[0]); 1005 ok(ret, "ActivateActCtx returned error %lu\n", GetLastError()); 1006 check_current_actctx_is(actctxs[0], FALSE); 1007 1008 test_ConvertThreadToFiber(); 1009 check_current_actctx_is(actctxs[0], FALSE); 1010 1011 ret = ActivateActCtx(actctxs[1], &cookies[1]); 1012 ok(ret, "ActivateActCtx returned error %lu\n", GetLastError()); 1013 check_current_actctx_is(actctxs[1], FALSE); 1014 1015 subtest_fiber_actctx_switch(actctxs[1], actctxs[2]); 1016 subtest_fiber_actctx_preservation(actctxs[1], actctxs[2]); 1017 1018 ret = DeactivateActCtx(0, cookies[1]); 1019 ok(ret, "DeactivateActCtx returned error %lu\n", GetLastError()); 1020 check_current_actctx_is(actctxs[0], FALSE); 1021 1022 test_ConvertFiberToThread(); 1023 check_current_actctx_is(actctxs[0], FALSE); 1024 1025 ret = DeactivateActCtx(0, cookies[0]); 1026 ok(ret, "DeactivateActCtx returned error %lu\n", GetLastError()); 1027 check_current_actctx_is(NULL, FALSE); 1028 1029 for (i = ARRAY_SIZE(actctxs); i > 0; ) 1030 ReleaseActCtx(actctxs[--i]); 1031} 1032 1033 1034static void WINAPI fls_exit_deadlock_callback(void *arg) 1035{ 1036 if (arg == (void *)1) 1037 Sleep(INFINITE); 1038 if (arg == (void *)2) 1039 /* Unfortunately this test won't affect the exit code if it fails, but 1040 * at least it will print a failure message. */ 1041 ok(RtlDllShutdownInProgress(), "expected DLL shutdown\n"); 1042} 1043 1044static DWORD CALLBACK fls_exit_deadlock_thread(void *arg) 1045{ 1046 FlsSetValue((DWORD_PTR)arg, (void *)1); 1047 return 0; 1048} 1049 1050static void fls_exit_deadlock_child(void) 1051{ 1052 DWORD index = FlsAlloc(fls_exit_deadlock_callback); 1053 FlsSetValue(index, (void *)2); 1054 CreateThread(NULL, 0, fls_exit_deadlock_thread, (void *)(DWORD_PTR)index, 0, NULL); 1055 Sleep(100); 1056 ExitProcess(0); 1057} 1058 1059static void test_fls_exit_deadlock(void) 1060{ 1061 char **argv, cmdline[MAX_PATH]; 1062 PROCESS_INFORMATION pi; 1063 STARTUPINFOA si = {0}; 1064 BOOL ret; 1065 1066 /* Regression test for the following deadlock: 1067 * 1068 * Thread A Thread B 1069 * ----------------------------- 1070 * ExitThread 1071 * acquire FLS lock 1072 * call FLS callback 1073 * ExitProcess 1074 * terminate thread A 1075 * acquire FLS lock 1076 * 1077 * Thread B deadlocks on acquiring the FLS lock (in order to itself call its 1078 * FLS callbacks.) 1079 */ 1080 1081 winetest_get_mainargs(&argv); 1082 sprintf(cmdline, "%s %s fls_exit_deadlock", argv[0], argv[1]); 1083 ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); 1084 ok(ret, "failed to create child, error %lu\n", GetLastError()); 1085 ret = WaitForSingleObject(pi.hProcess, 1000); 1086 ok(!ret, "wait failed\n"); 1087 CloseHandle(pi.hProcess); 1088 CloseHandle(pi.hThread); 1089} 1090 1091START_TEST(fiber) 1092{ 1093 char **argv; 1094 int argc; 1095 1096 argc = winetest_get_mainargs(&argv); 1097 1098 if (argc == 3 && !strcmp(argv[2], "fls_exit_deadlock")) 1099 { 1100 fls_exit_deadlock_child(); 1101 return; 1102 } 1103 1104 init_funcs(); 1105 1106 if (!pCreateFiber) 1107 { 1108 win_skip( "Fibers not supported by win95\n" ); 1109 return; 1110 } 1111 1112 test_FiberHandling(); 1113 test_FiberLocalStorage(); 1114 test_FiberLocalStorageCallback(FiberLocalStorageProc); 1115 test_FiberLocalStorageWithFibers(FiberLocalStorageProc); 1116 test_fiber_actctx(); 1117 test_fls_exit_deadlock(); 1118}