1 /* 2 * PROJECT: ReactOS System Libraries 3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) 4 * PURPOSE: Fiber Implementation 5 * COPYRIGHT: Copyright 2005-2011 Alex Ionescu (alex@relsoft.net) 6 * Copyright 2003-2008 KJK::Hyperion (noog@libero.it) 7 * Copyright 2018 Mark Jansen (mark.jansen@reactos.org) 8 */ 9 10 #include <k32.h> 11 #include <ndk/rtltypes.h> 12 13 #define NDEBUG 14 #include <debug.h> 15 16 #ifdef _M_IX86 17 C_ASSERT(FIELD_OFFSET(FIBER, ExceptionList) == 0x04); 18 C_ASSERT(FIELD_OFFSET(FIBER, StackBase) == 0x08); 19 C_ASSERT(FIELD_OFFSET(FIBER, StackLimit) == 0x0C); 20 C_ASSERT(FIELD_OFFSET(FIBER, DeallocationStack) == 0x10); 21 C_ASSERT(FIELD_OFFSET(FIBER, FiberContext) == 0x14); 22 C_ASSERT(FIELD_OFFSET(FIBER, GuaranteedStackBytes) == 0x2E0); 23 C_ASSERT(FIELD_OFFSET(FIBER, FlsData) == 0x2E4); 24 C_ASSERT(FIELD_OFFSET(FIBER, ActivationContextStackPointer) == 0x2E8); 25 C_ASSERT(RTL_FLS_MAXIMUM_AVAILABLE == FLS_MAXIMUM_AVAILABLE); 26 #endif // _M_IX86 27 28 /* PRIVATE FUNCTIONS **********************************************************/ 29 30 VOID 31 WINAPI 32 BaseRundownFls(_In_ PVOID FlsData) 33 { 34 ULONG n, FlsHighIndex; 35 PRTL_FLS_DATA pFlsData; 36 PFLS_CALLBACK_FUNCTION lpCallback; 37 38 pFlsData = FlsData; 39 40 RtlAcquirePebLock(); 41 FlsHighIndex = NtCurrentPeb()->FlsHighIndex; 42 RemoveEntryList(&pFlsData->ListEntry); 43 RtlReleasePebLock(); 44 45 for (n = 1; n <= FlsHighIndex; ++n) 46 { 47 lpCallback = NtCurrentPeb()->FlsCallback[n]; 48 if (lpCallback && pFlsData->Data[n]) 49 { 50 lpCallback(pFlsData->Data[n]); 51 } 52 } 53 54 RtlFreeHeap(RtlGetProcessHeap(), 0, FlsData); 55 } 56 57 /* PUBLIC FUNCTIONS ***********************************************************/ 58 59 /* 60 * @implemented 61 */ 62 BOOL 63 WINAPI 64 ConvertFiberToThread(VOID) 65 { 66 PTEB Teb; 67 PFIBER FiberData; 68 DPRINT1("Converting Fiber to Thread\n"); 69 70 /* Check if the thread is already not a fiber */ 71 Teb = NtCurrentTeb(); 72 if (!Teb->HasFiberData) 73 { 74 /* Fail */ 75 SetLastError(ERROR_ALREADY_THREAD); 76 return FALSE; 77 } 78 79 /* This thread won't run a fiber anymore */ 80 Teb->HasFiberData = FALSE; 81 FiberData = Teb->NtTib.FiberData; 82 Teb->NtTib.FiberData = NULL; 83 84 /* Free the fiber */ 85 ASSERT(FiberData != NULL); 86 RtlFreeHeap(RtlGetProcessHeap(), 87 0, 88 FiberData); 89 90 /* Success */ 91 return TRUE; 92 } 93 94 /* 95 * @implemented 96 */ 97 LPVOID 98 WINAPI 99 ConvertThreadToFiberEx(_In_opt_ LPVOID lpParameter, 100 _In_ DWORD dwFlags) 101 { 102 PTEB Teb; 103 PFIBER Fiber; 104 DPRINT1("Converting Thread to Fiber\n"); 105 106 /* Check for invalid flags */ 107 if (dwFlags & ~FIBER_FLAG_FLOAT_SWITCH) 108 { 109 /* Fail */ 110 SetLastError(ERROR_INVALID_PARAMETER); 111 return NULL; 112 } 113 114 /* Are we already a fiber? */ 115 Teb = NtCurrentTeb(); 116 if (Teb->HasFiberData) 117 { 118 /* Fail */ 119 SetLastError(ERROR_ALREADY_FIBER); 120 return NULL; 121 } 122 123 /* Allocate the fiber */ 124 Fiber = RtlAllocateHeap(RtlGetProcessHeap(), 125 0, 126 sizeof(FIBER)); 127 if (!Fiber) 128 { 129 /* Fail */ 130 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 131 return NULL; 132 } 133 134 /* Copy some contextual data from the thread to the fiber */ 135 Fiber->FiberData = lpParameter; 136 Fiber->ExceptionList = Teb->NtTib.ExceptionList; 137 Fiber->StackBase = Teb->NtTib.StackBase; 138 Fiber->StackLimit = Teb->NtTib.StackLimit; 139 Fiber->DeallocationStack = Teb->DeallocationStack; 140 Fiber->FlsData = Teb->FlsData; 141 Fiber->GuaranteedStackBytes = Teb->GuaranteedStackBytes; 142 Fiber->ActivationContextStackPointer = Teb->ActivationContextStackPointer; 143 144 /* Save FPU State if requested, otherwise just the basic registers */ 145 Fiber->FiberContext.ContextFlags = (dwFlags & FIBER_FLAG_FLOAT_SWITCH) ? 146 (CONTEXT_FULL | CONTEXT_FLOATING_POINT) : 147 CONTEXT_FULL; 148 149 /* Associate the fiber to the current thread */ 150 Teb->NtTib.FiberData = Fiber; 151 Teb->HasFiberData = TRUE; 152 153 /* Return opaque fiber data */ 154 return (LPVOID)Fiber; 155 } 156 157 /* 158 * @implemented 159 */ 160 LPVOID 161 WINAPI 162 ConvertThreadToFiber(_In_opt_ LPVOID lpParameter) 163 { 164 /* Call the newer function */ 165 return ConvertThreadToFiberEx(lpParameter, 166 0); 167 } 168 169 /* 170 * @implemented 171 */ 172 LPVOID 173 WINAPI 174 CreateFiber(_In_ SIZE_T dwStackSize, 175 _In_ LPFIBER_START_ROUTINE lpStartAddress, 176 _In_opt_ LPVOID lpParameter) 177 { 178 /* Call the Newer Function */ 179 return CreateFiberEx(dwStackSize, 180 0, 181 0, 182 lpStartAddress, 183 lpParameter); 184 } 185 186 /* 187 * @implemented 188 */ 189 LPVOID 190 WINAPI 191 CreateFiberEx(_In_ SIZE_T dwStackCommitSize, 192 _In_ SIZE_T dwStackReserveSize, 193 _In_ DWORD dwFlags, 194 _In_ LPFIBER_START_ROUTINE lpStartAddress, 195 _In_opt_ LPVOID lpParameter) 196 { 197 PFIBER Fiber; 198 NTSTATUS Status; 199 INITIAL_TEB InitialTeb; 200 PACTIVATION_CONTEXT_STACK ActivationContextStackPointer; 201 DPRINT("Creating Fiber\n"); 202 203 /* Check for invalid flags */ 204 if (dwFlags & ~FIBER_FLAG_FLOAT_SWITCH) 205 { 206 /* Fail */ 207 SetLastError(ERROR_INVALID_PARAMETER); 208 return NULL; 209 } 210 211 /* Allocate the Activation Context Stack */ 212 ActivationContextStackPointer = NULL; 213 Status = RtlAllocateActivationContextStack(&ActivationContextStackPointer); 214 if (!NT_SUCCESS(Status)) 215 { 216 /* Fail */ 217 BaseSetLastNTError(Status); 218 return NULL; 219 } 220 221 /* Allocate the fiber */ 222 Fiber = RtlAllocateHeap(RtlGetProcessHeap(), 223 0, 224 sizeof(FIBER)); 225 if (!Fiber) 226 { 227 /* Free the activation context stack */ 228 RtlFreeActivationContextStack(ActivationContextStackPointer); 229 230 /* Fail */ 231 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 232 return NULL; 233 } 234 235 /* Create the stack for the fiber */ 236 Status = BaseCreateStack(NtCurrentProcess(), 237 dwStackCommitSize, 238 dwStackReserveSize, 239 &InitialTeb); 240 if (!NT_SUCCESS(Status)) 241 { 242 /* Free the fiber */ 243 RtlFreeHeap(GetProcessHeap(), 244 0, 245 Fiber); 246 247 /* Free the activation context stack */ 248 RtlFreeActivationContextStack(ActivationContextStackPointer); 249 250 /* Failure */ 251 BaseSetLastNTError(Status); 252 return NULL; 253 } 254 255 /* Clear the context */ 256 RtlZeroMemory(&Fiber->FiberContext, 257 sizeof(CONTEXT)); 258 259 /* Copy the data into the fiber */ 260 Fiber->StackBase = InitialTeb.StackBase; 261 Fiber->StackLimit = InitialTeb.StackLimit; 262 Fiber->DeallocationStack = InitialTeb.AllocatedStackBase; 263 Fiber->FiberData = lpParameter; 264 Fiber->ExceptionList = EXCEPTION_CHAIN_END; 265 Fiber->GuaranteedStackBytes = 0; 266 Fiber->FlsData = NULL; 267 Fiber->ActivationContextStackPointer = ActivationContextStackPointer; 268 269 /* Save FPU State if requested, otherwise just the basic registers */ 270 Fiber->FiberContext.ContextFlags = (dwFlags & FIBER_FLAG_FLOAT_SWITCH) ? 271 (CONTEXT_FULL | CONTEXT_FLOATING_POINT) : 272 CONTEXT_FULL; 273 274 /* Initialize the context for the fiber */ 275 BaseInitializeContext(&Fiber->FiberContext, 276 lpParameter, 277 lpStartAddress, 278 InitialTeb.StackBase, 279 2); 280 281 /* Return the Fiber */ 282 return Fiber; 283 } 284 285 /* 286 * @implemented 287 */ 288 VOID 289 WINAPI 290 DeleteFiber(_In_ LPVOID lpFiber) 291 { 292 SIZE_T Size; 293 PFIBER Fiber; 294 PTEB Teb; 295 296 /* Are we deleting ourselves? */ 297 Teb = NtCurrentTeb(); 298 Fiber = (PFIBER)lpFiber; 299 if ((Teb->HasFiberData) && 300 (Teb->NtTib.FiberData == Fiber)) 301 { 302 /* Just exit */ 303 ExitThread(1); 304 } 305 306 /* Not ourselves, de-allocate the stack */ 307 Size = 0 ; 308 NtFreeVirtualMemory(NtCurrentProcess(), 309 &Fiber->DeallocationStack, 310 &Size, 311 MEM_RELEASE); 312 313 /* Get rid of FLS */ 314 if (Fiber->FlsData) BaseRundownFls(Fiber->FlsData); 315 316 /* Get rid of the activation context stack */ 317 RtlFreeActivationContextStack(Fiber->ActivationContextStackPointer); 318 319 /* Free the fiber data */ 320 RtlFreeHeap(RtlGetProcessHeap(), 321 0, 322 lpFiber); 323 } 324 325 /* 326 * @implemented 327 */ 328 BOOL 329 WINAPI 330 IsThreadAFiber(VOID) 331 { 332 /* Return flag in the TEB */ 333 return NtCurrentTeb()->HasFiberData; 334 } 335 336 /* 337 * @implemented 338 */ 339 DWORD 340 WINAPI 341 FlsAlloc(PFLS_CALLBACK_FUNCTION lpCallback) 342 { 343 DWORD dwFlsIndex; 344 PPEB Peb = NtCurrentPeb(); 345 PRTL_FLS_DATA pFlsData; 346 347 RtlAcquirePebLock(); 348 349 pFlsData = NtCurrentTeb()->FlsData; 350 351 if (!Peb->FlsCallback && 352 !(Peb->FlsCallback = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, 353 FLS_MAXIMUM_AVAILABLE * sizeof(PVOID)))) 354 { 355 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 356 dwFlsIndex = FLS_OUT_OF_INDEXES; 357 } 358 else 359 { 360 dwFlsIndex = RtlFindClearBitsAndSet(Peb->FlsBitmap, 1, 1); 361 if (dwFlsIndex != FLS_OUT_OF_INDEXES) 362 { 363 if (!pFlsData && 364 !(pFlsData = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(RTL_FLS_DATA)))) 365 { 366 RtlClearBits(Peb->FlsBitmap, dwFlsIndex, 1); 367 dwFlsIndex = FLS_OUT_OF_INDEXES; 368 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 369 } 370 else 371 { 372 if (!NtCurrentTeb()->FlsData) 373 { 374 NtCurrentTeb()->FlsData = pFlsData; 375 InsertTailList(&Peb->FlsListHead, &pFlsData->ListEntry); 376 } 377 378 pFlsData->Data[dwFlsIndex] = NULL; /* clear the value */ 379 Peb->FlsCallback[dwFlsIndex] = lpCallback; 380 381 if (dwFlsIndex > Peb->FlsHighIndex) 382 Peb->FlsHighIndex = dwFlsIndex; 383 } 384 } 385 else 386 { 387 SetLastError(ERROR_NO_MORE_ITEMS); 388 } 389 } 390 RtlReleasePebLock(); 391 return dwFlsIndex; 392 } 393 394 395 /* 396 * @implemented 397 */ 398 BOOL 399 WINAPI 400 FlsFree(DWORD dwFlsIndex) 401 { 402 BOOL ret; 403 PPEB Peb = NtCurrentPeb(); 404 405 if (dwFlsIndex >= FLS_MAXIMUM_AVAILABLE) 406 { 407 SetLastError(ERROR_INVALID_PARAMETER); 408 return FALSE; 409 } 410 411 RtlAcquirePebLock(); 412 413 _SEH2_TRY 414 { 415 ret = RtlAreBitsSet(Peb->FlsBitmap, dwFlsIndex, 1); 416 if (ret) 417 { 418 PLIST_ENTRY Entry; 419 PFLS_CALLBACK_FUNCTION lpCallback; 420 421 RtlClearBits(Peb->FlsBitmap, dwFlsIndex, 1); 422 lpCallback = Peb->FlsCallback[dwFlsIndex]; 423 424 for (Entry = Peb->FlsListHead.Flink; Entry != &Peb->FlsListHead; Entry = Entry->Flink) 425 { 426 PRTL_FLS_DATA pFlsData; 427 428 pFlsData = CONTAINING_RECORD(Entry, RTL_FLS_DATA, ListEntry); 429 if (pFlsData->Data[dwFlsIndex]) 430 { 431 if (lpCallback) 432 { 433 lpCallback(pFlsData->Data[dwFlsIndex]); 434 } 435 pFlsData->Data[dwFlsIndex] = NULL; 436 } 437 } 438 Peb->FlsCallback[dwFlsIndex] = NULL; 439 } 440 else 441 { 442 SetLastError(ERROR_INVALID_PARAMETER); 443 } 444 } 445 _SEH2_FINALLY 446 { 447 RtlReleasePebLock(); 448 } 449 _SEH2_END; 450 451 return ret; 452 } 453 454 455 /* 456 * @implemented 457 */ 458 PVOID 459 WINAPI 460 FlsGetValue(DWORD dwFlsIndex) 461 { 462 PRTL_FLS_DATA pFlsData; 463 464 pFlsData = NtCurrentTeb()->FlsData; 465 if (!dwFlsIndex || dwFlsIndex >= FLS_MAXIMUM_AVAILABLE || !pFlsData) 466 { 467 SetLastError(ERROR_INVALID_PARAMETER); 468 return NULL; 469 } 470 471 SetLastError(ERROR_SUCCESS); 472 return pFlsData->Data[dwFlsIndex]; 473 } 474 475 476 /* 477 * @implemented 478 */ 479 BOOL 480 WINAPI 481 FlsSetValue(DWORD dwFlsIndex, 482 PVOID lpFlsData) 483 { 484 PRTL_FLS_DATA pFlsData; 485 486 if (!dwFlsIndex || dwFlsIndex >= FLS_MAXIMUM_AVAILABLE) 487 { 488 SetLastError(ERROR_INVALID_PARAMETER); 489 return FALSE; 490 } 491 492 pFlsData = NtCurrentTeb()->FlsData; 493 494 if (!NtCurrentTeb()->FlsData && 495 !(NtCurrentTeb()->FlsData = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, 496 sizeof(RTL_FLS_DATA)))) 497 { 498 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 499 return FALSE; 500 } 501 if (!pFlsData) 502 { 503 pFlsData = NtCurrentTeb()->FlsData; 504 RtlAcquirePebLock(); 505 InsertTailList(&NtCurrentPeb()->FlsListHead, &pFlsData->ListEntry); 506 RtlReleasePebLock(); 507 } 508 pFlsData->Data[dwFlsIndex] = lpFlsData; 509 return TRUE; 510 } 511 512 /* EOF */ 513