update for HEAD-2003091401
[reactos.git] / 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 <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  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 */