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)
11 #include <ndk/rtltypes.h>
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
);
28 /* PRIVATE FUNCTIONS **********************************************************/
32 BaseRundownFls(_In_ PVOID FlsData
)
34 ULONG n
, FlsHighIndex
;
35 PRTL_FLS_DATA pFlsData
;
36 PFLS_CALLBACK_FUNCTION lpCallback
;
41 FlsHighIndex
= NtCurrentPeb()->FlsHighIndex
;
42 RemoveEntryList(&pFlsData
->ListEntry
);
45 for (n
= 1; n
<= FlsHighIndex
; ++n
)
47 lpCallback
= NtCurrentPeb()->FlsCallback
[n
];
48 if (lpCallback
&& pFlsData
->Data
[n
])
50 lpCallback(pFlsData
->Data
[n
]);
54 RtlFreeHeap(RtlGetProcessHeap(), 0, FlsData
);
57 /* PUBLIC FUNCTIONS ***********************************************************/
64 ConvertFiberToThread(VOID
)
68 DPRINT1("Converting Fiber to Thread\n");
70 /* Check if the thread is already not a fiber */
72 if (!Teb
->HasFiberData
)
75 SetLastError(ERROR_ALREADY_THREAD
);
79 /* This thread won't run a fiber anymore */
80 Teb
->HasFiberData
= FALSE
;
81 FiberData
= Teb
->NtTib
.FiberData
;
82 Teb
->NtTib
.FiberData
= NULL
;
85 ASSERT(FiberData
!= NULL
);
86 RtlFreeHeap(RtlGetProcessHeap(),
99 ConvertThreadToFiberEx(_In_opt_ LPVOID lpParameter
,
104 DPRINT1("Converting Thread to Fiber\n");
106 /* Check for invalid flags */
107 if (dwFlags
& ~FIBER_FLAG_FLOAT_SWITCH
)
110 SetLastError(ERROR_INVALID_PARAMETER
);
114 /* Are we already a fiber? */
115 Teb
= NtCurrentTeb();
116 if (Teb
->HasFiberData
)
119 SetLastError(ERROR_ALREADY_FIBER
);
123 /* Allocate the fiber */
124 Fiber
= RtlAllocateHeap(RtlGetProcessHeap(),
130 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
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
;
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
) :
149 /* Associate the fiber to the current thread */
150 Teb
->NtTib
.FiberData
= Fiber
;
151 Teb
->HasFiberData
= TRUE
;
153 /* Return opaque fiber data */
154 return (LPVOID
)Fiber
;
162 ConvertThreadToFiber(_In_opt_ LPVOID lpParameter
)
164 /* Call the newer function */
165 return ConvertThreadToFiberEx(lpParameter
,
174 CreateFiber(_In_ SIZE_T dwStackSize
,
175 _In_ LPFIBER_START_ROUTINE lpStartAddress
,
176 _In_opt_ LPVOID lpParameter
)
178 /* Call the Newer Function */
179 return CreateFiberEx(dwStackSize
,
191 CreateFiberEx(_In_ SIZE_T dwStackCommitSize
,
192 _In_ SIZE_T dwStackReserveSize
,
194 _In_ LPFIBER_START_ROUTINE lpStartAddress
,
195 _In_opt_ LPVOID lpParameter
)
199 INITIAL_TEB InitialTeb
;
200 PACTIVATION_CONTEXT_STACK ActivationContextStackPointer
;
201 DPRINT("Creating Fiber\n");
203 /* Check for invalid flags */
204 if (dwFlags
& ~FIBER_FLAG_FLOAT_SWITCH
)
207 SetLastError(ERROR_INVALID_PARAMETER
);
211 /* Allocate the Activation Context Stack */
212 ActivationContextStackPointer
= NULL
;
213 Status
= RtlAllocateActivationContextStack(&ActivationContextStackPointer
);
214 if (!NT_SUCCESS(Status
))
217 BaseSetLastNTError(Status
);
221 /* Allocate the fiber */
222 Fiber
= RtlAllocateHeap(RtlGetProcessHeap(),
227 /* Free the activation context stack */
228 RtlFreeActivationContextStack(ActivationContextStackPointer
);
231 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
235 /* Create the stack for the fiber */
236 Status
= BaseCreateStack(NtCurrentProcess(),
240 if (!NT_SUCCESS(Status
))
243 RtlFreeHeap(GetProcessHeap(),
247 /* Free the activation context stack */
248 RtlFreeActivationContextStack(ActivationContextStackPointer
);
251 BaseSetLastNTError(Status
);
255 /* Clear the context */
256 RtlZeroMemory(&Fiber
->FiberContext
,
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
;
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
) :
274 /* Initialize the context for the fiber */
275 BaseInitializeContext(&Fiber
->FiberContext
,
278 InitialTeb
.StackBase
,
281 /* Return the Fiber */
290 DeleteFiber(_In_ LPVOID lpFiber
)
296 /* Are we deleting ourselves? */
297 Teb
= NtCurrentTeb();
298 Fiber
= (PFIBER
)lpFiber
;
299 if ((Teb
->HasFiberData
) &&
300 (Teb
->NtTib
.FiberData
== Fiber
))
306 /* Not ourselves, de-allocate the stack */
308 NtFreeVirtualMemory(NtCurrentProcess(),
309 &Fiber
->DeallocationStack
,
314 if (Fiber
->FlsData
) BaseRundownFls(Fiber
->FlsData
);
316 /* Get rid of the activation context stack */
317 RtlFreeActivationContextStack(Fiber
->ActivationContextStackPointer
);
319 /* Free the fiber data */
320 RtlFreeHeap(RtlGetProcessHeap(),
332 /* Return flag in the TEB */
333 return NtCurrentTeb()->HasFiberData
;
341 FlsAlloc(PFLS_CALLBACK_FUNCTION lpCallback
)
344 PPEB Peb
= NtCurrentPeb();
345 PRTL_FLS_DATA pFlsData
;
349 pFlsData
= NtCurrentTeb()->FlsData
;
351 if (!Peb
->FlsCallback
&&
352 !(Peb
->FlsCallback
= RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
,
353 FLS_MAXIMUM_AVAILABLE
* sizeof(PVOID
))))
355 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
356 dwFlsIndex
= FLS_OUT_OF_INDEXES
;
360 dwFlsIndex
= RtlFindClearBitsAndSet(Peb
->FlsBitmap
, 1, 1);
361 if (dwFlsIndex
!= FLS_OUT_OF_INDEXES
)
364 !(pFlsData
= RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(RTL_FLS_DATA
))))
366 RtlClearBits(Peb
->FlsBitmap
, dwFlsIndex
, 1);
367 dwFlsIndex
= FLS_OUT_OF_INDEXES
;
368 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
372 if (!NtCurrentTeb()->FlsData
)
374 NtCurrentTeb()->FlsData
= pFlsData
;
375 InsertTailList(&Peb
->FlsListHead
, &pFlsData
->ListEntry
);
378 pFlsData
->Data
[dwFlsIndex
] = NULL
; /* clear the value */
379 Peb
->FlsCallback
[dwFlsIndex
] = lpCallback
;
381 if (dwFlsIndex
> Peb
->FlsHighIndex
)
382 Peb
->FlsHighIndex
= dwFlsIndex
;
387 SetLastError(ERROR_NO_MORE_ITEMS
);
400 FlsFree(DWORD dwFlsIndex
)
403 PPEB Peb
= NtCurrentPeb();
405 if (dwFlsIndex
>= FLS_MAXIMUM_AVAILABLE
)
407 SetLastError(ERROR_INVALID_PARAMETER
);
415 ret
= RtlAreBitsSet(Peb
->FlsBitmap
, dwFlsIndex
, 1);
419 PFLS_CALLBACK_FUNCTION lpCallback
;
421 RtlClearBits(Peb
->FlsBitmap
, dwFlsIndex
, 1);
422 lpCallback
= Peb
->FlsCallback
[dwFlsIndex
];
424 for (Entry
= Peb
->FlsListHead
.Flink
; Entry
!= &Peb
->FlsListHead
; Entry
= Entry
->Flink
)
426 PRTL_FLS_DATA pFlsData
;
428 pFlsData
= CONTAINING_RECORD(Entry
, RTL_FLS_DATA
, ListEntry
);
429 if (pFlsData
->Data
[dwFlsIndex
])
433 lpCallback(pFlsData
->Data
[dwFlsIndex
]);
435 pFlsData
->Data
[dwFlsIndex
] = NULL
;
438 Peb
->FlsCallback
[dwFlsIndex
] = NULL
;
442 SetLastError(ERROR_INVALID_PARAMETER
);
460 FlsGetValue(DWORD dwFlsIndex
)
462 PRTL_FLS_DATA pFlsData
;
464 pFlsData
= NtCurrentTeb()->FlsData
;
465 if (!dwFlsIndex
|| dwFlsIndex
>= FLS_MAXIMUM_AVAILABLE
|| !pFlsData
)
467 SetLastError(ERROR_INVALID_PARAMETER
);
471 SetLastError(ERROR_SUCCESS
);
472 return pFlsData
->Data
[dwFlsIndex
];
481 FlsSetValue(DWORD dwFlsIndex
,
484 PRTL_FLS_DATA pFlsData
;
486 if (!dwFlsIndex
|| dwFlsIndex
>= FLS_MAXIMUM_AVAILABLE
)
488 SetLastError(ERROR_INVALID_PARAMETER
);
492 pFlsData
= NtCurrentTeb()->FlsData
;
494 if (!NtCurrentTeb()->FlsData
&&
495 !(NtCurrentTeb()->FlsData
= RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
,
496 sizeof(RTL_FLS_DATA
))))
498 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
503 pFlsData
= NtCurrentTeb()->FlsData
;
505 InsertTailList(&NtCurrentPeb()->FlsListHead
, &pFlsData
->ListEntry
);
508 pFlsData
->Data
[dwFlsIndex
] = lpFlsData
;