Sync with trunk r65656.
[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: 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 PVOID Parameter; /* 0x00 0x00 */
19 PEXCEPTION_REGISTRATION_RECORD ExceptionList; /* 0x04 0x08 */
20 PVOID StackBase; /* 0x08 0x10 */
21 PVOID StackLimit; /* 0x0C 0x18 */
22 PVOID DeallocationStack; /* 0x10 0x20 */
23 CONTEXT Context; /* 0x14 0x28 */
24 ULONG GuaranteedStackBytes; /* 0x2E0 */
25 PVOID FlsData; /* 0x2E4 */
26 PACTIVATION_CONTEXT_STACK 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 PACTIVATION_CONTEXT_STACK 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 /* Free the activation context stack */
199 RtlFreeActivationContextStack(ActivationContextStack);
200
201 /* Fail */
202 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
203 return NULL;
204 }
205
206 /* Create the stack for the fiber */
207 Status = BaseCreateStack(NtCurrentProcess(),
208 dwStackCommitSize,
209 dwStackReserveSize,
210 &InitialTeb);
211 if (!NT_SUCCESS(Status))
212 {
213 /* Free the fiber */
214 RtlFreeHeap(GetProcessHeap(), 0, Fiber);
215
216 /* Free the activation context stack */
217 RtlFreeActivationContextStack(ActivationContextStack);
218
219 /* Failure */
220 BaseSetLastNTError(Status);
221 return NULL;
222 }
223
224 /* Clear the context */
225 RtlZeroMemory(&Fiber->Context, sizeof(CONTEXT));
226
227 /* Copy the data into the fiber */
228 Fiber->StackBase = InitialTeb.StackBase;
229 Fiber->StackLimit = InitialTeb.StackLimit;
230 Fiber->DeallocationStack = InitialTeb.AllocatedStackBase;
231 Fiber->Parameter = lpParameter;
232 Fiber->ExceptionList = EXCEPTION_CHAIN_END;
233 Fiber->GuaranteedStackBytes = 0;
234 Fiber->FlsData = NULL;
235 Fiber->ActivationContextStack = ActivationContextStack;
236 Fiber->Context.ContextFlags = CONTEXT_FULL;
237
238 /* Save FPU State if requested */
239 Fiber->Context.ContextFlags = (dwFlags & FIBER_FLAG_FLOAT_SWITCH) ? CONTEXT_FLOATING_POINT : 0;
240
241 /* initialize the context for the fiber */
242 BaseInitializeContext(&Fiber->Context,
243 lpParameter,
244 lpStartAddress,
245 InitialTeb.StackBase,
246 2);
247
248 /* Return the Fiber */
249 return Fiber;
250 }
251
252 /*
253 * @implemented
254 */
255 VOID
256 WINAPI
257 DeleteFiber(LPVOID lpFiber)
258 {
259 SIZE_T Size = 0;
260 PFIBER Fiber = (PFIBER)lpFiber;
261 PTEB Teb;
262
263 /* First, exit the thread */
264 Teb = NtCurrentTeb();
265 if ((Teb->HasFiberData) && (Teb->NtTib.FiberData == Fiber)) ExitThread(1);
266
267 /* Now de-allocate the stack */
268 NtFreeVirtualMemory(NtCurrentProcess(),
269 &Fiber->DeallocationStack,
270 &Size,
271 MEM_RELEASE);
272
273 /* Get rid of FLS */
274 if (Fiber->FlsData) BaseRundownFls(Fiber->FlsData);
275
276 /* Get rid of the activation context stack */
277 RtlFreeActivationContextStack(Fiber->ActivationContextStack);
278
279 /* Free the fiber data */
280 RtlFreeHeap(GetProcessHeap(), 0, lpFiber);
281 }
282
283 /*
284 * @implemented
285 */
286 BOOL
287 WINAPI
288 IsThreadAFiber(VOID)
289 {
290 return NtCurrentTeb()->HasFiberData;
291 }
292
293 /*
294 * @unimplemented
295 */
296 DWORD
297 WINAPI
298 FlsAlloc(PFLS_CALLBACK_FUNCTION lpCallback)
299 {
300 (void)lpCallback;
301
302 UNIMPLEMENTED;
303 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
304 return FLS_OUT_OF_INDEXES;
305 }
306
307
308 /*
309 * @unimplemented
310 */
311 BOOL
312 WINAPI
313 FlsFree(DWORD dwFlsIndex)
314 {
315 (void)dwFlsIndex;
316
317 UNIMPLEMENTED;
318 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
319 return FALSE;
320 }
321
322
323 /*
324 * @implemented
325 */
326 PVOID
327 WINAPI
328 FlsGetValue(DWORD dwFlsIndex)
329 {
330 PVOID *ppFlsSlots;
331 PVOID pRetVal;
332
333 if(dwFlsIndex >= 128) goto l_InvalidParam;
334
335 ppFlsSlots = NtCurrentTeb()->FlsData;
336
337 if(ppFlsSlots == NULL) goto l_InvalidParam;
338
339 SetLastError(0);
340 pRetVal = ppFlsSlots[dwFlsIndex + 2];
341
342 return pRetVal;
343
344 l_InvalidParam:
345 SetLastError(ERROR_INVALID_PARAMETER);
346 return NULL;
347 }
348
349
350 /*
351 * @implemented
352 */
353 BOOL
354 WINAPI
355 FlsSetValue(DWORD dwFlsIndex, PVOID lpFlsData)
356 {
357 PVOID *ppFlsSlots;
358 TEB *pTeb = NtCurrentTeb();
359
360 if(dwFlsIndex >= 128) goto l_InvalidParam;
361
362 ppFlsSlots = pTeb->FlsData;
363
364 if (ppFlsSlots == NULL)
365 {
366 PEB *pPeb = pTeb->ProcessEnvironmentBlock;
367
368 ppFlsSlots = RtlAllocateHeap(pPeb->ProcessHeap,
369 HEAP_ZERO_MEMORY,
370 (128 + 2) * sizeof(PVOID));
371 if(ppFlsSlots == NULL) goto l_OutOfMemory;
372
373 pTeb->FlsData = ppFlsSlots;
374
375 RtlAcquirePebLock();
376
377 /* TODO: initialization */
378
379 RtlReleasePebLock();
380 }
381
382 ppFlsSlots[dwFlsIndex + 2] = lpFlsData;
383
384 return TRUE;
385
386 l_OutOfMemory:
387 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
388 goto l_Fail;
389
390 l_InvalidParam:
391 SetLastError(ERROR_INVALID_PARAMETER);
392
393 l_Fail:
394 return FALSE;
395 }
396
397 /* EOF */