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