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 "wine/test.h" 22 23 static LPVOID (WINAPI *pCreateFiber)(SIZE_T,LPFIBER_START_ROUTINE,LPVOID); 24 static LPVOID (WINAPI *pConvertThreadToFiber)(LPVOID); 25 static BOOL (WINAPI *pConvertFiberToThread)(void); 26 static void (WINAPI *pSwitchToFiber)(LPVOID); 27 static void (WINAPI *pDeleteFiber)(LPVOID); 28 static LPVOID (WINAPI *pConvertThreadToFiberEx)(LPVOID,DWORD); 29 static LPVOID (WINAPI *pCreateFiberEx)(SIZE_T,SIZE_T,DWORD,LPFIBER_START_ROUTINE,LPVOID); 30 static BOOL (WINAPI *pIsThreadAFiber)(void); 31 static DWORD (WINAPI *pFlsAlloc)(PFLS_CALLBACK_FUNCTION); 32 static BOOL (WINAPI *pFlsFree)(DWORD); 33 static PVOID (WINAPI *pFlsGetValue)(DWORD); 34 static BOOL (WINAPI *pFlsSetValue)(DWORD,PVOID); 35 36 static void *fibers[3]; 37 static BYTE testparam = 185; 38 static DWORD fls_index_to_set = FLS_OUT_OF_INDEXES; 39 static void* fls_value_to_set; 40 41 static int fiberCount = 0; 42 static int cbCount = 0; 43 44 static VOID init_funcs(void) 45 { 46 HMODULE hKernel32 = GetModuleHandleA("kernel32.dll"); 47 48 #define X(f) p##f = (void*)GetProcAddress(hKernel32, #f); 49 X(CreateFiber); 50 X(ConvertThreadToFiber); 51 X(ConvertFiberToThread); 52 X(SwitchToFiber); 53 X(DeleteFiber); 54 X(ConvertThreadToFiberEx); 55 X(CreateFiberEx); 56 X(IsThreadAFiber); 57 X(FlsAlloc); 58 X(FlsFree); 59 X(FlsGetValue); 60 X(FlsSetValue); 61 #undef X 62 } 63 64 static VOID WINAPI FiberLocalStorageProc(PVOID lpFlsData) 65 { 66 ok(lpFlsData == fls_value_to_set, 67 "FlsData expected not to be changed, value is %p, expected %p\n", 68 lpFlsData, fls_value_to_set); 69 cbCount++; 70 } 71 72 static VOID WINAPI FiberMainProc(LPVOID lpFiberParameter) 73 { 74 BYTE *tparam = (BYTE *)lpFiberParameter; 75 fiberCount++; 76 ok(*tparam == 185, "Parameterdata expected not to be changed\n"); 77 if (fls_index_to_set != FLS_OUT_OF_INDEXES) 78 { 79 void* ret; 80 BOOL bret; 81 82 ret = pFlsGetValue(fls_index_to_set); 83 ok(ret == NULL, "FlsGetValue returned %p, expected NULL\n", ret); 84 85 /* Set the FLS value */ 86 bret = pFlsSetValue(fls_index_to_set, fls_value_to_set); 87 ok(bret, "FlsSetValue failed with error %u\n", GetLastError()); 88 89 /* Verify that FlsGetValue retrieves the value set by FlsSetValue */ 90 SetLastError( 0xdeadbeef ); 91 ret = pFlsGetValue(fls_index_to_set); 92 ok(ret == fls_value_to_set, "FlsGetValue returned %p, expected %p\n", ret, fls_value_to_set); 93 ok(GetLastError() == ERROR_SUCCESS, "FlsGetValue error %u\n", GetLastError()); 94 } 95 pSwitchToFiber(fibers[0]); 96 } 97 98 static void test_ConvertThreadToFiber(void) 99 { 100 if (pConvertThreadToFiber) 101 { 102 fibers[0] = pConvertThreadToFiber(&testparam); 103 ok(fibers[0] != NULL, "ConvertThreadToFiber failed with error %u\n", GetLastError()); 104 } 105 else 106 { 107 win_skip( "ConvertThreadToFiber not present\n" ); 108 } 109 } 110 111 static void test_ConvertThreadToFiberEx(void) 112 { 113 if (pConvertThreadToFiberEx) 114 { 115 fibers[0] = pConvertThreadToFiberEx(&testparam, 0); 116 ok(fibers[0] != NULL, "ConvertThreadToFiberEx failed with error %u\n", GetLastError()); 117 } 118 else 119 { 120 win_skip( "ConvertThreadToFiberEx not present\n" ); 121 } 122 } 123 124 static void test_ConvertFiberToThread(void) 125 { 126 if (pConvertFiberToThread) 127 { 128 BOOL ret = pConvertFiberToThread(); 129 ok(ret, "ConvertFiberToThread failed with error %u\n", GetLastError()); 130 } 131 else 132 { 133 win_skip( "ConvertFiberToThread not present\n" ); 134 } 135 } 136 137 static void test_FiberHandling(void) 138 { 139 fiberCount = 0; 140 fibers[0] = pCreateFiber(0,FiberMainProc,&testparam); 141 ok(fibers[0] != NULL, "CreateFiber failed with error %u\n", GetLastError()); 142 pDeleteFiber(fibers[0]); 143 144 test_ConvertThreadToFiber(); 145 test_ConvertFiberToThread(); 146 if (pConvertThreadToFiberEx) 147 test_ConvertThreadToFiberEx(); 148 else 149 test_ConvertThreadToFiber(); 150 151 fibers[1] = pCreateFiber(0,FiberMainProc,&testparam); 152 ok(fibers[1] != NULL, "CreateFiber failed with error %u\n", GetLastError()); 153 154 pSwitchToFiber(fibers[1]); 155 ok(fiberCount == 1, "Wrong fiber count: %d\n", fiberCount); 156 pDeleteFiber(fibers[1]); 157 158 if (pCreateFiberEx) 159 { 160 fibers[1] = pCreateFiberEx(0,0,0,FiberMainProc,&testparam); 161 ok(fibers[1] != NULL, "CreateFiberEx failed with error %u\n", GetLastError()); 162 163 pSwitchToFiber(fibers[1]); 164 ok(fiberCount == 2, "Wrong fiber count: %d\n", fiberCount); 165 pDeleteFiber(fibers[1]); 166 } 167 else win_skip( "CreateFiberEx not present\n" ); 168 169 if (pIsThreadAFiber) ok(pIsThreadAFiber(), "IsThreadAFiber reported FALSE\n"); 170 test_ConvertFiberToThread(); 171 if (pIsThreadAFiber) ok(!pIsThreadAFiber(), "IsThreadAFiber reported TRUE\n"); 172 } 173 174 static void test_FiberLocalStorage(void) 175 { 176 DWORD fls, fls_2; 177 BOOL ret; 178 void* val; 179 180 if (!pFlsAlloc || !pFlsSetValue || !pFlsGetValue || !pFlsFree) 181 { 182 win_skip( "Fiber Local Storage not supported\n" ); 183 return; 184 } 185 186 /* Test an unallocated index 187 * FlsFree should fail 188 * FlsGetValue and FlsSetValue should succeed 189 */ 190 SetLastError( 0xdeadbeef ); 191 ret = pFlsFree( 127 ); 192 ok( !ret, "freeing fls index 127 (unallocated) succeeded\n" ); 193 ok( GetLastError() == ERROR_INVALID_PARAMETER, 194 "freeing fls index 127 (unallocated) wrong error %u\n", GetLastError() ); 195 196 val = pFlsGetValue( 127 ); 197 ok( val == NULL, 198 "getting fls index 127 (unallocated) failed with error %u\n", GetLastError() ); 199 200 ret = pFlsSetValue( 127, (void*) 0x217 ); 201 ok( ret, "setting fls index 127 (unallocated) failed with error %u\n", GetLastError() ); 202 203 SetLastError( 0xdeadbeef ); 204 val = pFlsGetValue( 127 ); 205 ok( val == (void*) 0x217, "fls index 127 (unallocated) wrong value %p\n", val ); 206 ok( GetLastError() == ERROR_SUCCESS, 207 "getting fls index 127 (unallocated) failed with error %u\n", GetLastError() ); 208 209 /* FlsFree, FlsGetValue, and FlsSetValue out of bounds should return 210 * ERROR_INVALID_PARAMETER 211 */ 212 SetLastError( 0xdeadbeef ); 213 ret = pFlsFree( 128 ); 214 ok( !ret, "freeing fls index 128 (out of bounds) succeeded\n" ); 215 ok( GetLastError() == ERROR_INVALID_PARAMETER, 216 "freeing fls index 128 (out of bounds) wrong error %u\n", GetLastError() ); 217 218 SetLastError( 0xdeadbeef ); 219 ret = pFlsSetValue( 128, (void*) 0x217 ); 220 ok( !ret, "setting fls index 128 (out of bounds) succeeded\n" ); 221 ok( GetLastError() == ERROR_INVALID_PARAMETER, 222 "setting fls index 128 (out of bounds) wrong error %u\n", GetLastError() ); 223 224 SetLastError( 0xdeadbeef ); 225 val = pFlsGetValue( 128 ); 226 ok( GetLastError() == ERROR_INVALID_PARAMETER, 227 "getting fls index 128 (out of bounds) wrong error %u\n", GetLastError() ); 228 229 /* Test index 0 */ 230 SetLastError( 0xdeadbeef ); 231 val = pFlsGetValue( 0 ); 232 ok( !val, "fls index 0 set to %p\n", val ); 233 ok( GetLastError() == ERROR_INVALID_PARAMETER, "setting fls index wrong error %u\n", GetLastError() ); 234 SetLastError( 0xdeadbeef ); 235 ret = pFlsSetValue( 0, (void *)0xdeadbeef ); 236 ok( !ret, "setting fls index 0 succeeded\n" ); 237 ok( GetLastError() == ERROR_INVALID_PARAMETER, "setting fls index wrong error %u\n", GetLastError() ); 238 SetLastError( 0xdeadbeef ); 239 val = pFlsGetValue( 0 ); 240 ok( !val, "fls index 0 wrong value %p\n", val ); 241 ok( GetLastError() == ERROR_INVALID_PARAMETER, "setting fls index wrong error %u\n", GetLastError() ); 242 243 /* Test creating an FLS index */ 244 fls = pFlsAlloc( NULL ); 245 ok( fls != FLS_OUT_OF_INDEXES, "FlsAlloc failed\n" ); 246 ok( fls != 0, "fls index 0 allocated\n" ); 247 val = pFlsGetValue( fls ); 248 ok( !val, "fls index %u wrong value %p\n", fls, val ); 249 ret = pFlsSetValue( fls, (void *)0xdeadbeef ); 250 ok( ret, "setting fls index %u failed\n", fls ); 251 SetLastError( 0xdeadbeef ); 252 val = pFlsGetValue( fls ); 253 ok( val == (void *)0xdeadbeef, "fls index %u wrong value %p\n", fls, val ); 254 ok( GetLastError() == ERROR_SUCCESS, 255 "getting fls index %u failed with error %u\n", fls, GetLastError() ); 256 pFlsFree( fls ); 257 258 /* Undefined behavior: verify the value is NULL after it the slot is freed */ 259 SetLastError( 0xdeadbeef ); 260 val = pFlsGetValue( fls ); 261 ok( val == NULL, "fls index %u wrong value %p\n", fls, val ); 262 ok( GetLastError() == ERROR_SUCCESS, 263 "getting fls index %u failed with error %u\n", fls, GetLastError() ); 264 265 /* Undefined behavior: verify the value is settable after the slot is freed */ 266 ret = pFlsSetValue( fls, (void *)0xdeadbabe ); 267 ok( ret, "setting fls index %u failed\n", fls ); 268 val = pFlsGetValue( fls ); 269 ok( val == (void *)0xdeadbabe, "fls index %u wrong value %p\n", fls, val ); 270 271 /* Try to create the same FLS index again, and verify that is initialized to NULL */ 272 fls_2 = pFlsAlloc( NULL ); 273 ok( fls != FLS_OUT_OF_INDEXES, "FlsAlloc failed with error %u\n", GetLastError() ); 274 /* If this fails it is not an API error, but the test will be inconclusive */ 275 ok( fls_2 == fls, "different FLS index allocated, was %u, now %u\n", fls, fls_2 ); 276 277 SetLastError( 0xdeadbeef ); 278 val = pFlsGetValue( fls_2 ); 279 ok( val == NULL, "fls index %u wrong value %p\n", fls, val ); 280 ok( GetLastError() == ERROR_SUCCESS, 281 "getting fls index %u failed with error %u\n", fls_2, GetLastError() ); 282 pFlsFree( fls_2 ); 283 } 284 285 static void test_FiberLocalStorageCallback(PFLS_CALLBACK_FUNCTION cbfunc) 286 { 287 DWORD fls; 288 BOOL ret; 289 void* val, *val2; 290 291 if (!pFlsAlloc || !pFlsSetValue || !pFlsGetValue || !pFlsFree) 292 { 293 win_skip( "Fiber Local Storage not supported\n" ); 294 return; 295 } 296 297 /* Test that the callback is executed */ 298 cbCount = 0; 299 fls = pFlsAlloc( cbfunc ); 300 ok( fls != FLS_OUT_OF_INDEXES, "FlsAlloc failed with error %u\n", GetLastError() ); 301 302 val = (void*) 0x1587; 303 fls_value_to_set = val; 304 ret = pFlsSetValue( fls, val ); 305 ok(ret, "FlsSetValue failed with error %u\n", GetLastError() ); 306 307 val2 = pFlsGetValue( fls ); 308 ok(val == val2, "FlsGetValue returned %p, expected %p\n", val2, val); 309 310 ret = pFlsFree( fls ); 311 ok(ret, "FlsFree failed with error %u\n", GetLastError() ); 312 todo_wine ok( cbCount == 1, "Wrong callback count: %d\n", cbCount ); 313 314 /* Test that callback is not executed if value is NULL */ 315 cbCount = 0; 316 fls = pFlsAlloc( cbfunc ); 317 ok( fls != FLS_OUT_OF_INDEXES, "FlsAlloc failed with error %u\n", GetLastError() ); 318 319 ret = pFlsSetValue( fls, NULL ); 320 ok( ret, "FlsSetValue failed with error %u\n", GetLastError() ); 321 322 pFlsFree( fls ); 323 ok( ret, "FlsFree failed with error %u\n", GetLastError() ); 324 ok( cbCount == 0, "Wrong callback count: %d\n", cbCount ); 325 } 326 327 static void test_FiberLocalStorageWithFibers(PFLS_CALLBACK_FUNCTION cbfunc) 328 { 329 void* val1 = (void*) 0x314; 330 void* val2 = (void*) 0x152; 331 BOOL ret; 332 333 if (!pFlsAlloc || !pFlsFree || !pFlsSetValue || !pFlsGetValue) 334 { 335 win_skip( "Fiber Local Storage not supported\n" ); 336 return; 337 } 338 339 fls_index_to_set = pFlsAlloc(cbfunc); 340 ok(fls_index_to_set != FLS_OUT_OF_INDEXES, "FlsAlloc failed with error %u\n", GetLastError()); 341 342 test_ConvertThreadToFiber(); 343 344 fiberCount = 0; 345 cbCount = 0; 346 fibers[1] = pCreateFiber(0,FiberMainProc,&testparam); 347 fibers[2] = pCreateFiber(0,FiberMainProc,&testparam); 348 ok(fibers[1] != NULL, "CreateFiber failed with error %u\n", GetLastError()); 349 ok(fibers[2] != NULL, "CreateFiber failed with error %u\n", GetLastError()); 350 ok(fiberCount == 0, "Wrong fiber count: %d\n", fiberCount); 351 ok(cbCount == 0, "Wrong callback count: %d\n", cbCount); 352 353 fiberCount = 0; 354 cbCount = 0; 355 fls_value_to_set = val1; 356 pSwitchToFiber(fibers[1]); 357 ok(fiberCount == 1, "Wrong fiber count: %d\n", fiberCount); 358 ok(cbCount == 0, "Wrong callback count: %d\n", cbCount); 359 360 fiberCount = 0; 361 cbCount = 0; 362 fls_value_to_set = val2; 363 pSwitchToFiber(fibers[2]); 364 ok(fiberCount == 1, "Wrong fiber count: %d\n", fiberCount); 365 ok(cbCount == 0, "Wrong callback count: %d\n", cbCount); 366 367 fls_value_to_set = val2; 368 ret = pFlsSetValue(fls_index_to_set, fls_value_to_set); 369 ok(ret, "FlsSetValue failed\n"); 370 ok(val2 == pFlsGetValue(fls_index_to_set), "FlsGetValue failed\n"); 371 372 fiberCount = 0; 373 cbCount = 0; 374 fls_value_to_set = val1; 375 pDeleteFiber(fibers[1]); 376 ok(fiberCount == 0, "Wrong fiber count: %d\n", fiberCount); 377 todo_wine ok(cbCount == 1, "Wrong callback count: %d\n", cbCount); 378 379 fiberCount = 0; 380 cbCount = 0; 381 fls_value_to_set = val2; 382 pFlsFree(fls_index_to_set); 383 ok(fiberCount == 0, "Wrong fiber count: %d\n", fiberCount); 384 todo_wine ok(cbCount == 2, "Wrong callback count: %d\n", cbCount); 385 386 fiberCount = 0; 387 cbCount = 0; 388 fls_value_to_set = val1; 389 pDeleteFiber(fibers[2]); 390 ok(fiberCount == 0, "Wrong fiber count: %d\n", fiberCount); 391 ok(cbCount == 0, "Wrong callback count: %d\n", cbCount); 392 393 test_ConvertFiberToThread(); 394 } 395 396 START_TEST(fiber) 397 { 398 init_funcs(); 399 400 if (!pCreateFiber) 401 { 402 win_skip( "Fibers not supported by win95\n" ); 403 return; 404 } 405 406 test_FiberHandling(); 407 test_FiberLocalStorage(); 408 test_FiberLocalStorageCallback(FiberLocalStorageProc); 409 test_FiberLocalStorageWithFibers(FiberLocalStorageProc); 410 } 411