update for HEAD-2003091401
[reactos.git] / lib / kernel32 / thread / fiber.c
index 27ef802..e04bd78 100644 (file)
  */
 #include <k32.h>
 
+#define NDEBUG
+#include <kernel32/kernel32.h>
 
+struct _FIBER                                           /* Field offsets:  */
+{                                                       /* 32 bit   64 bit */
+ /* this must be the first field */
+ LPVOID Parameter;                                      /*   0x00     0x00 */
 
-/**********************************************************************
- *     ConvertThreadToFiber
+ struct _EXCEPTION_REGISTRATION_RECORD * ExceptionList; /*   0x04     0x08 */
+ LPVOID StackBase;                                      /*   0x08     0x10 */
+ LPVOID StackLimit;                                     /*   0x0C     0x18 */
+ LPVOID DeallocationStack;                              /*   0x10     0x20 */
+ ULONG_PTR Flags;                                       /*   0x14     0x28 */
+#if defined(_M_IX86)
+ /* control flow registers */
+ DWORD Eip;                                             /*   0x18     ---- */
+ DWORD Esp;                                             /*   0x1C     ---- */
+ DWORD Ebp;                                             /*   0x20     ---- */
+
+ /* general-purpose registers that must be preserved across calls */
+ DWORD Ebx;                                             /*   0x24     ---- */
+ DWORD Esi;                                             /*   0x28     ---- */
+ DWORD Edi;                                             /*   0x2C     ---- */
+
+ /* floating point save area (optional) */
+ FLOATING_SAVE_AREA FloatSave;                          /*   0x30     ---- */
+#else
+#error Unspecified or unsupported architecture.
+#endif
+};
+
+typedef struct _FIBER FIBER, * PFIBER;
+
+__declspec(noreturn) void WINAPI FiberStartup(PVOID lpStartAddress);
+
+/*
+ * @implemented
  */
-LPVOID
-STDCALL
-ConvertThreadToFiber(
-       LPVOID lpArgument
-       )
+BOOL WINAPI ConvertFiberToThread(void)
 {
-       SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-       return NULL;
+ PTEB pTeb = NtCurrentTeb();
+
+ /* the current thread isn't running a fiber: failure */
+ if(!pTeb->IsFiber)
+ {
+  SetLastError(ERROR_INVALID_PARAMETER);
+  return FALSE;
+ }
+
+ /* this thread won't run a fiber anymore */
+ pTeb->IsFiber = FALSE;
+
+ /* free the fiber */
+ if(pTeb->Tib.Fib.FiberData != NULL)
+  RtlFreeHeap(pTeb->Peb->ProcessHeap, 0, pTeb->Tib.Fib.FiberData);
+
+ /* success */
+ return TRUE;
 }
 
 
-/**********************************************************************
- *     CreateFiber
+/*
+ * @implemented
  */
-LPVOID
-STDCALL
-CreateFiber(
-       DWORD                   dwStackSize,
-       LPFIBER_START_ROUTINE   lpStartAddress,
-       LPVOID                  lpArgument
-       )
+LPVOID WINAPI ConvertThreadToFiber(LPVOID lpParameter)
 {
-       SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-       return NULL;
+ return ConvertThreadToFiberEx(lpParameter, 0);
 }
 
 
-/**********************************************************************
- *     DeleteFiber
+/*
+ * @implemented
  */
-VOID
-STDCALL
-DeleteFiber(
-       LPVOID  lpFiber
-       )
+LPVOID WINAPI ConvertThreadToFiberEx(LPVOID lpParameter, DWORD dwFlags)
 {
-       SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-       return;
+ PTEB pTeb = NtCurrentTeb();
+ PFIBER pfCurFiber;
+
+ /* the current thread is already a fiber */
+ if(pTeb->IsFiber && pTeb->Tib.Fib.FiberData) return pTeb->Tib.Fib.FiberData;
+
+ /* allocate the fiber */
+ pfCurFiber = (PFIBER)RtlAllocateHeap(pTeb->Peb->ProcessHeap, 0, sizeof(FIBER));
+
+ /* failure */
+ if(pfCurFiber == NULL)
+ {
+  SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+  return NULL;
+ }
+
+ pfCurFiber->Parameter = lpParameter;
+ pfCurFiber->Flags = dwFlags;
+
+ /* copy some contextual data from the thread to the fiber */
+ pfCurFiber->ExceptionList = pTeb->Tib.ExceptionList;
+ pfCurFiber->StackBase = pTeb->Tib.StackBase;
+ pfCurFiber->StackLimit = pTeb->Tib.StackLimit;
+ pfCurFiber->DeallocationStack = pTeb->DeallocationStack;
+
+ /* associate the fiber to the current thread */
+ pTeb->Tib.Fib.FiberData = pfCurFiber;
+ pTeb->IsFiber = TRUE;
+
+ /* success */
+ return (LPVOID)pfCurFiber;
 }
 
 
-/**********************************************************************
- *     GetCurrentFiber
+/*
+ * @implemented
  */
-PVOID
-STDCALL
-GetCurrentFiber(VOID)
+LPVOID WINAPI CreateFiber
+(
+ SIZE_T dwStackSize,
+ LPFIBER_START_ROUTINE lpStartAddress,
+ LPVOID lpParameter
+)
 {
-       SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-       return NULL;
+ return CreateFiberEx(dwStackSize, 0, 0, lpStartAddress, lpParameter);
 }
 
 
-/**********************************************************************
- *     GetFiberData
+/*
+ * @implemented
  */
