101ca1621f817ae616ffe789e160013b62124b1e
[reactos.git] / reactos / dll / win32 / kernel32 / client / fiber.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS System Libraries
4 * FILE: lib/kernel32/thread/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 typedef struct _FIBER /* Field offsets: */
16 { /* 32 bit 64 bit */
17 /* this must be the first field */
18 LPVOID Parameter; /* 0x00 0x00 */
19 struct _EXCEPTION_REGISTRATION_RECORD * ExceptionList; /* 0x04 0x08 */
20 LPVOID StackBase; /* 0x08 0x10 */
21 LPVOID StackLimit; /* 0x0C 0x18 */
22 LPVOID DeallocationStack; /* 0x10 0x20 */
23 CONTEXT Context; /* 0x14 0x28 */
24 ULONG GuaranteedStackBytes; /* 0x2E0 */
25 PVOID FlsData; /* 0x2E4 */
26 PVOID ActivationContextStack; /* 0x2E8 */
27 } FIBER, *PFIBER;
28
29 /* PRIVATE FUNCTIONS **********************************************************/
30
31 VOID
32 WINAPI
33 BaseRundownFls(IN PVOID FlsData)
34 {
35 /* No FLS support yet */
36
37 }
38
39 /* PUBLIC FUNCTIONS ***********************************************************/
40
41 /*
42 * @implemented
43 */
44 BOOL
45 WINAPI
46 ConvertFiberToThread(VOID)
47 {
48 PTEB Teb;
49 PFIBER FiberData;
50 DPRINT1("Converting Fiber to Thread\n");
51
52 /* Check if the thread is already not a fiber */
53 Teb = NtCurrentTeb();
54 if (!Teb->HasFiberData)
55 {
56 /* Fail */
57 SetLastError(ERROR_ALREADY_THREAD);
58 return FALSE;
59 }
60
61 /* this thread won't run a fiber anymore */
62 Teb->HasFiberData = FALSE;
63 FiberData = Teb->NtTib.FiberData;
64 Teb->NtTib.FiberData = NULL;
65
66 /* Free the fiber */
67 ASSERT(FiberData != NULL);
68 RtlFreeHeap(GetProcessHeap(), 0, FiberData);
69
70 /* success */
71 return TRUE;
72 }
73
74 /*
75 * @implemented
76 */
77 LPVOID
78 WINAPI
79 ConvertThreadToFiberEx(LPVOID lpParameter,
80 DWORD dwFlags)
81 {
82 PTEB pTeb = NtCurrentTeb();
83 PFIBER pfCurFiber;
84 DPRINT1("Converting Thread to Fiber\n");
85
86 /* the current thread is already a fiber */
87 if(pTeb->HasFiberData && pTeb->NtTib.FiberData) return pTeb->NtTib.FiberData;
88
89 /* allocate the fiber */
90 pfCurFiber = (PFIBER)RtlAllocateHeap(GetProcessHeap(),
91 0,
92 sizeof(FIBER));
93
94 /* failure */
95 if (pfCurFiber == NULL)
96 {
97 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
98 return NULL;
99 }
100
101 /* copy some contextual data from the thread to the fiber */
102 pfCurFiber->Parameter = lpParameter;
103 pfCurFiber->ExceptionList = pTeb->NtTib.ExceptionList;
104 pfCurFiber->StackBase = pTeb->NtTib.StackBase;
105 pfCurFiber->StackLimit = pTeb->NtTib.StackLimit;
106 pfCurFiber->DeallocationStack = pTeb->DeallocationStack;
107 pfCurFiber->FlsData = pTeb->FlsData;
108 pfCurFiber->GuaranteedStackBytes = pTeb->GuaranteedStackBytes;
109 pfCurFiber->ActivationContextStack = pTeb->ActivationContextStackPointer;
110 pfCurFiber->Context.ContextFlags = CONTEXT_FULL;
111
112 /* Save FPU State if requsted */
113 if (dwFlags & FIBER_FLAG_FLOAT_SWITCH)
114 {
115 pfCurFiber->Context.ContextFlags |= CONTEXT_FLOATING_POINT;
116 }
117
118 /* associate the fiber to the current thread */
119 pTeb->NtTib.FiberData = pfCurFiber;
120 pTeb->HasFiberData = TRUE;
121
122 /* success */
123 return (LPVOID)pfCurFiber;
124 }
125
126 /*
127 * @implemented
128 */
129 LPVOID
130 WINAPI
131 ConvertThreadToFiber(LPVOID lpParameter)
132 {
133 /* Call the newer function */
134 return ConvertThreadToFiberEx(lpParameter, 0);
135 }
136
137 /*
138 * @implemented
139 */
140 LPVOID
141 WINAPI
142 CreateFiber(SIZE_T dwStackSize,
143 LPFIBER_START_ROUTINE lpStartAddress,
144 LPVOID lpParameter)
145 {
146 /* Call the Newer Function */
147 return CreateFiberEx(dwStackSize, 0, 0, lpStartAddress, lpParameter);
148 }
149
150 /*
151 * @implemented
152 */
153 LPVOID
154 WINAPI
155 CreateFiberEx(SIZE_T dwStackCommitSize,
156 SIZE_T dwStackReserveSize,
157 DWORD dwFlags,
158 LPFIBER_START_ROUTINE lpStartAddress,
159 LPVOID lpParameter)
160 {
161 PFIBER Fiber;
162 NTSTATUS Status;
163 INITIAL_TEB InitialTeb;
164 PVOID ActivationContextStack = NULL;
165 DPRINT("Creating Fiber\n");
166
167 /* Check for invalid flags */
168 if (dwFlags &~ FIBER_FLAG_FLOAT_SWITCH)
169 {
170 /* Fail */
171 SetLastError(ERROR_INVALID_PARAMETER);
172 return NULL;
173 }
174
175 /* Allocate the Activation Context Stack */
176 Status = RtlAllocateActivationContextStack(&ActivationContextStack);
177 if (!NT_SUCCESS(Status))
178 {
179 /* Fail */
180 BaseSetLastNTError(Status);
181 return NULL;
182 }
183
184 /* Allocate the fiber */
185 Fiber = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(FIBER));
186 if (!Fiber)
187 {
188 /* Fail */
189 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
190 return NULL;
191 }
192
193 /* Create the stack for the fiber */
194 Status = BasepCreateStack(NtCurrentProcess(),
195 dwStackCommitSize,
196 dwStackReserveSize,
197 &InitialTeb);
198 if (!NT_SUCCESS(Status))
199 {
200 /* Free the fiber */
201 RtlFreeHeap(GetProcessHeap(), 0, Fiber);
202
203 /* Free the activation context */
204 DPRINT1("Leaking activation stack because nobody implemented free");
205 //RtlFreeActivationContextStack(&ActivationContextStack);
206
207 /* Failure */
208 BaseSetLastNTError(Status);
209 return NULL;
210 }
211
212 /* Clear the context */
213 RtlZeroMemory(&Fiber->Context, sizeof(CONTEXT));
214
215 /* Copy the data into the fiber */
216 Fiber->StackBase = InitialTeb.StackBase;
217 Fiber->StackLimit = InitialTeb.StackLimit;
218 Fiber->DeallocationStack = InitialTeb.AllocatedStackBase;
219 Fiber->Parameter = lpParameter;
220 Fiber->ExceptionList = EXCEPTION_CHAIN_END;
221 Fiber->GuaranteedStackBytes = 0;
222 Fiber->FlsData = NULL;
223 Fiber->ActivationContextStack = ActivationContextStack;
224 Fiber->Context.ContextFlags = CONTEXT_FULL;
225
226 /* Save FPU State if requested */
227 Fiber->Context.ContextFlags = (dwFlags & FIBER_FLAG_FLOAT_SWITCH) ? CONTEXT_FLOATING_POINT : 0;
228
229 /* initialize the context for the fiber */
230 BasepInitializeContext(&Fiber->Context,
231 lpParameter,
232 lpStartAddress,
233 InitialTeb.StackBase,
234 2);
235
236 /* Return the Fiber */
237 return Fiber;
238 }
239
240 /*
241 * @implemented
242 */
243 VOID
244 WINAPI
245 DeleteFiber(LPVOID lpFiber)
246 {
247 SIZE_T Size = 0;
248 PFIBER Fiber = (PFIBER)lpFiber;
249 PTEB Teb;
250
251 /* First, exit the thread */
252 Teb = NtCurrentTeb();
253 if ((Teb->HasFiberData) && (Teb->NtTib.FiberData == Fiber)) ExitThread(1);
254
255 /* Now de-allocate the stack */
256 NtFreeVirtualMemory(NtCurrentProcess(),
257 &Fiber->DeallocationStack,
258 &Size,
259 MEM_RELEASE);
260
261 /* Get rid of FLS */
262 if (Fiber->FlsData) BaseRundownFls(Fiber->FlsData);
263
264 /* Get rid of the activation stack */
265 DPRINT1("Leaking activation stack because nobody implemented free");
266 //RtlFreeActivationContextStack(Fiber->ActivationContextStack);
267
268 /* Free the fiber data */
269 RtlFreeHeap(GetProcessHeap(), 0, lpFiber);
270 }
271
272 /*
273 * @implemented
274 */
275 BOOL
276 WINAPI
277 IsThreadAFiber(VOID)
278 {
279 return NtCurrentTeb()->HasFiberData;
280 }
281
282 /*
283 * @unimplemented
284 */
285 DWORD
286 WINAPI
287 FlsAlloc(PFLS_CALLBACK_FUNCTION lpCallback)
288 {
289 (void)lpCallback;
290
291 UNIMPLEMENTED;
292 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
293 return FLS_OUT_OF_INDEXES;
294 }
295
296
297 /*
298 * @unimplemented
299 */
300 BOOL
301 WINAPI
302 FlsFree(DWORD dwFlsIndex)
303 {
304 (void)dwFlsIndex;
305
306 UNIMPLEMENTED;
307 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
308 return FALSE;
309 }
310
311
312 /*
313 * @implemented
314 */
315 PVOID
316 WINAPI
317 FlsGetValue(DWORD dwFlsIndex)
318 {
319 PVOID *ppFlsSlots;
320 PVOID pRetVal;
321
322 if(dwFlsIndex >= 128) goto l_InvalidParam;
323
324 ppFlsSlots = NtCurrentTeb()->FlsData;
325
326 if(ppFlsSlots == NULL) goto l_InvalidParam;
327
328 SetLastError(0);
329 pRetVal = ppFlsSlots[dwFlsIndex + 2];
330
331 return pRetVal;
332
333 l_InvalidParam:
334 SetLastError(ERROR_INVALID_PARAMETER);
335 return NULL;
336 }
337
338
339 /*
340 * @implemented
341 */
342 BOOL
343 WINAPI
344 FlsSetValue(DWORD dwFlsIndex, PVOID lpFlsData)
345 {
346 PVOID *ppFlsSlots;
347 TEB *pTeb = NtCurrentTeb();
348
349 if(dwFlsIndex >= 128) goto l_InvalidParam;
350
351 ppFlsSlots = pTeb->FlsData;
352
353 if (ppFlsSlots == NULL)
354 {
355 PEB *pPeb = pTeb->ProcessEnvironmentBlock;
356
357 ppFlsSlots = RtlAllocateHeap(pPeb->ProcessHeap,
358 HEAP_ZERO_MEMORY,
359 (128 + 2) * sizeof(PVOID));
360 if(ppFlsSlots == NULL) goto l_OutOfMemory;
361
362 pTeb->FlsData = ppFlsSlots;
363
364 RtlAcquirePebLock();
365
366 /* TODO: initialization */
367
368 RtlReleasePebLock();
369 }
370
371 ppFlsSlots[dwFlsIndex + 2] = lpFlsData;
372
373 return TRUE;
374
375 l_OutOfMemory:
376 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
377 goto l_Fail;
378
379 l_InvalidParam:
380 SetLastError(ERROR_INVALID_PARAMETER);
381
382 l_Fail:
383 return FALSE;
384 }
385
386 /* EOF */