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