-PVOID
-STDCALL
-GetFiberData(VOID)
+LPVOID WINAPI CreateFiberEx
+(
+ SIZE_T dwStackCommitSize,
+ SIZE_T dwStackReserveSize,
+ DWORD dwFlags,
+ LPFIBER_START_ROUTINE lpStartAddress,
+ LPVOID lpParameter
+)
 {
-       SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-       return NULL;
+ PFIBER pfCurFiber;
+ NTSTATUS nErrCode;
+ PSIZE_T pnStackReserve = NULL;
+ PSIZE_T pnStackCommit = NULL;
+ USER_STACK usFiberStack;
+ CONTEXT ctxFiberContext;
+ PTEB pTeb = NtCurrentTeb();
+
+ /* allocate the fiber */
+ pfCurFiber = (PFIBER)RtlAllocateHeap(pTeb->Peb->ProcessHeap, 0, sizeof(FIBER));
+
+ /* failure */
+ if(pfCurFiber == NULL)
+ {
+  SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+  return NULL;
+ }
+
+ /* if the stack reserve or commit size weren't specified, use defaults */
+ if(dwStackReserveSize > 0) pnStackReserve = &dwStackReserveSize;
+ if(dwStackCommitSize > 0) pnStackCommit = &dwStackCommitSize;
+
+ /* create the stack for the fiber */
+ nErrCode = RtlRosCreateStack
+ (
+  NtCurrentProcess(),
+  &usFiberStack,
+  0,
+  pnStackReserve,
+  pnStackCommit
+ );
+
+ /* failure */
+ if(!NT_SUCCESS(nErrCode)) goto l_CleanupFiber;
+
+ /* initialize the context for the fiber */
+ nErrCode = RtlRosInitializeContext
+ (
+  NtCurrentProcess(),
+  &ctxFiberContext,
+  FiberStartup,
+  &usFiberStack,
+  1,
+  (ULONG_PTR *)&lpStartAddress
+ );
+
+ /* failure */
+ if(!NT_SUCCESS(nErrCode)) goto l_CleanupStack;
+
+ /* copy the data into the fiber */
+
+ /* fixed-size stack */
+ if(usFiberStack.FixedStackBase && usFiberStack.FixedStackLimit)
+ {
+  pfCurFiber->StackBase = usFiberStack.FixedStackBase;
+  pfCurFiber->StackLimit = usFiberStack.FixedStackLimit;
+  pfCurFiber->DeallocationStack = usFiberStack.FixedStackLimit;
+ }
+ /* expandable stack */
+ else if
+ (
+  usFiberStack.ExpandableStackBase &&
+  usFiberStack.ExpandableStackLimit &&
+  usFiberStack.ExpandableStackBottom
+ )
+ {
+  pfCurFiber->StackBase = usFiberStack.ExpandableStackBase;
+  pfCurFiber->StackLimit = usFiberStack.ExpandableStackLimit;
+  pfCurFiber->DeallocationStack = usFiberStack.ExpandableStackBottom;
+ }
+ /* bad initial stack */
+ else goto l_CleanupStack;
+
+ pfCurFiber->Parameter = lpParameter;
+ pfCurFiber->Flags = dwFlags;
+ pfCurFiber->ExceptionList = (struct _EXCEPTION_REGISTRATION_RECORD *)-1;
+
+#if defined(_M_IX86)
+
+ pfCurFiber->Eip = ctxFiberContext.Eip;
+ pfCurFiber->Esp = ctxFiberContext.Esp;
+ pfCurFiber->Ebp = ctxFiberContext.Ebp;
+ pfCurFiber->Ebx = ctxFiberContext.Ebx;
+ pfCurFiber->Esi = ctxFiberContext.Esi;
+ pfCurFiber->Edi = ctxFiberContext.Edi;
+
+ if(dwFlags & FIBER_FLAG_FLOAT_SWITCH)
+  pfCurFiber->FloatSave = ctxFiberContext.FloatSave;
+#else
+#error Unspecified or unsupported architecture.
+#endif
+ return pfCurFiber;
+
+l_CleanupStack:
+ /* free the stack */
+ RtlRosDeleteStack(NtCurrentProcess(), &usFiberStack);
+
+l_CleanupFiber:
+ /* free the fiber */
+ RtlFreeHeap(pTeb->Peb->ProcessHeap, 0, pfCurFiber);
+
+ /* failure */
+ assert(!NT_SUCCESS(nErrCode));
+ SetLastErrorByStatus(nErrCode);
+ return NULL;
 }
 
 
-/**********************************************************************
- *     SwitchToFiber
+/*
+ * @implemented
  */
-VOID
-STDCALL
-SwitchToFiber(
-       LPVOID  lpFiber
-       )
+void WINAPI DeleteFiber(LPVOID lpFiber)
 {
-       SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-       return;
+ SIZE_T nSize = 0;
+ PVOID pStackAllocBase = ((PFIBER)lpFiber)->DeallocationStack;
+ PTEB pTeb = NtCurrentTeb();
+
+ /* free the fiber */
+ RtlFreeHeap(pTeb->Peb->ProcessHeap, 0, lpFiber);
+
+ /* the fiber is deleting itself: let the system deallocate the stack */
+ if(pTeb->Tib.Fib.FiberData == lpFiber) ExitThread(1);
+
+ /* deallocate the stack */
+ NtFreeVirtualMemory
+ (
+  NtCurrentProcess(),
+  &pStackAllocBase,
+  &nSize,
+  MEM_RELEASE
+ );
 }
 
 
+__declspec(noreturn) extern void WINAPI ThreadStartup
+(
+ LPTHREAD_START_ROUTINE lpStartAddress,
+ LPVOID lpParameter
+);
+
+
+__declspec(noreturn) void WINAPI FiberStartup(PVOID lpStartAddress)
+{
+ /* FIXME? this should be pretty accurate */
+ ThreadStartup(lpStartAddress, NtCurrentTeb()->Tib.Fib.FiberData);
+}
+
 /* EOF */