07d65b35a120b2e50b6107bbda51ccf02a6570ad
[reactos.git] / reactos / lib / kernel32 / thread / fiber.c
1 /* $Id: fiber.c,v 1.7 2003/07/22 20:10:04 hyperion Exp $
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 <kernel32/kernel32.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.Fib.FiberData != NULL)
64 RtlFreeHeap(pTeb->Peb->ProcessHeap, 0, pTeb->Tib.Fib.FiberData);
65
66 /* success */
67 }
68
69
70 /*
71 * @implemented
72 */
73 LPVOID WINAPI ConvertThreadToFiber(LPVOID lpParameter)
74 {
75 return ConvertThreadToFiberEx(lpParameter, 0);
76 }
77
78
79 /*
80 * @implemented
81 */
82 LPVOID WINAPI ConvertThreadToFiberEx(LPVOID lpParameter, DWORD dwFlags)
83 {
84 PTEB pTeb = NtCurrentTeb();
85 PFIBER pfCurFiber;
86
87 /* the current thread is already a fiber */
88 if(pTeb->IsFiber && pTeb->Tib.Fib.FiberData) return pTeb->Tib.Fib.FiberData;
89
90 /* allocate the fiber */
91 pfCurFiber = (PFIBER)RtlAllocateHeap(pTeb->Peb->ProcessHeap, 0, sizeof(FIBER));
92
93 /* failure */
94 if(pfCurFiber == NULL)
95 {
96 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
97 return NULL;
98 }
99
100 pfCurFiber->Parameter = lpParameter;
101 pfCurFiber->Flags = dwFlags;
102
103 /* copy some contextual data from the thread to the fiber */
104 pfCurFiber->ExceptionList = pTeb->Tib.ExceptionList;
105 pfCurFiber->StackBase = pTeb->Tib.StackBase;
106 pfCurFiber->StackLimit = pTeb->Tib.StackLimit;
107 pfCurFiber->DeallocationStack = pTeb->DeallocationStack;
108
109 /* associate the fiber to the current thread */
110 pTeb->Tib.Fib.FiberData = pfCurFiber;
111 pTeb->IsFiber = TRUE;
112
113 /* success */
114 return (LPVOID)pfCurFiber;
115 }
116
117
118 /*
119 * @implemented
120 */
121 LPVOID WINAPI CreateFiber
122 (
123 SIZE_T dwStackSize,
124 LPFIBER_START_ROUTINE lpStartAddress,
125 LPVOID lpParameter
126 )
127 {
128 return CreateFiberEx(dwStackSize, 0, 0, lpStartAddress, lpParameter);
129 }
130
131
132 /*
133 * @implemented
134 */
135 LPVOID WINAPI CreateFiberEx
136 (
137 SIZE_T dwStackCommitSize,
138 SIZE_T dwStackReserveSize,
139 DWORD dwFlags,
140 LPFIBER_START_ROUTINE lpStartAddress,
141 LPVOID lpParameter
142 )
143 {
144 PFIBER pfCurFiber;
145 NTSTATUS nErrCode;
146 PSIZE_T pnStackReserve = NULL;
147 PSIZE_T pnStackCommit = NULL;
148 USER_STACK usFiberStack;
149 CONTEXT ctxFiberContext;
150 PCHAR pStackBase;
151 PCHAR pStackLimit;
152 PTEB pTeb = NtCurrentTeb();
153
154 /* allocate the fiber */
155 pfCurFiber = (PFIBER)RtlAllocateHeap(pTeb->Peb->ProcessHeap, 0, sizeof(FIBER));
156
157 /* failure */
158 if(pfCurFiber == NULL)
159 {
160 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
161 return NULL;
162 }
163
164 /* if the stack reserve or commit size weren't specified, use defaults */
165 if(dwStackReserveSize > 0) pnStackReserve = &dwStackReserveSize;
166 if(dwStackCommitSize > 0) pnStackCommit = &dwStackCommitSize;
167
168 /* create the stack for the fiber */
169 nErrCode = RtlRosCreateStack
170 (
171 NtCurrentProcess(),
172 &usFiberStack,
173 0,
174 pnStackReserve,
175 pnStackCommit
176 );
177
178 /* failure */
179 if(!NT_SUCCESS(nErrCode)) goto l_CleanupFiber;
180
181 /* initialize the context for the fiber */
182 nErrCode = RtlRosInitializeContext
183 (
184 NtCurrentProcess(),
185 &ctxFiberContext,
186 FiberStartup,
187 &usFiberStack,
188 1,
189 (ULONG_PTR *)&lpStartAddress
190 );
191
192 /* failure */
193 if(!NT_SUCCESS(nErrCode)) goto l_CleanupStack;
194
195 /* copy the data into the fiber */
196
197 /* fixed-size stack */
198 if(usFiberStack.FixedStackBase && usFiberStack.FixedStackLimit)
199 {
200 pfCurFiber->StackBase = usFiberStack.FixedStackBase;
201 pfCurFiber->StackLimit = usFiberStack.FixedStackLimit;
202 pfCurFiber->DeallocationStack = usFiberStack.FixedStackLimit;
203 }
204 /* expandable stack */
205 else if
206 (
207 usFiberStack.ExpandableStackBase &&
208 usFiberStack.ExpandableStackLimit &&
209 usFiberStack.ExpandableStackBottom
210 )
211 {
212 pfCurFiber->StackBase = usFiberStack.ExpandableStackBase;
213 pfCurFiber->StackLimit = usFiberStack.ExpandableStackLimit;
214 pfCurFiber->DeallocationStack = usFiberStack.ExpandableStackBottom;
215 }
216 /* bad initial stack */
217 else goto l_CleanupStack;
218
219 pfCurFiber->Parameter = lpParameter;
220 pfCurFiber->Flags = dwFlags;
221 pfCurFiber->ExceptionList = (struct _EXCEPTION_REGISTRATION_RECORD *)-1;
222
223 #if defined(_M_IX86)
224
225 pfCurFiber->Eip = ctxFiberContext.Eip;
226 pfCurFiber->Esp = ctxFiberContext.Esp;
227 pfCurFiber->Ebp = ctxFiberContext.Ebp;
228 pfCurFiber->Ebx = ctxFiberContext.Ebx;
229 pfCurFiber->Esi = ctxFiberContext.Esi;
230 pfCurFiber->Edi = ctxFiberContext.Edi;
231
232 if(dwFlags & FIBER_FLAG_FLOAT_SWITCH)
233 pfCurFiber->FloatSave = ctxFiberContext.FloatSave;
234
235 #else
236 #error Unspecified or unsupported architecture.
237 #endif
238
239 return pfCurFiber;
240
241 l_CleanupStack:
242 /* free the stack */
243 RtlRosDeleteStack(NtCurrentProcess(), &usFiberStack);
244
245 l_CleanupFiber:
246 /* free the fiber */
247 RtlFreeHeap(pTeb->Peb->ProcessHeap, 0, pfCurFiber);
248
249 /* failure */
250 assert(!NT_SUCCESS(nErrCode));
251 SetLastErrorByStatus(nErrCode);
252 return NULL;
253 }
254
255
256 /*
257 * @implemented
258 */
259 void WINAPI DeleteFiber(LPVOID lpFiber)
260 {
261 SIZE_T nSize = 0;
262 PVOID pStackAllocBase = ((PFIBER)lpFiber)->DeallocationStack;
263 PTEB pTeb = NtCurrentTeb();
264
265 /* free the fiber */
266 RtlFreeHeap(pTeb->Peb->ProcessHeap, 0, lpFiber);
267
268 /* the fiber is deleting itself: let the system deallocate the stack */
269 if(pTeb->Tib.Fib.FiberData == lpFiber) ExitThread(1);
270
271 /* deallocate the stack */
272 NtFreeVirtualMemory
273 (
274 NtCurrentProcess(),
275 &pStackAllocBase,
276 &nSize,
277 MEM_RELEASE
278 );
279 }
280
281
282 __declspec(noreturn) extern void WINAPI ThreadStartup
283 (
284 LPTHREAD_START_ROUTINE lpStartAddress,
285 LPVOID lpParameter
286 );
287
288
289 __declspec(noreturn) void WINAPI FiberStartup(PVOID lpStartAddress)
290 {
291 /* FIXME? this should be pretty accurate */
292 ThreadStartup(lpStartAddress, NtCurrentTeb()->Tib.Fib.FiberData);
293 }
294
295 /* EOF */