8daa28e1deda608bf73b7f14821cf1f5efb57f30
[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
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 * @unimplemented
316 */
317 DWORD
318 WINAPI
319 FlsAlloc(PFLS_CALLBACK_FUNCTION lpCallback)
320 {
321 (void)lpCallback;
322
323 UNIMPLEMENTED;
324 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
325 return FLS_OUT_OF_INDEXES;
326 }
327
328
329 /*
330 * @unimplemented
331 */
332 BOOL
333 WINAPI
334 FlsFree(DWORD dwFlsIndex)
335 {
336 (void)dwFlsIndex;
337
338 UNIMPLEMENTED;
339 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
340 return FALSE;
341 }
342
343
344 /*
345 * @implemented
346 */
347 PVOID
348 WINAPI
349 FlsGetValue(DWORD dwFlsIndex)
350 {
351 PVOID *ppFlsSlots;
352 PVOID pRetVal;
353
354 if(dwFlsIndex >= 128) goto l_InvalidParam;
355
356 ppFlsSlots = NtCurrentTeb()->FlsData;
357
358 if(ppFlsSlots == NULL) goto l_InvalidParam;
359
360 SetLastError(0);
361 pRetVal = ppFlsSlots[dwFlsIndex + 2];
362
363 return pRetVal;
364
365 l_InvalidParam:
366 SetLastError(ERROR_INVALID_PARAMETER);
367 return NULL;
368 }
369
370
371 /*
372 * @implemented
373 */
374 BOOL
375 WINAPI
376 FlsSetValue(DWORD dwFlsIndex,
377 PVOID lpFlsData)
378 {
379 PVOID *ppFlsSlots;
380 TEB *pTeb = NtCurrentTeb();
381
382 if(dwFlsIndex >= 128) goto l_InvalidParam;
383
384 ppFlsSlots = pTeb->FlsData;
385
386 if (ppFlsSlots == NULL)
387 {
388 PEB *pPeb = pTeb->ProcessEnvironmentBlock;
389
390 ppFlsSlots = RtlAllocateHeap(pPeb->ProcessHeap,
391 HEAP_ZERO_MEMORY,
392 (128 + 2) * sizeof(PVOID));
393 if(ppFlsSlots == NULL) goto l_OutOfMemory;
394
395 pTeb->FlsData = ppFlsSlots;
396
397 RtlAcquirePebLock();
398
399 /* TODO: initialization */
400
401 RtlReleasePebLock();
402 }
403
404 ppFlsSlots[dwFlsIndex + 2] = lpFlsData;
405
406 return TRUE;
407
408 l_OutOfMemory:
409 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
410 goto l_Fail;
411
412 l_InvalidParam:
413 SetLastError(ERROR_INVALID_PARAMETER);
414
415 l_Fail:
416 return FALSE;
417 }
418
419 /* EOF */