[KERNEL32]
[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 Teb;
83 PFIBER Fiber;
84 DPRINT1("Converting Thread to Fiber\n");
85
86 /* Check for invalid flags */
87 if (dwFlags &~ FIBER_FLAG_FLOAT_SWITCH)
88 {
89 /* Fail */
90 SetLastError(ERROR_INVALID_PARAMETER);
91 return NULL;
92 }
93
94 /* Are we already a fiber? */
95 Teb = NtCurrentTeb();
96 if (Teb->HasFiberData)
97 {
98 /* Fail */
99 SetLastError(ERROR_ALREADY_FIBER);
100 return NULL;
101 }
102
103 /* Allocate the fiber */
104 Fiber = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(FIBER));
105 if (!Fiber)
106 {
107 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
108 return NULL;
109 }
110
111 /* Copy some contextual data from the thread to the fiber */
112 Fiber->Parameter = lpParameter;
113 Fiber->ExceptionList = Teb->NtTib.ExceptionList;
114 Fiber->StackBase = Teb->NtTib.StackBase;
115 Fiber->StackLimit = Teb->NtTib.StackLimit;
116 Fiber->DeallocationStack = Teb->DeallocationStack;
117 Fiber->FlsData = Teb->FlsData;
118 Fiber->GuaranteedStackBytes = Teb->GuaranteedStackBytes;
119 Fiber->ActivationContextStack = Teb->ActivationContextStackPointer;
120 Fiber->Context.ContextFlags = CONTEXT_FULL;
121
122 /* Save FPU State if requested */
123 if (dwFlags & FIBER_FLAG_FLOAT_SWITCH)
124 {
125 Fiber->Context.ContextFlags |= CONTEXT_FLOATING_POINT;
126 }
127
128 /* Associate the fiber to the current thread */
129 Teb->NtTib.FiberData = Fiber;
130 Teb->HasFiberData = TRUE;
131
132 /* Return opaque fiber data */
133 return (LPVOID)Fiber;
134 }
135
136 /*
137 * @implemented
138 */
139 LPVOID
140 WINAPI
141 ConvertThreadToFiber(LPVOID lpParameter)
142 {
143 /* Call the newer function */
144 return ConvertThreadToFiberEx(lpParameter, 0);
145 }
146
147 /*
148 * @implemented
149 */
150 LPVOID
151 WINAPI
152 CreateFiber(SIZE_T dwStackSize,
153 LPFIBER_START_ROUTINE lpStartAddress,
154 LPVOID lpParameter)
155 {
156 /* Call the Newer Function */
157 return CreateFiberEx(dwStackSize, 0, 0, lpStartAddress, lpParameter);
158 }
159
160 /*
161 * @implemented
162 */
163 LPVOID
164 WINAPI
165 CreateFiberEx(SIZE_T dwStackCommitSize,
166 SIZE_T dwStackReserveSize,
167 DWORD dwFlags,
168 LPFIBER_START_ROUTINE lpStartAddress,
169 LPVOID lpParameter)
170 {
171 PFIBER Fiber;
172 NTSTATUS Status;
173 INITIAL_TEB InitialTeb;
174 PVOID ActivationContextStack = NULL;
175 DPRINT("Creating Fiber\n");
176
177 /* Check for invalid flags */
178 if (dwFlags &~ FIBER_FLAG_FLOAT_SWITCH)
179 {
180 /* Fail */
181 SetLastError(ERROR_INVALID_PARAMETER);
182 return NULL;
183 }
184
185 /* Allocate the Activation Context Stack */
186 Status = RtlAllocateActivationContextStack(&ActivationContextStack);
187 if (!NT_SUCCESS(Status))
188 {
189 /* Fail */
190 BaseSetLastNTError(Status);
191 return NULL;
192 }
193
194 /* Allocate the fiber */
195 Fiber = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(FIBER));
196 if (!Fiber)
197 {
198 /* Fail */
199 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
200 return NULL;
201 }
202
203 /* Create the stack for the fiber */
204 Status = BasepCreateStack(NtCurrentProcess(),
205 dwStackCommitSize,
206 dwStackReserveSize,
207 &InitialTeb);
208 if (!NT_SUCCESS(Status))
209 {
210 /* Free the fiber */
211 RtlFreeHeap(GetProcessHeap(), 0, Fiber);
212
213 /* Free the activation context */
214 DPRINT1("Leaking activation stack because nobody implemented free");
215 //RtlFreeActivationContextStack(&ActivationContextStack);
216
217 /* Failure */
218 BaseSetLastNTError(Status);
219 return NULL;
220 }
221
222 /* Clear the context */
223 RtlZeroMemory(&Fiber->Context, sizeof(CONTEXT));
224
225 /* Copy the data into the fiber */
226 Fiber->StackBase = InitialTeb.StackBase;
227 Fiber->StackLimit = InitialTeb.StackLimit;
228 Fiber->DeallocationStack = InitialTeb.AllocatedStackBase;
229 Fiber->Parameter = lpParameter;
230 Fiber->ExceptionList = EXCEPTION_CHAIN_END;
231 Fiber->GuaranteedStackBytes = 0;
232 Fiber->FlsData = NULL;
233 Fiber->ActivationContextStack = ActivationContextStack;
234 Fiber->Context.ContextFlags = CONTEXT_FULL;
235
236 /* Save FPU State if requested */
237 Fiber->Context.ContextFlags = (dwFlags & FIBER_FLAG_FLOAT_SWITCH) ? CONTEXT_FLOATING_POINT : 0;
238
239 /* initialize the context for the fiber */
240 BasepInitializeContext(&Fiber->Context,
241 lpParameter,
242 lpStartAddress,
243 InitialTeb.StackBase,
244 2);
245
246 /* Return the Fiber */
247 return Fiber;
248 }
249
250 /*
251 * @implemented
252 */
253 VOID
254 WINAPI
255 DeleteFiber(LPVOID lpFiber)
256 {
257 SIZE_T Size = 0;
258 PFIBER Fiber = (PFIBER)lpFiber;
259 PTEB Teb;
260
261 /* First, exit the thread */
262 Teb = NtCurrentTeb();
263 if ((Teb->HasFiberData) && (Teb->NtTib.FiberData == Fiber)) ExitThread(1);
264
265 /* Now de-allocate the stack */
266 NtFreeVirtualMemory(NtCurrentProcess(),
267 &Fiber->DeallocationStack,
268 &Size,
269 MEM_RELEASE);
270
271 /* Get rid of FLS */
272 if (Fiber->FlsData) BaseRundownFls(Fiber->FlsData);
273
274 /* Get rid of the activation stack */
275 DPRINT1("Leaking activation stack because nobody implemented free");
276 //RtlFreeActivationContextStack(Fiber->ActivationContextStack);
277
278 /* Free the fiber data */
279 RtlFreeHeap(GetProcessHeap(), 0, lpFiber);
280 }
281
282 /*
283 * @implemented
284 */
285 BOOL
286 WINAPI
287 IsThreadAFiber(VOID)
288 {
289 return NtCurrentTeb()->HasFiberData;
290 }
291
292 /*
293 * @unimplemented
294 */
295 DWORD
296 WINAPI
297 FlsAlloc(PFLS_CALLBACK_FUNCTION lpCallback)
298 {
299 (void)lpCallback;
300
301 UNIMPLEMENTED;
302 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
303 return FLS_OUT_OF_INDEXES;
304 }
305
306
307 /*
308 * @unimplemented
309 */
310 BOOL
311 WINAPI
312 FlsFree(DWORD dwFlsIndex)
313 {
314 (void)dwFlsIndex;
315
316 UNIMPLEMENTED;
317 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
318 return FALSE;
319 }
320
321
322 /*
323 * @implemented
324 */
325 PVOID
326 WINAPI
327 FlsGetValue(DWORD dwFlsIndex)
328 {
329 PVOID *ppFlsSlots;
330 PVOID pRetVal;
331
332 if(dwFlsIndex >= 128) goto l_InvalidParam;
333
334 ppFlsSlots = NtCurrentTeb()->FlsData;
335
336 if(ppFlsSlots == NULL) goto l_InvalidParam;
337
338 SetLastError(0);
339 pRetVal = ppFlsSlots[dwFlsIndex + 2];
340
341 return pRetVal;
342
343 l_InvalidParam:
344 SetLastError(ERROR_INVALID_PARAMETER);
345 return NULL;
346 }
347
348
349 /*
350 * @implemented
351 */
352 BOOL
353 WINAPI
354 FlsSetValue(DWORD dwFlsIndex, PVOID lpFlsData)
355 {
356 PVOID *ppFlsSlots;
357 TEB *pTeb = NtCurrentTeb();
358
359 if(dwFlsIndex >= 128) goto l_InvalidParam;
360
361 ppFlsSlots = pTeb->FlsData;
362
363 if (ppFlsSlots == NULL)
364 {
365 PEB *pPeb = pTeb->ProcessEnvironmentBlock;
366
367 ppFlsSlots = RtlAllocateHeap(pPeb->ProcessHeap,
368 HEAP_ZERO_MEMORY,
369 (128 + 2) * sizeof(PVOID));
370 if(ppFlsSlots == NULL) goto l_OutOfMemory;
371
372 pTeb->FlsData = ppFlsSlots;
373
374 RtlAcquirePebLock();
375
376 /* TODO: initialization */
377
378 RtlReleasePebLock();
379 }
380
381 ppFlsSlots[dwFlsIndex + 2] = lpFlsData;
382
383 return TRUE;
384
385 l_OutOfMemory:
386 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
387 goto l_Fail;
388
389 l_InvalidParam:
390 SetLastError(ERROR_INVALID_PARAMETER);
391
392 l_Fail:
393 return FALSE;
394 }
395
396 /* EOF */