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