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