Branch setupapi
[reactos.git] / reactos / lib / kernel32 / thread / fiber.c
1 /* $Id$
2 *
3 * FILE: lib/kernel32/thread/fiber.c
4 *
5 * ReactOS Kernel32.dll
6 *
7 */
8 #include <k32.h>
9
10 #define NDEBUG
11 #include "../include/debug.h"
12
13 struct _FIBER /* Field offsets: */
14 { /* 32 bit 64 bit */
15 /* this must be the first field */
16 LPVOID Parameter; /* 0x00 0x00 */
17
18 struct _EXCEPTION_REGISTRATION_RECORD * ExceptionList; /* 0x04 0x08 */
19 LPVOID StackBase; /* 0x08 0x10 */
20 LPVOID StackLimit; /* 0x0C 0x18 */
21 LPVOID DeallocationStack; /* 0x10 0x20 */
22 ULONG_PTR Flags; /* 0x14 0x28 */
23 #if defined(_M_IX86)
24 /* control flow registers */
25 DWORD Eip; /* 0x18 ---- */
26 DWORD Esp; /* 0x1C ---- */
27 DWORD Ebp; /* 0x20 ---- */
28
29 /* general-purpose registers that must be preserved across calls */
30 DWORD Ebx; /* 0x24 ---- */
31 DWORD Esi; /* 0x28 ---- */
32 DWORD Edi; /* 0x2C ---- */
33
34 /* floating point save area (optional) */
35 FLOATING_SAVE_AREA FloatSave; /* 0x30 ---- */
36 #else
37 #error Unspecified or unsupported architecture.
38 #endif
39 };
40
41 typedef struct _FIBER FIBER, * PFIBER;
42
43 __declspec(noreturn) void WINAPI FiberStartup(PVOID lpStartAddress);
44
45 /*
46 * @implemented
47 */
48 BOOL WINAPI ConvertFiberToThread(void)
49 {
50 PTEB pTeb = NtCurrentTeb();
51
52 /* the current thread isn't running a fiber: failure */
53 if(!pTeb->IsFiber)
54 {
55 SetLastError(ERROR_INVALID_PARAMETER);
56 return FALSE;
57 }
58
59 /* this thread won't run a fiber anymore */
60 pTeb->IsFiber = FALSE;
61
62 /* free the fiber */
63 if(pTeb->Tib.FiberData != NULL)
64 RtlFreeHeap(pTeb->Peb->ProcessHeap, 0, pTeb->Tib.FiberData);
65
66 /* success */
67 return TRUE;
68 }
69
70
71 /*
72 * @implemented
73 */
74 LPVOID WINAPI ConvertThreadToFiberEx(LPVOID lpParameter, DWORD dwFlags)
75 {
76 PTEB pTeb = NtCurrentTeb();
77 PFIBER pfCurFiber;
78
79 /* the current thread is already a fiber */
80 if(pTeb->IsFiber && pTeb->Tib.FiberData) return pTeb->Tib.FiberData;
81
82 /* allocate the fiber */
83 pfCurFiber = (PFIBER)RtlAllocateHeap(pTeb->Peb->ProcessHeap, 0, sizeof(FIBER));
84
85 /* failure */
86 if(pfCurFiber == NULL)
87 {
88 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
89 return NULL;
90 }
91
92 pfCurFiber->Parameter = lpParameter;
93 pfCurFiber->Flags = dwFlags;
94
95 /* copy some contextual data from the thread to the fiber */
96 pfCurFiber->ExceptionList = pTeb->Tib.ExceptionList;
97 pfCurFiber->StackBase = pTeb->Tib.StackBase;
98 pfCurFiber->StackLimit = pTeb->Tib.StackLimit;
99 pfCurFiber->DeallocationStack = pTeb->DeallocationStack;
100
101 /* associate the fiber to the current thread */
102 pTeb->Tib.FiberData = pfCurFiber;
103 pTeb->IsFiber = TRUE;
104
105 /* success */
106 return (LPVOID)pfCurFiber;
107 }
108
109
110 /*
111 * @implemented
112 */
113 LPVOID WINAPI ConvertThreadToFiber(LPVOID lpParameter)
114 {
115 return ConvertThreadToFiberEx(lpParameter, 0);
116 }
117
118
119 /*
120 * @implemented
121 */
122 LPVOID WINAPI CreateFiber
123 (
124 SIZE_T dwStackSize,
125 LPFIBER_START_ROUTINE lpStartAddress,
126 LPVOID lpParameter
127 )
128 {
129 return CreateFiberEx(dwStackSize, 0, 0, lpStartAddress, lpParameter);
130 }
131
132
133 /*
134 * @implemented
135 */
136 LPVOID WINAPI CreateFiberEx
137 (
138 SIZE_T dwStackCommitSize,
139 SIZE_T dwStackReserveSize,
140 DWORD dwFlags,
141 LPFIBER_START_ROUTINE lpStartAddress,
142 LPVOID lpParameter
143 )
144 {
145 PFIBER pfCurFiber;
146 NTSTATUS nErrCode;
147 PSIZE_T pnStackReserve = NULL;
148 PSIZE_T pnStackCommit = NULL;
149 INITIAL_TEB usFiberInitialTeb;
150 CONTEXT ctxFiberContext;
151 PTEB pTeb = NtCurrentTeb();
152
153 /* allocate the fiber */
154 pfCurFiber = (PFIBER)RtlAllocateHeap(pTeb->Peb->ProcessHeap, 0, sizeof(FIBER));
155
156 /* failure */
157 if(pfCurFiber == NULL)
158 {
159 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
160 return NULL;
161 }
162
163 /* if the stack reserve or commit size weren't specified, use defaults */
164 if(dwStackReserveSize > 0) pnStackReserve = &dwStackReserveSize;
165 if(dwStackCommitSize > 0) pnStackCommit = &dwStackCommitSize;
166
167 /* create the stack for the fiber */
168 nErrCode = RtlRosCreateStack
169 (
170 NtCurrentProcess(),
171 &usFiberInitialTeb,
172 0,
173 pnStackReserve,
174 pnStackCommit
175 );
176
177 /* failure */
178 if(!NT_SUCCESS(nErrCode)) goto l_CleanupFiber;
179
180 /* initialize the context for the fiber */
181 nErrCode = RtlRosInitializeContext
182 (
183 NtCurrentProcess(),
184 &ctxFiberContext,
185 FiberStartup,
186 &usFiberInitialTeb,
187 1,
188 (ULONG_PTR *)&lpStartAddress
189 );
190
191 /* failure */
192 if(!NT_SUCCESS(nErrCode)) goto l_CleanupStack;
193
194 /* copy the data into the fiber */
195
196 /* fixed-size stack */
197 if(usFiberInitialTeb.StackBase && usFiberInitialTeb.StackLimit)
198 {
199 pfCurFiber->StackBase = usFiberInitialTeb.StackBase;
200 pfCurFiber->StackLimit = usFiberInitialTeb.StackLimit;
201 pfCurFiber->DeallocationStack = usFiberInitialTeb.StackLimit;
202 }
203 /* expandable stack */
204 else if
205 (
206 usFiberInitialTeb.StackCommit &&
207 usFiberInitialTeb.StackCommitMax &&
208 usFiberInitialTeb.StackReserved
209 )
210 {
211 pfCurFiber->StackBase = usFiberInitialTeb.StackCommit;
212 pfCurFiber->StackLimit = usFiberInitialTeb.StackCommitMax;
213 pfCurFiber->DeallocationStack = usFiberInitialTeb.StackReserved;
214 }
215 /* bad initial stack */
216 else goto l_CleanupStack;
217
218 pfCurFiber->Parameter = lpParameter;
219 pfCurFiber->Flags = dwFlags;
220 pfCurFiber->ExceptionList = (struct _EXCEPTION_REGISTRATION_RECORD *)-1;
221
222 #if defined(_M_IX86)
223
224 pfCurFiber->Eip = ctxFiberContext.Eip;
225 pfCurFiber->Esp = ctxFiberContext.Esp;
226 pfCurFiber->Ebp = ctxFiberContext.Ebp;
227 pfCurFiber->Ebx = ctxFiberContext.Ebx;
228 pfCurFiber->Esi = ctxFiberContext.Esi;
229 pfCurFiber->Edi = ctxFiberContext.Edi;
230
231 if(dwFlags & FIBER_FLAG_FLOAT_SWITCH)
232 pfCurFiber->FloatSave = ctxFiberContext.FloatSave;
233
234 #else
235 #error Unspecified or unsupported architecture.
236 #endif
237
238 return pfCurFiber;
239
240 l_CleanupStack:
241 /* free the stack */
242 RtlRosDeleteStack(NtCurrentProcess(), &usFiberInitialTeb);
243
244 l_CleanupFiber:
245 /* free the fiber */
246 RtlFreeHeap(pTeb->Peb->ProcessHeap, 0, pfCurFiber);
247
248 /* failure */
249 ASSERT(!NT_SUCCESS(nErrCode));
250 SetLastErrorByStatus(nErrCode);
251 return NULL;
252 }
253
254
255 /*
256 * @implemented
257 */
258 void WINAPI DeleteFiber(LPVOID lpFiber)
259 {
260 SIZE_T nSize = 0;
261 PVOID pStackAllocBase = ((PFIBER)lpFiber)->DeallocationStack;
262 PTEB pTeb = NtCurrentTeb();
263
264 /* free the fiber */
265 RtlFreeHeap(pTeb->Peb->ProcessHeap, 0, lpFiber);
266
267 /* the fiber is deleting itself: let the system deallocate the stack */
268 if(pTeb->Tib.FiberData == lpFiber) ExitThread(1);
269
270 /* deallocate the stack */
271 NtFreeVirtualMemory
272 (
273 NtCurrentProcess(),
274 &pStackAllocBase,
275 &nSize,
276 MEM_RELEASE
277 );
278 }
279
280
281 __declspec(noreturn) extern void WINAPI ThreadStartup
282 (
283 LPTHREAD_START_ROUTINE lpStartAddress,
284 LPVOID lpParameter
285 );
286
287
288 __declspec(noreturn) void WINAPI FiberStartup(PVOID lpStartAddress)
289 {
290 /* FIXME? this should be pretty accurate */
291 ThreadStartup(lpStartAddress, GetFiberData());
292 }
293
294 /* EOF */