6be7ad47f78fb57694094c3379d34a1680ea8abb
[reactos.git] / reactos / dll / win32 / kernel32 / thread / 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 /*
30 * @implemented
31 */
32 BOOL
33 WINAPI
34 ConvertFiberToThread(VOID)
35 {
36 PTEB pTeb = NtCurrentTeb();
37 DPRINT1("Converting Fiber to Thread\n");
38
39 /* the current thread isn't running a fiber: failure */
40 if (!pTeb->HasFiberData)
41 {
42 SetLastError(ERROR_INVALID_PARAMETER);
43 return FALSE;
44 }
45
46 /* this thread won't run a fiber anymore */
47 pTeb->HasFiberData = FALSE;
48
49 /* free the fiber */
50 if (pTeb->Tib.FiberData != NULL)
51 {
52 RtlFreeHeap(GetProcessHeap(), 0, pTeb->Tib.FiberData);
53 }
54
55 /* success */
56 return TRUE;
57 }
58
59 /*
60 * @implemented
61 */
62 LPVOID
63 WINAPI
64 ConvertThreadToFiberEx(LPVOID lpParameter,
65 DWORD dwFlags)
66 {
67 PTEB pTeb = NtCurrentTeb();
68 PFIBER pfCurFiber;
69 DPRINT1("Converting Thread to Fiber\n");
70
71 /* the current thread is already a fiber */
72 if (pTeb->HasFiberData && pTeb->Tib.FiberData)
73 return pTeb->Tib.FiberData;
74
75 /* allocate the fiber */
76 pfCurFiber = (PFIBER)RtlAllocateHeap(GetProcessHeap(),
77 0,
78 sizeof(FIBER));
79
80 /* failure */
81 if (pfCurFiber == NULL)
82 {
83 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
84 return NULL;
85 }
86
87 /* copy some contextual data from the thread to the fiber */
88 pfCurFiber->Parameter = lpParameter;
89 pfCurFiber->ExceptionList = pTeb->Tib.ExceptionList;
90 pfCurFiber->StackBase = pTeb->Tib.StackBase;
91 pfCurFiber->StackLimit = pTeb->Tib.StackLimit;
92 pfCurFiber->DeallocationStack = pTeb->DeallocationStack;
93 pfCurFiber->FlsData = pTeb->FlsData;
94 pfCurFiber->GuaranteedStackBytes = pTeb->GuaranteedStackBytes;
95 pfCurFiber->ActivationContextStack = pTeb->ActivationContextStackPointer;
96 pfCurFiber->Context.ContextFlags = CONTEXT_FULL;
97
98 /* Save FPU State if requsted */
99 if (dwFlags & FIBER_FLAG_FLOAT_SWITCH)
100 {
101 pfCurFiber->Context.ContextFlags |= CONTEXT_FLOATING_POINT;
102 }
103
104 /* associate the fiber to the current thread */
105 pTeb->Tib.FiberData = pfCurFiber;
106 pTeb->HasFiberData = TRUE;
107
108 /* success */
109 return (LPVOID)pfCurFiber;
110 }
111
112 /*
113 * @implemented
114 */
115 LPVOID
116 WINAPI
117 ConvertThreadToFiber(LPVOID lpParameter)
118 {
119 /* Call the newer function */
120 return ConvertThreadToFiberEx(lpParameter, 0);
121 }
122
123 /*
124 * @implemented
125 */
126 LPVOID
127 WINAPI
128 CreateFiber(SIZE_T dwStackSize,
129 LPFIBER_START_ROUTINE lpStartAddress,
130 LPVOID lpParameter)
131 {
132 /* Call the Newer Function */
133 return CreateFiberEx(dwStackSize, 0, 0, lpStartAddress, lpParameter);
134 }
135
136 /*
137 * @implemented
138 */
139 LPVOID
140 WINAPI
141 CreateFiberEx(SIZE_T dwStackCommitSize,
142 SIZE_T dwStackReserveSize,
143 DWORD dwFlags,
144 LPFIBER_START_ROUTINE lpStartAddress,
145 LPVOID lpParameter)
146 {
147 PFIBER pfCurFiber;
148 NTSTATUS nErrCode;
149 INITIAL_TEB usFiberInitialTeb;
150 CONTEXT ctxFiberContext;
151 PVOID ActivationContextStack = NULL;
152 DPRINT1("Creating Fiber\n");
153
154 #ifdef SXS_SUPPORT_ENABLED
155 /* Allocate the Activation Context Stack */
156 nErrCode = RtlAllocateActivationContextStack(&ActivationContextStack);
157 #endif
158
159 /* Allocate the fiber */
160 pfCurFiber = (PFIBER)RtlAllocateHeap(GetProcessHeap(),
161 0,
162 sizeof(FIBER));
163 /* Failure */
164 if (pfCurFiber == NULL)
165 {
166 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
167 return NULL;
168 }
169
170 /* Create the stack for the fiber */
171 nErrCode = BasepCreateStack(NtCurrentProcess(),
172 dwStackCommitSize,
173 dwStackReserveSize,
174 &usFiberInitialTeb);
175 /* Failure */
176 if(!NT_SUCCESS(nErrCode))
177 {
178 /* Free the fiber */
179 RtlFreeHeap(GetProcessHeap(), 0, pfCurFiber);
180
181 /* Failure */
182 SetLastErrorByStatus(nErrCode);
183 return NULL;
184 }
185
186 /* Clear the context */
187 RtlZeroMemory(&pfCurFiber->Context, sizeof(CONTEXT));
188
189 /* copy the data into the fiber */
190 pfCurFiber->StackBase = usFiberInitialTeb.StackBase;
191 pfCurFiber->StackLimit = usFiberInitialTeb.StackLimit;
192 pfCurFiber->DeallocationStack = usFiberInitialTeb.AllocatedStackBase;
193 pfCurFiber->Parameter = lpParameter;
194 pfCurFiber->ExceptionList = (struct _EXCEPTION_REGISTRATION_RECORD *)-1;
195 pfCurFiber->GuaranteedStackBytes = 0;
196 pfCurFiber->FlsData = NULL;
197 pfCurFiber->ActivationContextStack = ActivationContextStack;
198 pfCurFiber->Context.ContextFlags = CONTEXT_FULL;
199
200 /* Save FPU State if requsted */
201 if (dwFlags & FIBER_FLAG_FLOAT_SWITCH)
202 {
203 pfCurFiber->Context.ContextFlags |= CONTEXT_FLOATING_POINT;
204 }
205
206 /* initialize the context for the fiber */
207 BasepInitializeContext(&ctxFiberContext,
208 lpParameter,
209 lpStartAddress,
210 usFiberInitialTeb.StackBase,
211 2);
212
213 /* Return the Fiber */
214 return pfCurFiber;
215 }
216
217 /*
218 * @implemented
219 */
220 VOID
221 WINAPI
222 DeleteFiber(LPVOID lpFiber)
223 {
224 SIZE_T nSize = 0;
225 PVOID pStackAllocBase = ((PFIBER)lpFiber)->DeallocationStack;
226
227 /* free the fiber */
228 RtlFreeHeap(GetProcessHeap(), 0, lpFiber);
229
230 /* the fiber is deleting itself: let the system deallocate the stack */
231 if (NtCurrentTeb()->Tib.FiberData == lpFiber)
232 ExitThread(1);
233
234 /* deallocate the stack */
235 NtFreeVirtualMemory(NtCurrentProcess(),
236 &pStackAllocBase,
237 &nSize,
238 MEM_RELEASE);
239 }
240
241 /*
242 * @implemented
243 */
244 BOOL
245 WINAPI
246 IsThreadAFiber(VOID)
247 {
248 return NtCurrentTeb()->HasFiberData;
249 }
250
251
252 __declspec(noreturn)
253 VOID
254 WINAPI
255 BaseFiberStartup(VOID)
256 {
257 #ifdef _M_IX86
258 PFIBER Fiber = GetFiberData();
259
260 /* Call the Thread Startup Routine */
261 DPRINT1("Starting Fiber\n");
262 BaseThreadStartup((LPTHREAD_START_ROUTINE)Fiber->Context.Eax,
263 (LPVOID)Fiber->Context.Ebx);
264 #else
265 #warning Unknown architecture
266 UNIMPLEMENTED;
267 DbgBreakPoint();
268 #endif
269 }
270
271 /* EOF */