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
24 #define WIN32_NO_STATUS
26 #include "wine/test.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);
63 X(ConvertThreadToFiber
);
64 X(ConvertFiberToThread
);
67 X(ConvertThreadToFiberEx
);
76 #define X(f) p##f = (void*)GetProcAddress(hntdll, #f);
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
);
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
);
104 ok(*tparam
== 185, "Parameterdata expected not to be changed\n");
105 if (fls_index_to_set
!= FLS_OUT_OF_INDEXES
)
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)
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());
146 win_skip( "ConvertThreadToFiber not present\n" );
150 static void test_ConvertThreadToFiberEx(void)
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());
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());
179 win_skip( "ConvertFiberToThread not present\n" );
183 static void test_FiberHandling(void)
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();
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]);
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;
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
;
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
;
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();
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();
294 if (!pFlsAlloc
|| !pFlsSetValue
|| !pFlsGetValue
|| !pFlsFree
)
296 win_skip( "Fiber Local Storage not supported\n" );
304 status
= pRtlFlsGetValue(0, NULL
);
305 ok(status
== STATUS_INVALID_PARAMETER
, "Got unexpected status %#lx.\n", status
);
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
);
319 ok(fls_indices
[i
] == 0xdeadbeef, "Got unexpected index %#lx.\n", fls_indices
[i
]);
324 status
= pRtlFlsSetValue(fls_indices
[i
], (void *)(ULONG_PTR
)(i
+ 1));
325 ok(!status
, "Got unexpected status %#lx.\n", status
);
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
);
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
);
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",
492 ok(new_fls_data
->fls_list_entry
.Blink
== saved_entry
.Blink
, "Got unexpected Flink %p.\n",
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
);
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
);
515 win_skip("Old FLS data storage layout, skipping test.\n");
521 /* crashes on Windows. */
522 pRtlFlsGetValue(fls_indices
[0], NULL
);
525 for (i
= 0; i
< count
; ++i
)
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
);
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 );
567 "getting fls index 127 (unallocated) failed with error %lu\n", GetLastError() );
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() );
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() );
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() );
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() );
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() );
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() );
684 static void test_FiberLocalStorageCallback(PFLS_CALLBACK_FUNCTION cbfunc
)
690 if (!pFlsAlloc
|| !pFlsSetValue
|| !pFlsGetValue
|| !pFlsFree
)
692 win_skip( "Fiber Local Storage not supported\n" );
696 /* Test that the callback is executed */
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 */
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() );
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;
732 if (!pFlsAlloc
|| !pFlsFree
|| !pFlsSetValue
|| !pFlsGetValue
)
734 win_skip( "Fiber Local Storage not supported\n" );
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();
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
);
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
);
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");
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
);
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
);
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
)
801 cur_actctx
= (void*)0xdeadbeef;
802 ret
= GetCurrentActCtx(&cur_actctx
);
803 ok_(__FILE__
, line
)(ret
, "thread GetCurrentActCtx failed, %lu\n", GetLastError());
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
)
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
);
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]);
828 ret
= pConvertFiberToThread();
829 ok(ret
, "ConvertFiberToThread returned error %lu\n", GetLastError());
830 check_current_actctx_is(actctx
, FALSE
);
835 static void WINAPI
fiber_actctx_func(void *actctx
)
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());
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]);
888 static void WINAPI
exit_thread_fiber_func(void *unused
)
892 ret
= pConvertFiberToThread();
893 ok(ret
, "ConvertFiberToThread returned error %lu\n", GetLastError());
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");
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
)
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());
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]);
952 static HANDLE
create_actctx_from_module_manifest(void)
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];
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)
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);
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
);
1039 static void test_fls_exit_deadlock(void)
1041 char **argv
, cmdline
[MAX_PATH
];
1042 PROCESS_INFORMATION pi
;
1043 STARTUPINFOA si
= {0};
1046 /* Regression test for the following deadlock:
1049 * -----------------------------
1054 * terminate thread A
1057 * Thread B deadlocks on acquiring the FLS lock (in order to itself call its
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
);
1076 argc
= winetest_get_mainargs(&argv
);
1078 if (argc
== 3 && !strcmp(argv
[2], "fls_exit_deadlock"))
1080 fls_exit_deadlock_child();
1088 win_skip( "Fibers not supported by win95\n" );
1092 test_FiberHandling();
1093 test_FiberLocalStorage();
1094 test_FiberLocalStorageCallback(FiberLocalStorageProc
);
1095 test_FiberLocalStorageWithFibers(FiberLocalStorageProc
);
1096 test_fiber_actctx();
1097 test_fls_exit_deadlock();