[NTDLL][KERNEL32] Implement FLS callbacks.
[reactos.git] / dll / win32 / kernel32 / client / fiber.c
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 #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 ret = RtlAreBitsSet(Peb->FlsBitmap, dwFlsIndex, 1);
414 if (ret)
415 {
416 PLIST_ENTRY Entry;
417 PFLS_CALLBACK_FUNCTION lpCallback;
418
419 RtlClearBits(Peb->FlsBitmap, dwFlsIndex, 1);
420 lpCallback = Peb->FlsCallback[dwFlsIndex];
421 Peb->FlsCallback[dwFlsIndex] = NULL;
422
423 for (Entry = Peb->FlsListHead.Flink; Entry != &Peb->FlsListHead; Entry = Entry->Flink)
424 {
425 PRTL_FLS_DATA pFlsData;
426
427 pFlsData = CONTAINING_RECORD(Entry, RTL_FLS_DATA, ListEntry);
428 if (pFlsData->Data[dwFlsIndex])
429 {
430 if (lpCallback)
431 {
432 lpCallback(pFlsData->Data[dwFlsIndex]);
433 }
434 pFlsData->Data[dwFlsIndex] = NULL;
435 }
436 }
437 }
438 else
439 {
440 SetLastError(ERROR_INVALID_PARAMETER);
441 }
442 RtlReleasePebLock();
443 return ret;
444 }
445
446
447 /*
448 * @implemented
449 */
450 PVOID
451 WINAPI
452 FlsGetValue(DWORD dwFlsIndex)
453 {
454 PRTL_FLS_DATA pFlsData;
455
456 pFlsData = NtCurrentTeb()->FlsData;
457 if (!dwFlsIndex || dwFlsIndex >= FLS_MAXIMUM_AVAILABLE || !pFlsData)
458 {
459 SetLastError(ERROR_INVALID_PARAMETER);
460 return NULL;
461 }
462
463 SetLastError(ERROR_SUCCESS);
464 return pFlsData->Data[dwFlsIndex];
465 }
466
467
468 /*
469 * @implemented
470 */
471 BOOL
472 WINAPI
473 FlsSetValue(DWORD dwFlsIndex,
474 PVOID lpFlsData)
475 {
476 PRTL_FLS_DATA pFlsData;
477
478 if (!dwFlsIndex || dwFlsIndex >= FLS_MAXIMUM_AVAILABLE)
479 {
480 SetLastError(ERROR_INVALID_PARAMETER);
481 return FALSE;
482 }
483
484 pFlsData = NtCurrentTeb()->FlsData;
485
486 if (!NtCurrentTeb()->FlsData &&
487 !(NtCurrentTeb()->FlsData = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY,
488 sizeof(RTL_FLS_DATA))))
489 {
490 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
491 return FALSE;
492 }
493 if (!pFlsData)
494 {
495 pFlsData = NtCurrentTeb()->FlsData;
496 RtlAcquirePebLock();
497 InsertTailList(&NtCurrentPeb()->FlsListHead, &pFlsData->ListEntry);
498 RtlReleasePebLock();
499 }
500 pFlsData->Data[dwFlsIndex] = lpFlsData;
501 return TRUE;
502 }
503
504 /* EOF */