Move kernekl32-internal declarations from global include directory to local one.
[reactos.git] / reactos / lib / kernel32 / thread / fiber.c
1 /* $Id: fiber.c,v 1.9 2004/01/23 21:16:04 ekohl 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 "../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.Fib.FiberData != NULL)
64 RtlFreeHeap(pTeb->Peb->ProcessHeap, 0, pTeb->Tib.Fib.FiberData);
65
66 /* success */
67 return TRUE;
68 }
69
70
71 /*
72 * @implemented
73 */
74 LPVOID WINAPI ConvertThreadToFiber(LPVOID lpParameter)
75 {
76 return ConvertThreadToFiberEx(lpParameter, 0);
77 }
78
79
80 /*
81 * @implemented
82 */
83 LPVOID WINAPI ConvertThreadToFiberEx(LPVOID lpParameter, DWORD dwFlags)
84 {
85 PTEB pTeb = NtCurrentTeb();
86 PFIBER pfCurFiber;
87
88 /* the current thread is already a fiber */
89 if(pTeb->IsFiber && pTeb->Tib.Fib.FiberData) return pTeb->Tib.Fib.FiberData;
90
91 /* allocate the fiber */
92 pfCurFiber = (PFIBER)RtlAllocateHeap(pTeb->Peb->ProcessHeap, 0, sizeof(FIBER));
93
94 /* failure */
95 if(pfCurFiber == NULL)
96 {
97 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
98 return NULL;
99 }
100
101 pfCurFiber->Parameter = lpParameter;
102 pfCurFiber->Flags = dwFlags;
103
104 /* copy some contextual data from the thread to the fiber */
105 pfCurFiber->ExceptionList = pTeb->Tib.ExceptionList;
106 pfCurFiber->StackBase = pTeb->Tib.StackBase;
107 pfCurFiber->StackLimit = pTeb->Tib.StackLimit;
108 pfCurFiber->DeallocationStack = pTeb->DeallocationStack;
109
110 /* associate the fiber to the current thread */
111 pTeb->Tib.Fib.FiberData = pfCurFiber;
112 pTeb->IsFiber = TRUE;
113
114 /* success */
115 return (LPVOID)pfCurFiber;
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 USER_STACK usFiberStack;
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 &usFiberStack,
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 &usFiberStack,
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(usFiberStack.FixedStackBase && usFiberStack.FixedStackLimit)
198 {
199 pfCurFiber->StackBase = usFiberStack.FixedStackBase;
200 pfCurFiber->StackLimit = usFiberStack.FixedStackLimit;
201 pfCurFiber->DeallocationStack = usFiberStack.FixedStackLimit;
202 }
203 /* expandable stack */
204 else if
205 (
206 usFiberStack.ExpandableStackBase &&
207 usFiberStack.ExpandableStackLimit &&
208 usFiberStack.ExpandableStackBottom
209 )
210 {
211 pfCurFiber->StackBase = usFiberStack.ExpandableStackBase;
212 pfCurFiber->StackLimit = usFiberStack.ExpandableStackLimit;
213 pfCurFiber->DeallocationStack = usFiberStack.ExpandableStackBottom;
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(), &usFiberStack);
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.Fib.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, NtCurrentTeb()->Tib.Fib.FiberData);
292 }
293
294 /* EOF */