include/mscvpdb.h: Use flexible array members for the rest of structures.
[wine.git] / dlls / kernel32 / tests / fiber.c
blob46590075ffc3fa42fd52daa50cb04afc00fcc065
1 /*
2 * Unit tests for fiber functions
4 * Copyright (c) 2010 André Hentschel
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.
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.
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
21 #include <stdarg.h>
23 #include <ntstatus.h>
24 #define WIN32_NO_STATUS
25 #include <winternl.h>
26 #include "wine/test.h"
27 #include <winuser.h>
29 static LPVOID (WINAPI *pCreateFiber)(SIZE_T,LPFIBER_START_ROUTINE,LPVOID);
30 static LPVOID (WINAPI *pConvertThreadToFiber)(LPVOID);
31 static BOOL (WINAPI *pConvertFiberToThread)(void);
32 static void (WINAPI *pSwitchToFiber)(LPVOID);
33 static void (WINAPI *pDeleteFiber)(LPVOID);
34 static LPVOID (WINAPI *pConvertThreadToFiberEx)(LPVOID,DWORD);
35 static LPVOID (WINAPI *pCreateFiberEx)(SIZE_T,SIZE_T,DWORD,LPFIBER_START_ROUTINE,LPVOID);
36 static BOOL (WINAPI *pIsThreadAFiber)(void);
37 static DWORD (WINAPI *pFlsAlloc)(PFLS_CALLBACK_FUNCTION);
38 static BOOL (WINAPI *pFlsFree)(DWORD);
39 static PVOID (WINAPI *pFlsGetValue)(DWORD);
40 static BOOL (WINAPI *pFlsSetValue)(DWORD,PVOID);
41 static void (WINAPI *pRtlAcquirePebLock)(void);
42 static void (WINAPI *pRtlReleasePebLock)(void);
43 static NTSTATUS (WINAPI *pRtlFlsAlloc)(PFLS_CALLBACK_FUNCTION,DWORD*);
44 static NTSTATUS (WINAPI *pRtlFlsFree)(ULONG);
45 static NTSTATUS (WINAPI *pRtlFlsSetValue)(ULONG,void *);
46 static NTSTATUS (WINAPI *pRtlFlsGetValue)(ULONG,void **);
47 static void (WINAPI *pRtlProcessFlsData)(void *fls_data, ULONG flags);
48 static void *fibers[3];
49 static BYTE testparam = 185;
50 static DWORD fls_index_to_set = FLS_OUT_OF_INDEXES;
51 static void* fls_value_to_set;
53 static int fiberCount = 0;
54 static int cbCount = 0;
56 static VOID init_funcs(void)
58 HMODULE hKernel32 = GetModuleHandleA("kernel32.dll");
59 HMODULE hntdll = GetModuleHandleA("ntdll.dll");
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
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
88 static VOID WINAPI FiberLocalStorageProc(PVOID lpFlsData)
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++;
96 static VOID WINAPI FiberMainProc(LPVOID lpFiberParameter)
98 BYTE *tparam = (BYTE *)lpFiberParameter;
99 TEB *teb = NtCurrentTeb();
101 ok(!teb->FlsSlots, "Got unexpected FlsSlots %p.\n", teb->FlsSlots);
103 fiberCount++;
104 ok(*tparam == 185, "Parameterdata expected not to be changed\n");
105 if (fls_index_to_set != FLS_OUT_OF_INDEXES)
107 void* ret;
108 BOOL bret;
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());
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());
119 ok(!!teb->FlsSlots, "Got unexpected FlsSlots %p.\n", teb->FlsSlots);
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());
127 pSwitchToFiber(fibers[0]);
130 static void test_ConvertThreadToFiber(void)
132 void *ret;
134 if (pConvertThreadToFiber)
136 fibers[0] = pConvertThreadToFiber(&testparam);
137 ok(fibers[0] != NULL, "ConvertThreadToFiber failed with error %lu\n", GetLastError());
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());
144 else
146 win_skip( "ConvertThreadToFiber not present\n" );
150 static void test_ConvertThreadToFiberEx(void)
152 void *ret;
154 if (pConvertThreadToFiberEx)
156 fibers[0] = pConvertThreadToFiberEx(&testparam, 0);
157 ok(fibers[0] != NULL, "ConvertThreadToFiberEx failed with error %lu\n", GetLastError());
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());
164 else
166 win_skip( "ConvertThreadToFiberEx not present\n" );
170 static void test_ConvertFiberToThread(void)
172 if (pConvertFiberToThread)
174 BOOL ret = pConvertFiberToThread();
175 ok(ret, "ConvertFiberToThread failed with error %lu\n", GetLastError());
177 else
179 win_skip( "ConvertFiberToThread not present\n" );
183 static void test_FiberHandling(void)
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]);
190 test_ConvertThreadToFiber();
191 test_ConvertFiberToThread();
192 if (pConvertThreadToFiberEx)
193 test_ConvertThreadToFiberEx();
194 else
195 test_ConvertThreadToFiber();
197 fibers[1] = pCreateFiber(0,FiberMainProc,&testparam);
198 ok(fibers[1] != NULL, "CreateFiber failed with error %lu\n", GetLastError());
200 pSwitchToFiber(fibers[1]);
201 ok(fiberCount == 1, "Wrong fiber count: %d\n", fiberCount);
202 pDeleteFiber(fibers[1]);
204 if (pCreateFiberEx)
206 fibers[1] = pCreateFiberEx(0,0,0,FiberMainProc,&testparam);
207 ok(fibers[1] != NULL, "CreateFiberEx failed with error %lu\n", GetLastError());
209 pSwitchToFiber(fibers[1]);
210 ok(fiberCount == 2, "Wrong fiber count: %d\n", fiberCount);
211 pDeleteFiber(fibers[1]);
213 else win_skip( "CreateFiberEx not present\n" );
215 if (pIsThreadAFiber) ok(pIsThreadAFiber(), "IsThreadAFiber reported FALSE\n");
216 test_ConvertFiberToThread();
217 if (pIsThreadAFiber) ok(!pIsThreadAFiber(), "IsThreadAFiber reported TRUE\n");
220 #define FLS_TEST_INDEX_COUNT 4096
222 static unsigned int check_linked_list(const LIST_ENTRY *le, const LIST_ENTRY *search_entry, unsigned int *index_found)
224 unsigned int count = 0;
225 LIST_ENTRY *entry;
227 *index_found = ~0;
229 for (entry = le->Flink; entry != le; entry = entry->Flink)
231 if (entry == search_entry)
233 ok(*index_found == ~0, "Duplicate list entry.\n");
234 *index_found = count;
236 ++count;
238 return count;
241 static unsigned int test_fls_callback_call_count;
243 static void WINAPI test_fls_callback(void *data)
245 ++test_fls_callback_call_count;
248 static unsigned int test_fls_chunk_size(unsigned int chunk_index)
250 return 0x10 << chunk_index;
253 static unsigned int test_fls_chunk_index_from_index(unsigned int index, unsigned int *index_in_chunk)
255 unsigned int chunk_index = 0;
257 while (index >= test_fls_chunk_size(chunk_index))
258 index -= test_fls_chunk_size(chunk_index++);
260 *index_in_chunk = index;
261 return chunk_index;
264 static HANDLE test_fiberlocalstorage_peb_locked_event;
265 static HANDLE test_fiberlocalstorage_done_event;
268 static DWORD WINAPI test_FiberLocalStorage_thread(void *arg)
270 pRtlAcquirePebLock();
271 SetEvent(test_fiberlocalstorage_peb_locked_event);
272 WaitForSingleObject(test_fiberlocalstorage_done_event, INFINITE);
273 pRtlReleasePebLock();
274 return 0;
277 static void test_FiberLocalStorage(void)
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;
294 if (!pFlsAlloc || !pFlsSetValue || !pFlsGetValue || !pFlsFree)
296 win_skip( "Fiber Local Storage not supported\n" );
297 return;
300 if (pRtlFlsAlloc)
302 if (pRtlFlsGetValue)
304 status = pRtlFlsGetValue(0, NULL);
305 ok(status == STATUS_INVALID_PARAMETER, "Got unexpected status %#lx.\n", status);
307 else
309 win_skip("RtlFlsGetValue is not available.\n");
312 for (i = 0; i < FLS_TEST_INDEX_COUNT; ++i)
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)
319 ok(fls_indices[i] == 0xdeadbeef, "Got unexpected index %#lx.\n", fls_indices[i]);
320 break;
322 if (pRtlFlsSetValue)
324 status = pRtlFlsSetValue(fls_indices[i], (void *)(ULONG_PTR)(i + 1));
325 ok(!status, "Got unexpected status %#lx.\n", status);
328 count = i;
330 fls_data = teb->FlsSlots;
332 /* FLS limits are increased since Win10 18312. */
333 ok(count && (count <= 127 || (count > 4000 && count < 4096)), "Got unexpected count %u.\n", count);
335 if (!peb->FlsCallback)
337 ok(pRtlFlsSetValue && pRtlFlsGetValue, "Missing RtlFlsGetValue / RtlFlsSetValue.\n");
338 ok(!peb->FlsBitmap, "Got unexpected FlsBitmap %p.\n", peb->FlsBitmap);
339 ok(!peb->FlsListHead.Flink && !peb->FlsListHead.Blink, "Got nonzero FlsListHead.\n");
340 ok(!peb->FlsHighIndex, "Got unexpected FlsHighIndex %lu.\n", peb->FlsHighIndex);
342 fls_list_head = fls_data->fls_list_entry.Flink;
344 entry_count = check_linked_list(fls_list_head, &fls_data->fls_list_entry, &index);
345 ok(entry_count == 1, "Got unexpected count %u.\n", entry_count);
346 ok(!index, "Got unexpected index %u.\n", index);
348 g_fls_data = CONTAINING_RECORD(fls_list_head, GLOBAL_FLS_DATA, fls_list_head);
350 ok(g_fls_data->fls_high_index == 0xfef, "Got unexpected fls_high_index %#lx.\n", g_fls_data->fls_high_index);
352 for (i = 0; i < 8; ++i)
354 ok(!!g_fls_data->fls_callback_chunks[i], "Got zero fls_callback_chunks[%u].\n", i);
355 ok(g_fls_data->fls_callback_chunks[i]->count == test_fls_chunk_size(i),
356 "Got unexpected g_fls_data->fls_callback_chunks[%u]->count %lu.\n",
357 i, g_fls_data->fls_callback_chunks[i]->count);
359 size = HeapSize(GetProcessHeap(), 0, g_fls_data->fls_callback_chunks[i]);
360 ok(size == sizeof(ULONG_PTR) + sizeof(FLS_CALLBACK) * test_fls_chunk_size(i),
361 "Got unexpected size %p.\n", (void *)size);
363 ok(!!fls_data->fls_data_chunks[i], "Got zero fls_data->fls_data_chunks[%u].\n", i);
364 ok(!fls_data->fls_data_chunks[i][0], "Got unexpected fls_data->fls_data_chunks[%u][0] %p.\n",
365 i, fls_data->fls_data_chunks[i][0]);
366 size = HeapSize(GetProcessHeap(), 0, fls_data->fls_data_chunks[i]);
367 ok(size == sizeof(void *) * (test_fls_chunk_size(i) + 1), "Got unexpected size %p.\n", (void *)size);
369 if (!i)
371 ok(g_fls_data->fls_callback_chunks[0]->callbacks[0].callback == (void *)~(ULONG_PTR)0,
372 "Got unexpected callback %p.\n",
373 g_fls_data->fls_callback_chunks[0]->callbacks[0].callback);
376 for (j = i ? 0 : fls_indices[0]; j < test_fls_chunk_size(i); ++j)
378 ok(!g_fls_data->fls_callback_chunks[i]->callbacks[j].unknown,
379 "Got unexpected unknown %p, i %u, j %u.\n",
380 g_fls_data->fls_callback_chunks[i]->callbacks[j].unknown, i, j);
381 ok(g_fls_data->fls_callback_chunks[i]->callbacks[j].callback == test_fls_callback,
382 "Got unexpected callback %p, i %u, j %u.\n",
383 g_fls_data->fls_callback_chunks[i]->callbacks[j].callback, i, j);
386 for (i = 0; i < count; ++i)
388 j = test_fls_chunk_index_from_index(fls_indices[i], &index);
389 ok(fls_data->fls_data_chunks[j][index + 1] == (void *)(ULONG_PTR)(i + 1),
390 "Got unexpected FLS value %p, i %u, j %u, index %u.\n",
391 fls_data->fls_data_chunks[j][index + 1], i, j, index);
393 j = test_fls_chunk_index_from_index(fls_indices[0x10], &index);
394 g_fls_data->fls_callback_chunks[j]->callbacks[index].callback = NULL;
395 status = pRtlFlsFree(fls_indices[0x10]);
396 ok(status == STATUS_INVALID_PARAMETER, "Got unexpected status %#lx.\n", status);
398 g_fls_data->fls_callback_chunks[j]->callbacks[index].callback = test_fls_callback;
399 test_fls_callback_call_count = 0;
400 status = pRtlFlsFree(fls_indices[0x10]);
401 ok(!status, "Got unexpected status %#lx.\n", status);
402 ok(test_fls_callback_call_count == 1, "Got unexpected callback call count %u.\n",
403 test_fls_callback_call_count);
405 ok(!fls_data->fls_data_chunks[j][0], "Got unexpected fls_data->fls_data_chunks[%u][0] %p.\n",
406 j, fls_data->fls_data_chunks[j][0]);
407 ok(!g_fls_data->fls_callback_chunks[j]->callbacks[index].callback,
408 "Got unexpected callback %p.\n",
409 g_fls_data->fls_callback_chunks[j]->callbacks[index].callback);
411 fls_data->fls_data_chunks[j][index + 1] = (void *)(ULONG_PTR)0x28;
412 status = pRtlFlsAlloc(test_fls_callback, &index2);
413 ok(!status, "Got unexpected status %#lx.\n", status);
414 ok(index2 == fls_indices[0x10], "Got unexpected index %lu.\n", index2);
415 ok(fls_data->fls_data_chunks[j][index + 1] == (void *)(ULONG_PTR)0x28, "Got unexpected data %p.\n",
416 fls_data->fls_data_chunks[j][index + 1]);
418 status = pRtlFlsSetValue(index2, (void *)(ULONG_PTR)0x11);
419 ok(!status, "Got unexpected status %#lx.\n", status);
421 teb->FlsSlots = NULL;
423 val = (void *)0xdeadbeef;
424 status = pRtlFlsGetValue(fls_indices[1], &val);
425 new_fls_data = teb->FlsSlots;
426 ok(status == STATUS_INVALID_PARAMETER, "Got unexpected status %#lx.\n", status);
427 ok(val == (void *)0xdeadbeef, "Got unexpected val %p.\n", val);
428 ok(!new_fls_data, "Got unexpected teb->FlsSlots %p.\n", new_fls_data);
430 status = pRtlFlsSetValue(fls_indices[1], (void *)(ULONG_PTR)0x28);
431 new_fls_data = teb->FlsSlots;
432 ok(!status, "Got unexpected status %#lx.\n", status);
433 ok(!!new_fls_data, "Got unexpected teb->FlsSlots %p.\n", new_fls_data);
435 entry_count = check_linked_list(fls_list_head, &fls_data->fls_list_entry, &index);
436 ok(entry_count == 2, "Got unexpected count %u.\n", entry_count);
437 ok(!index, "Got unexpected index %u.\n", index);
438 check_linked_list(fls_list_head, &new_fls_data->fls_list_entry, &index);
439 ok(index == 1, "Got unexpected index %u.\n", index);
441 val = (void *)0xdeadbeef;
442 status = pRtlFlsGetValue(fls_indices[2], &val);
443 ok(!status, "Got unexpected status %#lx.\n", status);
444 ok(!val, "Got unexpected val %p.\n", val);
447 /* With bit 0 of flags set RtlProcessFlsData is removing FLS data from the linked list
448 * and calls FLS callbacks. With bit 1 set the memory is freed. The remaining bits do not seem
449 * to have any obvious effect. */
450 for (i = 2; i < 32; ++i)
452 pRtlProcessFlsData(new_fls_data, 1 << i);
453 size = HeapSize(GetProcessHeap(), 0, new_fls_data);
454 ok(size == sizeof(*new_fls_data), "Got unexpected size %p.\n", (void *)size);
457 if (0)
459 pRtlProcessFlsData(new_fls_data, 2);
460 entry_count = check_linked_list(fls_list_head, &fls_data->fls_list_entry, &index);
461 ok(entry_count == 2, "Got unexpected count %u.\n", entry_count);
463 /* Crashes on Windows. */
464 HeapSize(GetProcessHeap(), 0, new_fls_data);
467 test_fiberlocalstorage_peb_locked_event = CreateEventA(NULL, FALSE, FALSE, NULL);
468 test_fiberlocalstorage_done_event = CreateEventA(NULL, FALSE, FALSE, NULL);
469 hthread = CreateThread(NULL, 0, test_FiberLocalStorage_thread, NULL, 0, NULL);
470 ok(!!hthread, "CreateThread failed.\n");
471 result = WaitForSingleObject(test_fiberlocalstorage_peb_locked_event, INFINITE);
472 ok(result == WAIT_OBJECT_0, "Got unexpected result %lu.\n", result);
473 teb->FlsSlots = NULL;
475 test_fls_callback_call_count = 0;
476 saved_entry = new_fls_data->fls_list_entry;
477 pRtlProcessFlsData(new_fls_data, 1);
478 ok(!teb->FlsSlots, "Got unexpected teb->FlsSlots %p.\n", teb->FlsSlots);
480 teb->FlsSlots = fls_data;
481 ok(test_fls_callback_call_count == 1, "Got unexpected callback call count %u.\n",
482 test_fls_callback_call_count);
484 SetEvent(test_fiberlocalstorage_done_event);
485 WaitForSingleObject(hthread, INFINITE);
486 CloseHandle(hthread);
487 CloseHandle(test_fiberlocalstorage_peb_locked_event);
488 CloseHandle(test_fiberlocalstorage_done_event);
490 ok(new_fls_data->fls_list_entry.Flink == saved_entry.Flink, "Got unexpected Flink %p.\n",
491 saved_entry.Flink);
492 ok(new_fls_data->fls_list_entry.Blink == saved_entry.Blink, "Got unexpected Flink %p.\n",
493 saved_entry.Blink);
494 size = HeapSize(GetProcessHeap(), 0, new_fls_data);
495 ok(size == sizeof(*new_fls_data), "Got unexpected size %p.\n", (void *)size);
496 test_fls_callback_call_count = 0;
497 i = test_fls_chunk_index_from_index(fls_indices[1], &index);
498 new_fls_data->fls_data_chunks[i][index + 1] = (void *)(ULONG_PTR)0x28;
499 pRtlProcessFlsData(new_fls_data, 2);
500 ok(!test_fls_callback_call_count, "Got unexpected callback call count %u.\n",
501 test_fls_callback_call_count);
503 if (0)
505 /* crashes on Windows. */
506 HeapSize(GetProcessHeap(), 0, new_fls_data);
509 entry_count = check_linked_list(fls_list_head, &fls_data->fls_list_entry, &index);
510 ok(entry_count == 1, "Got unexpected count %u.\n", entry_count);
511 ok(!index, "Got unexpected index %u.\n", index);
513 else
515 win_skip("Old FLS data storage layout, skipping test.\n");
516 g_fls_data = NULL;
519 if (0)
521 /* crashes on Windows. */
522 pRtlFlsGetValue(fls_indices[0], NULL);
525 for (i = 0; i < count; ++i)
527 if (pRtlFlsGetValue)
529 status = pRtlFlsGetValue(fls_indices[i], &val);
530 ok(!status, "Got unexpected status %#lx.\n", status);
531 ok(val == (void *)(ULONG_PTR)(i + 1), "Got unexpected val %p, i %u.\n", val, i);
534 status = pRtlFlsFree(fls_indices[i]);
535 ok(!status, "Got unexpected status %#lx, i %u.\n", status, i);
538 if (!peb->FlsCallback)
540 ok(g_fls_data->fls_high_index == 0xfef, "Got unexpected fls_high_index %#lx.\n",
541 g_fls_data->fls_high_index);
543 for (i = 0; i < 8; ++i)
545 ok(!!g_fls_data->fls_callback_chunks[i], "Got zero fls_callback_chunks[%u].\n", i);
546 ok(!!fls_data->fls_data_chunks[i], "Got zero fls_data->fls_data_chunks[%u].\n", i);
550 else
552 win_skip("RtlFlsAlloc is not available.\n");
555 /* Test an unallocated index
556 * FlsFree should fail
557 * FlsGetValue and FlsSetValue should succeed
559 SetLastError( 0xdeadbeef );
560 ret = pFlsFree( 127 );
561 ok( !ret, "freeing fls index 127 (unallocated) succeeded\n" );
562 ok( GetLastError() == ERROR_INVALID_PARAMETER,
563 "freeing fls index 127 (unallocated) wrong error %lu\n", GetLastError() );
565 val = pFlsGetValue( 127 );
566 ok( val == NULL,
567 "getting fls index 127 (unallocated) failed with error %lu\n", GetLastError() );
569 if (pRtlFlsGetValue)
571 val = (void *)0xdeadbeef;
572 status = pRtlFlsGetValue(127, &val);
573 ok( !status, "Got unexpected status %#lx.\n", status );
574 ok( !val, "Got unexpected val %p.\n", val );
577 ret = pFlsSetValue( 127, (void*) 0x217 );
578 ok( ret, "setting fls index 127 (unallocated) failed with error %lu\n", GetLastError() );
580 SetLastError( 0xdeadbeef );
581 val = pFlsGetValue( 127 );
582 ok( val == (void*) 0x217, "fls index 127 (unallocated) wrong value %p\n", val );
583 ok( GetLastError() == ERROR_SUCCESS,
584 "getting fls index 127 (unallocated) failed with error %lu\n", GetLastError() );
586 if (pRtlFlsGetValue)
588 val = (void *)0xdeadbeef;
589 status = pRtlFlsGetValue(127, &val);
590 ok( !status, "Got unexpected status %#lx.\n", status );
591 ok( val == (void*)0x217, "Got unexpected val %p.\n", val );
594 /* FlsFree, FlsGetValue, and FlsSetValue out of bounds should return
595 * ERROR_INVALID_PARAMETER
597 SetLastError( 0xdeadbeef );
598 ret = pFlsFree( 128 );
599 ok( !ret, "freeing fls index 128 (out of bounds) succeeded\n" );
600 ok( GetLastError() == ERROR_INVALID_PARAMETER,
601 "freeing fls index 128 (out of bounds) wrong error %lu\n", GetLastError() );
603 SetLastError( 0xdeadbeef );
604 ret = pFlsSetValue( 128, (void*) 0x217 );
605 ok( ret || GetLastError() == ERROR_INVALID_PARAMETER,
606 "setting fls index 128 (out of bounds) wrong error %lu\n", GetLastError() );
608 SetLastError( 0xdeadbeef );
609 val = pFlsGetValue( 128 );
610 ok( GetLastError() == ERROR_INVALID_PARAMETER || val == (void *)0x217,
611 "getting fls index 128 (out of bounds) wrong error %lu\n", GetLastError() );
613 /* Test index 0 */
614 SetLastError( 0xdeadbeef );
615 val = pFlsGetValue( 0 );
616 ok( !val, "fls index 0 set to %p\n", val );
617 ok( GetLastError() == ERROR_INVALID_PARAMETER, "setting fls index wrong error %lu\n", GetLastError() );
618 if (pRtlFlsGetValue)
620 val = (void *)0xdeadbeef;
621 status = pRtlFlsGetValue(0, &val);
622 ok( status == STATUS_INVALID_PARAMETER, "Got unexpected status %#lx.\n", status );
623 ok( val == (void*)0xdeadbeef, "Got unexpected val %p.\n", val );
626 SetLastError( 0xdeadbeef );
627 ret = pFlsSetValue( 0, (void *)0xdeadbeef );
628 ok( !ret, "setting fls index 0 succeeded\n" );
629 ok( GetLastError() == ERROR_INVALID_PARAMETER, "setting fls index wrong error %lu\n", GetLastError() );
630 if (pRtlFlsSetValue)
632 status = pRtlFlsSetValue( 0, (void *)0xdeadbeef );
633 ok( status == STATUS_INVALID_PARAMETER, "Got unexpected status %#lx.\n", status );
635 SetLastError( 0xdeadbeef );
636 val = pFlsGetValue( 0 );
637 ok( !val, "fls index 0 wrong value %p\n", val );
638 ok( GetLastError() == ERROR_INVALID_PARAMETER, "setting fls index wrong error %lu\n", GetLastError() );
640 /* Test creating an FLS index */
641 fls = pFlsAlloc( NULL );
642 ok( fls != FLS_OUT_OF_INDEXES, "FlsAlloc failed\n" );
643 ok( fls != 0, "fls index 0 allocated\n" );
644 val = pFlsGetValue( fls );
645 ok( !val, "fls index %lu wrong value %p\n", fls, val );
646 SetLastError( 0xdeadbeef );
647 ret = pFlsSetValue( fls, (void *)0xdeadbeef );
648 ok( ret, "setting fls index %lu failed\n", fls );
649 ok( GetLastError() == 0xdeadbeef, "setting fls index wrong error %lu\n", GetLastError() );
650 SetLastError( 0xdeadbeef );
651 val = pFlsGetValue( fls );
652 ok( val == (void *)0xdeadbeef, "fls index %lu wrong value %p\n", fls, val );
653 ok( GetLastError() == ERROR_SUCCESS,
654 "getting fls index %lu failed with error %lu\n", fls, GetLastError() );
655 pFlsFree( fls );
657 /* Undefined behavior: verify the value is NULL after it the slot is freed */
658 SetLastError( 0xdeadbeef );
659 val = pFlsGetValue( fls );
660 ok( val == NULL, "fls index %lu wrong value %p\n", fls, val );
661 ok( GetLastError() == ERROR_SUCCESS,
662 "getting fls index %lu failed with error %lu\n", fls, GetLastError() );
664 /* Undefined behavior: verify the value is settable after the slot is freed */
665 ret = pFlsSetValue( fls, (void *)0xdeadbabe );
666 ok( ret, "setting fls index %lu failed\n", fls );
667 val = pFlsGetValue( fls );
668 ok( val == (void *)0xdeadbabe, "fls index %lu wrong value %p\n", fls, val );
670 /* Try to create the same FLS index again, and verify that is initialized to NULL */
671 fls_2 = pFlsAlloc( NULL );
672 ok( fls != FLS_OUT_OF_INDEXES, "FlsAlloc failed with error %lu\n", GetLastError() );
673 /* If this fails it is not an API error, but the test will be inconclusive */
674 ok( fls_2 == fls, "different FLS index allocated, was %lu, now %lu\n", fls, fls_2 );
676 SetLastError( 0xdeadbeef );
677 val = pFlsGetValue( fls_2 );
678 ok( val == NULL || val == (void *)0xdeadbabe, "fls index %lu wrong value %p\n", fls, val );
679 ok( GetLastError() == ERROR_SUCCESS,
680 "getting fls index %lu failed with error %lu\n", fls_2, GetLastError() );
681 pFlsFree( fls_2 );
684 static void test_FiberLocalStorageCallback(PFLS_CALLBACK_FUNCTION cbfunc)
686 DWORD fls;
687 BOOL ret;
688 void* val, *val2;
690 if (!pFlsAlloc || !pFlsSetValue || !pFlsGetValue || !pFlsFree)
692 win_skip( "Fiber Local Storage not supported\n" );
693 return;
696 /* Test that the callback is executed */
697 cbCount = 0;
698 fls = pFlsAlloc( cbfunc );
699 ok( fls != FLS_OUT_OF_INDEXES, "FlsAlloc failed with error %lu\n", GetLastError() );
701 val = (void*) 0x1587;
702 fls_value_to_set = val;
703 ret = pFlsSetValue( fls, val );
704 ok(ret, "FlsSetValue failed with error %lu\n", GetLastError() );
706 val2 = pFlsGetValue( fls );
707 ok(val == val2, "FlsGetValue returned %p, expected %p\n", val2, val);
709 ret = pFlsFree( fls );
710 ok(ret, "FlsFree failed with error %lu\n", GetLastError() );
711 ok( cbCount == 1, "Wrong callback count: %d\n", cbCount );
713 /* Test that callback is not executed if value is NULL */
714 cbCount = 0;
715 fls = pFlsAlloc( cbfunc );
716 ok( fls != FLS_OUT_OF_INDEXES, "FlsAlloc failed with error %lu\n", GetLastError() );
718 ret = pFlsSetValue( fls, NULL );
719 ok( ret, "FlsSetValue failed with error %lu\n", GetLastError() );
721 pFlsFree( fls );
722 ok( ret, "FlsFree failed with error %lu\n", GetLastError() );
723 ok( cbCount == 0, "Wrong callback count: %d\n", cbCount );
726 static void test_FiberLocalStorageWithFibers(PFLS_CALLBACK_FUNCTION cbfunc)
728 void* val1 = (void*) 0x314;
729 void* val2 = (void*) 0x152;
730 BOOL ret;
732 if (!pFlsAlloc || !pFlsFree || !pFlsSetValue || !pFlsGetValue)
734 win_skip( "Fiber Local Storage not supported\n" );
735 return;
738 fls_index_to_set = pFlsAlloc(cbfunc);
739 ok(fls_index_to_set != FLS_OUT_OF_INDEXES, "FlsAlloc failed with error %lu\n", GetLastError());
741 test_ConvertThreadToFiber();
743 fiberCount = 0;
744 cbCount = 0;
745 fibers[1] = pCreateFiber(0,FiberMainProc,&testparam);
746 fibers[2] = pCreateFiber(0,FiberMainProc,&testparam);
747 ok(fibers[1] != NULL, "CreateFiber failed with error %lu\n", GetLastError());
748 ok(fibers[2] != NULL, "CreateFiber failed with error %lu\n", GetLastError());
749 ok(fiberCount == 0, "Wrong fiber count: %d\n", fiberCount);
750 ok(cbCount == 0, "Wrong callback count: %d\n", cbCount);
752 fiberCount = 0;
753 cbCount = 0;
754 fls_value_to_set = val1;
755 pSwitchToFiber(fibers[1]);
756 ok(fiberCount == 1, "Wrong fiber count: %d\n", fiberCount);
757 ok(cbCount == 0, "Wrong callback count: %d\n", cbCount);
759 fiberCount = 0;
760 cbCount = 0;
761 fls_value_to_set = val2;
762 pSwitchToFiber(fibers[2]);
763 ok(fiberCount == 1, "Wrong fiber count: %d\n", fiberCount);
764 ok(cbCount == 0, "Wrong callback count: %d\n", cbCount);
766 fls_value_to_set = val2;
767 ret = pFlsSetValue(fls_index_to_set, fls_value_to_set);
768 ok(ret, "FlsSetValue failed\n");
769 ok(val2 == pFlsGetValue(fls_index_to_set), "FlsGetValue failed\n");
771 fiberCount = 0;
772 cbCount = 0;
773 fls_value_to_set = val1;
774 pDeleteFiber(fibers[1]);
775 ok(fiberCount == 0, "Wrong fiber count: %d\n", fiberCount);
776 ok(cbCount == 1, "Wrong callback count: %d\n", cbCount);
778 fiberCount = 0;
779 cbCount = 0;
780 fls_value_to_set = val2;
781 pFlsFree(fls_index_to_set);
782 ok(fiberCount == 0, "Wrong fiber count: %d\n", fiberCount);
783 ok(cbCount == 2, "Wrong callback count: %d\n", cbCount);
785 fiberCount = 0;
786 cbCount = 0;
787 fls_value_to_set = val1;
788 pDeleteFiber(fibers[2]);
789 ok(fiberCount == 0, "Wrong fiber count: %d\n", fiberCount);
790 ok(cbCount == 0, "Wrong callback count: %d\n", cbCount);
792 test_ConvertFiberToThread();
795 #define check_current_actctx_is(e,t) check_current_actctx_is_(__LINE__, e, t)
796 static void check_current_actctx_is_(int line, HANDLE expected_actctx, BOOL todo)
798 HANDLE cur_actctx;
799 BOOL ret;
801 cur_actctx = (void*)0xdeadbeef;
802 ret = GetCurrentActCtx(&cur_actctx);
803 ok_(__FILE__, line)(ret, "thread GetCurrentActCtx failed, %lu\n", GetLastError());
805 todo_wine_if(todo)
806 ok_(__FILE__, line)(cur_actctx == expected_actctx, "got %p, expected %p\n", cur_actctx, expected_actctx);
808 ReleaseActCtx(cur_actctx);
811 static DWORD WINAPI subthread_actctx_func(void *actctx)
813 HANDLE fiber;
814 BOOL ret;
816 check_current_actctx_is(actctx, FALSE);
818 fiber = pConvertThreadToFiber(NULL);
819 ok(fiber != NULL, "ConvertThreadToFiber returned error %lu\n", GetLastError());
820 check_current_actctx_is(actctx, FALSE);
821 fibers[2] = fiber;
823 SwitchToFiber(fibers[0]);
824 check_current_actctx_is(actctx, FALSE);
826 ok(fibers[2] == fiber, "fibers[2]: expected %p, got %p\n", fiber, fibers[2]);
827 fibers[2] = NULL;
828 ret = pConvertFiberToThread();
829 ok(ret, "ConvertFiberToThread returned error %lu\n", GetLastError());
830 check_current_actctx_is(actctx, FALSE);
832 return 0;
835 static void WINAPI fiber_actctx_func(void *actctx)
837 ULONG_PTR cookie;
838 DWORD tid, wait;
839 HANDLE thread;
840 BOOL ret;
842 check_current_actctx_is(NULL, FALSE);
844 ret = ActivateActCtx(actctx, &cookie);
845 ok(ret, "ActivateActCtx returned error %lu\n", GetLastError());
846 check_current_actctx_is(actctx, FALSE);
848 SwitchToFiber(fibers[0]);
849 check_current_actctx_is(actctx, FALSE);
851 thread = CreateThread(NULL, 0, subthread_actctx_func, actctx, 0, &tid);
852 ok(thread != NULL, "CreateThread returned error %lu\n", GetLastError());
854 wait = WaitForSingleObject(thread, INFINITE);
855 ok(wait == WAIT_OBJECT_0, "WaitForSingleObject returned %lu (last error: %lu)\n",
856 wait, GetLastError());
857 CloseHandle(thread);
859 ret = DeactivateActCtx(0, cookie);
860 ok(ret, "DeactivateActCtx returned error %lu\n", GetLastError());
861 check_current_actctx_is(NULL, FALSE);
863 SwitchToFiber(fibers[0]);
864 ok(0, "unreachable\n");
867 /* Test that activation context is switched on SwitchToFiber() call */
868 static void subtest_fiber_actctx_switch(HANDLE current_actctx, HANDLE child_actctx)
870 fibers[1] = pCreateFiber(0, fiber_actctx_func, child_actctx);
871 ok(fibers[1] != NULL, "CreateFiber returned error %lu\n", GetLastError());
872 check_current_actctx_is(current_actctx, FALSE);
874 SwitchToFiber(fibers[1]);
875 check_current_actctx_is(current_actctx, FALSE);
877 SwitchToFiber(fibers[1]);
878 check_current_actctx_is(current_actctx, FALSE);
880 SwitchToFiber(fibers[2]);
881 check_current_actctx_is(current_actctx, FALSE);
882 ok(fibers[2] == NULL, "expected fiber to be deleted (got %p)\n", fibers[2]);
884 DeleteFiber(fibers[1]);
885 fibers[1] = NULL;
888 static void WINAPI exit_thread_fiber_func(void *unused)
890 BOOL ret;
892 ret = pConvertFiberToThread();
893 ok(ret, "ConvertFiberToThread returned error %lu\n", GetLastError());
895 ExitThread(16);
898 static DWORD WINAPI thread_actctx_func_early_exit(void *actctx)
900 HANDLE exit_thread_fiber;
902 check_current_actctx_is(actctx, FALSE);
904 fibers[1] = pConvertThreadToFiber(NULL);
905 ok(fibers[1] != NULL, "ConvertThreadToFiber returned error %lu\n", GetLastError());
906 check_current_actctx_is(actctx, FALSE);
908 exit_thread_fiber = pCreateFiber(0, exit_thread_fiber_func, NULL);
909 ok(exit_thread_fiber != NULL, "CreateFiber returned error %lu\n", GetLastError());
911 /* exit thread, but keep current fiber */
912 SwitchToFiber(exit_thread_fiber);
913 check_current_actctx_is(actctx, FALSE);
915 SwitchToFiber(fibers[0]);
916 ok(0, "unreachable\n");
917 return 17;
920 /* Test that fiber activation context stack is preserved regardless of creator thread's lifetime */
921 static void subtest_fiber_actctx_preservation(HANDLE current_actctx, HANDLE child_actctx)
923 ULONG_PTR cookie;
924 DWORD tid, wait;
925 HANDLE thread;
926 BOOL ret;
928 ret = ActivateActCtx(child_actctx, &cookie);
929 ok(ret, "ActivateActCtx returned error %lu\n", GetLastError());
930 check_current_actctx_is(child_actctx, FALSE);
932 thread = CreateThread(NULL, 0, thread_actctx_func_early_exit, child_actctx, 0, &tid);
933 ok(thread != NULL, "CreateThread returned error %lu\n", GetLastError());
935 ret = DeactivateActCtx(0, cookie);
936 ok(ret, "DeactivateActCtx returned error %lu\n", GetLastError());
937 check_current_actctx_is(current_actctx, FALSE);
939 wait = WaitForSingleObject(thread, INFINITE);
940 ok(wait == WAIT_OBJECT_0, "WaitForSingleObject returned %lu (last error: %lu)\n",
941 wait, GetLastError());
942 CloseHandle(thread);
944 /* The exited thread has been converted to a fiber */
945 SwitchToFiber(fibers[1]);
946 check_current_actctx_is(current_actctx, FALSE);
948 DeleteFiber(fibers[1]);
949 fibers[1] = NULL;
952 static HANDLE create_actctx_from_module_manifest(void)
954 ACTCTXW actctx;
956 memset(&actctx, 0, sizeof(ACTCTXW));
957 actctx.cbSize = sizeof(actctx);
958 actctx.dwFlags = ACTCTX_FLAG_HMODULE_VALID | ACTCTX_FLAG_RESOURCE_NAME_VALID;
959 actctx.lpResourceName = MAKEINTRESOURCEW(124);
960 actctx.hModule = GetModuleHandleW(NULL);
962 return CreateActCtxW(&actctx);
965 static void test_fiber_actctx(void)
967 ULONG_PTR cookies[3];
968 HANDLE actctxs[3];
969 size_t i, j;
970 BOOL ret;
972 for (i = 0; i < ARRAY_SIZE(actctxs); i++)
974 actctxs[i] = create_actctx_from_module_manifest();
975 ok(actctxs[i] != INVALID_HANDLE_VALUE, "failed to create context, error %lu\n", GetLastError());
976 for (j = 0; j < i; j++)
978 ok(actctxs[i] != actctxs[j],
979 "actctxs[%Iu] (%p) and actctxs[%Iu] (%p) should not alias\n",
980 i, actctxs[i], j, actctxs[j]);
984 ret = ActivateActCtx(actctxs[0], &cookies[0]);
985 ok(ret, "ActivateActCtx returned error %lu\n", GetLastError());
986 check_current_actctx_is(actctxs[0], FALSE);
988 test_ConvertThreadToFiber();
989 check_current_actctx_is(actctxs[0], FALSE);
991 ret = ActivateActCtx(actctxs[1], &cookies[1]);
992 ok(ret, "ActivateActCtx returned error %lu\n", GetLastError());
993 check_current_actctx_is(actctxs[1], FALSE);
995 subtest_fiber_actctx_switch(actctxs[1], actctxs[2]);
996 subtest_fiber_actctx_preservation(actctxs[1], actctxs[2]);
998 ret = DeactivateActCtx(0, cookies[1]);
999 ok(ret, "DeactivateActCtx returned error %lu\n", GetLastError());
1000 check_current_actctx_is(actctxs[0], FALSE);
1002 test_ConvertFiberToThread();
1003 check_current_actctx_is(actctxs[0], FALSE);
1005 ret = DeactivateActCtx(0, cookies[0]);
1006 ok(ret, "DeactivateActCtx returned error %lu\n", GetLastError());
1007 check_current_actctx_is(NULL, FALSE);
1009 for (i = ARRAY_SIZE(actctxs); i > 0; )
1010 ReleaseActCtx(actctxs[--i]);
1014 static void WINAPI fls_exit_deadlock_callback(void *arg)
1016 if (arg == (void *)1)
1017 Sleep(INFINITE);
1018 if (arg == (void *)2)
1019 /* Unfortunately this test won't affect the exit code if it fails, but
1020 * at least it will print a failure message. */
1021 ok(RtlDllShutdownInProgress(), "expected DLL shutdown\n");
1024 static DWORD CALLBACK fls_exit_deadlock_thread(void *arg)
1026 FlsSetValue((DWORD_PTR)arg, (void *)1);
1027 return 0;
1030 static void fls_exit_deadlock_child(void)
1032 DWORD index = FlsAlloc(fls_exit_deadlock_callback);
1033 FlsSetValue(index, (void *)2);
1034 CreateThread(NULL, 0, fls_exit_deadlock_thread, (void *)(DWORD_PTR)index, 0, NULL);
1035 Sleep(100);
1036 ExitProcess(0);
1039 static void test_fls_exit_deadlock(void)
1041 char **argv, cmdline[MAX_PATH];
1042 PROCESS_INFORMATION pi;
1043 STARTUPINFOA si = {0};
1044 BOOL ret;
1046 /* Regression test for the following deadlock:
1048 * Thread A Thread B
1049 * -----------------------------
1050 * ExitThread
1051 * acquire FLS lock
1052 * call FLS callback
1053 * ExitProcess
1054 * terminate thread A
1055 * acquire FLS lock
1057 * Thread B deadlocks on acquiring the FLS lock (in order to itself call its
1058 * FLS callbacks.)
1061 winetest_get_mainargs(&argv);
1062 sprintf(cmdline, "%s %s fls_exit_deadlock", argv[0], argv[1]);
1063 ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
1064 ok(ret, "failed to create child, error %lu\n", GetLastError());
1065 ret = WaitForSingleObject(pi.hProcess, 1000);
1066 ok(!ret, "wait failed\n");
1067 CloseHandle(pi.hProcess);
1068 CloseHandle(pi.hThread);
1071 START_TEST(fiber)
1073 char **argv;
1074 int argc;
1076 argc = winetest_get_mainargs(&argv);
1078 if (argc == 3 && !strcmp(argv[2], "fls_exit_deadlock"))
1080 fls_exit_deadlock_child();
1081 return;
1084 init_funcs();
1086 if (!pCreateFiber)
1088 win_skip( "Fibers not supported by win95\n" );
1089 return;
1092 test_FiberHandling();
1093 test_FiberLocalStorage();
1094 test_FiberLocalStorageCallback(FiberLocalStorageProc);
1095 test_FiberLocalStorageWithFibers(FiberLocalStorageProc);
1096 test_fiber_actctx();
1097 test_fls_exit_deadlock();