xref: /reactos/dll/win32/kernel32/client/fiber.c (revision 8a978a17)
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