[KERNEL32] Implement FlsAlloc/FlsFree based on Wine's implementation.
[reactos.git] / dll / win32 / kernel32 / client / fiber.c
index 338c9b7..ce6cd2d 100644 (file)
@@ -1,9 +1,9 @@
 /*
  * COPYRIGHT:   See COPYING in the top level directory
  * PROJECT:     ReactOS System Libraries
- * FILE:        lib/kernel32/thread/fiber.c
+ * FILE:        dll/win32/kernel32/client/fiber.c
  * PURPOSE:     Fiber Implementation
- * PROGRAMMERS: 
+ * PROGRAMMERS:
  *              Alex Ionescu (alex@relsoft.net)
  *              KJK::Hyperion <noog@libero.it>
  */
 #define NDEBUG
 #include <debug.h>
 
-typedef struct _FIBER                                      /* Field offsets:  */
-{                                                          /* 32 bit   64 bit */
-    /* this must be the first field */
-    LPVOID Parameter;                                      /*   0x00     0x00 */
-    struct _EXCEPTION_REGISTRATION_RECORD * ExceptionList; /*   0x04     0x08 */
-    LPVOID StackBase;                                      /*   0x08     0x10 */
-    LPVOID StackLimit;                                     /*   0x0C     0x18 */
-    LPVOID DeallocationStack;                              /*   0x10     0x20 */
-    CONTEXT Context;                                       /*   0x14     0x28 */
-    ULONG GuaranteedStackBytes;                            /*   0x2E0         */
-    PVOID FlsData;                                         /*   0x2E4         */
-    PVOID ActivationContextStack;                          /*   0x2E8         */
-} FIBER, *PFIBER;
+#ifdef _M_IX86
+C_ASSERT(FIELD_OFFSET(FIBER, ExceptionList) == 0x04);
+C_ASSERT(FIELD_OFFSET(FIBER, StackBase) == 0x08);
+C_ASSERT(FIELD_OFFSET(FIBER, StackLimit) == 0x0C);
+C_ASSERT(FIELD_OFFSET(FIBER, DeallocationStack) == 0x10);
+C_ASSERT(FIELD_OFFSET(FIBER, FiberContext) == 0x14);
+C_ASSERT(FIELD_OFFSET(FIBER, GuaranteedStackBytes) == 0x2E0);
+C_ASSERT(FIELD_OFFSET(FIBER, FlsData) == 0x2E4);
+C_ASSERT(FIELD_OFFSET(FIBER, ActivationContextStackPointer) == 0x2E8);
+#endif // _M_IX86
 
 /* PRIVATE FUNCTIONS **********************************************************/
 
 VOID
 WINAPI
-BaseRundownFls(IN PVOID FlsData)
+BaseRundownFls(_In_ PVOID FlsData)
 {
     /* No FLS support yet */
-    
 }
 
 /* PUBLIC FUNCTIONS ***********************************************************/
@@ -58,16 +54,18 @@ ConvertFiberToThread(VOID)
         return FALSE;
     }
 
-    /* this thread won't run a fiber anymore */
+    /* This thread won't run a fiber anymore */
     Teb->HasFiberData = FALSE;
     FiberData = Teb->NtTib.FiberData;
     Teb->NtTib.FiberData = NULL;
 
     /* Free the fiber */
     ASSERT(FiberData != NULL);
-    RtlFreeHeap(GetProcessHeap(), 0, FiberData);
+    RtlFreeHeap(GetProcessHeap(),
+                0,
+                FiberData);
 
-    /* success */
+    /* Success */
     return TRUE;
 }
 
@@ -76,15 +74,15 @@ ConvertFiberToThread(VOID)
  */
 LPVOID
 WINAPI
-ConvertThreadToFiberEx(LPVOID lpParameter, 
-                       DWORD dwFlags)
+ConvertThreadToFiberEx(_In_opt_ LPVOID lpParameter,
+                       _In_ DWORD dwFlags)
 {
     PTEB Teb;
     PFIBER Fiber;
     DPRINT1("Converting Thread to Fiber\n");
 
     /* Check for invalid flags */
-    if (dwFlags &FIBER_FLAG_FLOAT_SWITCH)
+    if (dwFlags & ~FIBER_FLAG_FLOAT_SWITCH)
     {
         /* Fail */
         SetLastError(ERROR_INVALID_PARAMETER);
@@ -101,29 +99,30 @@ ConvertThreadToFiberEx(LPVOID lpParameter,
     }
 
     /* Allocate the fiber */
-    Fiber = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(FIBER));
+    Fiber = RtlAllocateHeap(RtlGetProcessHeap(),
+                            0,
+                            sizeof(FIBER));
     if (!Fiber)
     {
+        /* Fail */
         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
         return NULL;
     }
 
     /* Copy some contextual data from the thread to the fiber */
-    Fiber->Parameter = lpParameter;
+    Fiber->FiberData = lpParameter;
     Fiber->ExceptionList = Teb->NtTib.ExceptionList;
     Fiber->StackBase = Teb->NtTib.StackBase;
     Fiber->StackLimit = Teb->NtTib.StackLimit;
     Fiber->DeallocationStack = Teb->DeallocationStack;
     Fiber->FlsData = Teb->FlsData;
     Fiber->GuaranteedStackBytes = Teb->GuaranteedStackBytes;
-    Fiber->ActivationContextStack = Teb->ActivationContextStackPointer;
-    Fiber->Context.ContextFlags = CONTEXT_FULL;
+    Fiber->ActivationContextStackPointer = Teb->ActivationContextStackPointer;
 
-    /* Save FPU State if requested */
-    if (dwFlags & FIBER_FLAG_FLOAT_SWITCH)
-    {
-        Fiber->Context.ContextFlags |= CONTEXT_FLOATING_POINT;
-    }
+    /* Save FPU State if requested, otherwise just the basic registers */
+    Fiber->FiberContext.ContextFlags = (dwFlags & FIBER_FLAG_FLOAT_SWITCH) ?
+                                       (CONTEXT_FULL | CONTEXT_FLOATING_POINT) :
+                                       CONTEXT_FULL;
 
     /* Associate the fiber to the current thread */
     Teb->NtTib.FiberData = Fiber;
@@ -138,10 +137,11 @@ ConvertThreadToFiberEx(LPVOID lpParameter,
  */
 LPVOID
 WINAPI
-ConvertThreadToFiber(LPVOID lpParameter)
+ConvertThreadToFiber(_In_opt_ LPVOID lpParameter)
 {
     /* Call the newer function */
-    return ConvertThreadToFiberEx(lpParameter, 0);
+    return ConvertThreadToFiberEx(lpParameter,
+                                  0);
 }
 
 /*
@@ -149,12 +149,16 @@ ConvertThreadToFiber(LPVOID lpParameter)
  */
 LPVOID
 WINAPI
-CreateFiber(SIZE_T dwStackSize,
-            LPFIBER_START_ROUTINE lpStartAddress,
-            LPVOID lpParameter)
+CreateFiber(_In_ SIZE_T dwStackSize,
+            _In_ LPFIBER_START_ROUTINE lpStartAddress,
+            _In_opt_ LPVOID lpParameter)
 {
     /* Call the Newer Function */
-    return CreateFiberEx(dwStackSize, 0, 0, lpStartAddress, lpParameter);
+    return CreateFiberEx(dwStackSize,
+                         0,
+                         0,
+                         lpStartAddress,
+                         lpParameter);
 }
 
 /*
@@ -162,20 +166,20 @@ CreateFiber(SIZE_T dwStackSize,
  */
 LPVOID
 WINAPI
-CreateFiberEx(SIZE_T dwStackCommitSize,
-              SIZE_T dwStackReserveSize,
-              DWORD dwFlags,
-              LPFIBER_START_ROUTINE lpStartAddress,
-              LPVOID lpParameter)
+CreateFiberEx(_In_ SIZE_T dwStackCommitSize,
+              _In_ SIZE_T dwStackReserveSize,
+              _In_ DWORD dwFlags,
+              _In_ LPFIBER_START_ROUTINE lpStartAddress,
+              _In_opt_ LPVOID lpParameter)
 {
     PFIBER Fiber;
     NTSTATUS Status;
     INITIAL_TEB InitialTeb;
-    PVOID ActivationContextStack = NULL;
+    PACTIVATION_CONTEXT_STACK ActivationContextStackPointer;
     DPRINT("Creating Fiber\n");
 
     /* Check for invalid flags */
-    if (dwFlags &FIBER_FLAG_FLOAT_SWITCH)
+    if (dwFlags & ~FIBER_FLAG_FLOAT_SWITCH)
     {
         /* Fail */
         SetLastError(ERROR_INVALID_PARAMETER);
@@ -183,7 +187,8 @@ CreateFiberEx(SIZE_T dwStackCommitSize,
     }
 
     /* Allocate the Activation Context Stack */
-    Status = RtlAllocateActivationContextStack(&ActivationContextStack);
+    ActivationContextStackPointer = NULL;
+    Status = RtlAllocateActivationContextStack(&ActivationContextStackPointer);
     if (!NT_SUCCESS(Status))
     {
         /* Fail */
@@ -192,9 +197,14 @@ CreateFiberEx(SIZE_T dwStackCommitSize,
     }
 
     /* Allocate the fiber */
-    Fiber = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(FIBER));
+    Fiber = RtlAllocateHeap(RtlGetProcessHeap(),
+                            0,
+                            sizeof(FIBER));
     if (!Fiber)
     {
+        /* Free the activation context stack */
+        RtlFreeActivationContextStack(ActivationContextStackPointer);
+
         /* Fail */
         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
         return NULL;
@@ -208,11 +218,12 @@ CreateFiberEx(SIZE_T dwStackCommitSize,
     if (!NT_SUCCESS(Status))
     {
         /* Free the fiber */
-        RtlFreeHeap(GetProcessHeap(), 0, Fiber);
+        RtlFreeHeap(GetProcessHeap(),
+                    0,
+                    Fiber);
 
-        /* Free the activation context */
-        DPRINT1("Leaking activation stack because nobody implemented free");
-        //RtlFreeActivationContextStack(&ActivationContextStack);
+        /* Free the activation context stack */
+        RtlFreeActivationContextStack(ActivationContextStackPointer);
 
         /* Failure */
         BaseSetLastNTError(Status);
@@ -220,24 +231,26 @@ CreateFiberEx(SIZE_T dwStackCommitSize,
     }
 
     /* Clear the context */
-    RtlZeroMemory(&Fiber->Context, sizeof(CONTEXT));
+    RtlZeroMemory(&Fiber->FiberContext,
+                  sizeof(CONTEXT));
 
     /* Copy the data into the fiber */
     Fiber->StackBase = InitialTeb.StackBase;
     Fiber->StackLimit = InitialTeb.StackLimit;
     Fiber->DeallocationStack = InitialTeb.AllocatedStackBase;
-    Fiber->Parameter = lpParameter;
+    Fiber->FiberData = lpParameter;
     Fiber->ExceptionList = EXCEPTION_CHAIN_END;
     Fiber->GuaranteedStackBytes = 0;
     Fiber->FlsData = NULL;
-    Fiber->ActivationContextStack = ActivationContextStack;
-    Fiber->Context.ContextFlags = CONTEXT_FULL;
+    Fiber->ActivationContextStackPointer = ActivationContextStackPointer;
 
-    /* Save FPU State if requested */
-    Fiber->Context.ContextFlags = (dwFlags & FIBER_FLAG_FLOAT_SWITCH) ? CONTEXT_FLOATING_POINT : 0;
+    /* Save FPU State if requested, otherwise just the basic registers */
+    Fiber->FiberContext.ContextFlags = (dwFlags & FIBER_FLAG_FLOAT_SWITCH) ?
+                                       (CONTEXT_FULL | CONTEXT_FLOATING_POINT) :
+                                       CONTEXT_FULL;
 
-    /* initialize the context for the fiber */
-    BaseInitializeContext(&Fiber->Context,
+    /* Initialize the context for the fiber */
+    BaseInitializeContext(&Fiber->FiberContext,
                           lpParameter,
                           lpStartAddress,
                           InitialTeb.StackBase,
@@ -252,17 +265,24 @@ CreateFiberEx(SIZE_T dwStackCommitSize,
  */
 VOID
 WINAPI
-DeleteFiber(LPVOID lpFiber)
+DeleteFiber(_In_ LPVOID lpFiber)
 {
-    SIZE_T Size = 0;
-    PFIBER Fiber = (PFIBER)lpFiber;
+    SIZE_T Size;
+    PFIBER Fiber;
     PTEB Teb;
 
-    /* First, exit the thread */
+    /* Are we deleting ourselves? */
     Teb = NtCurrentTeb();
-    if ((Teb->HasFiberData) && (Teb->NtTib.FiberData == Fiber)) ExitThread(1);
+    Fiber = (PFIBER)lpFiber;
+    if ((Teb->HasFiberData) &&
+        (Teb->NtTib.FiberData == Fiber))
+    {
+        /* Just exit */
+        ExitThread(1);
+    }
 
-    /* Now de-allocate the stack */
+    /* Not ourselves, de-allocate the stack */
+    Size = 0 ;
     NtFreeVirtualMemory(NtCurrentProcess(),
                         &Fiber->DeallocationStack,
                         &Size,
@@ -271,12 +291,13 @@ DeleteFiber(LPVOID lpFiber)
     /* Get rid of FLS */
     if (Fiber->FlsData) BaseRundownFls(Fiber->FlsData);
 
-    /* Get rid of the activation stack */
-    DPRINT1("Leaking activation stack because nobody implemented free");
-    //RtlFreeActivationContextStack(Fiber->ActivationContextStack);
+    /* Get rid of the activation context stack */
+    RtlFreeActivationContextStack(Fiber->ActivationContextStackPointer);
 
     /* Free the fiber data */
-    RtlFreeHeap(GetProcessHeap(), 0, lpFiber);
+    RtlFreeHeap(GetProcessHeap(),
+                0,
+                lpFiber);
 }
 
 /*
@@ -286,36 +307,102 @@ BOOL
 WINAPI
 IsThreadAFiber(VOID)
 {
+    /* Return flag in the TEB */
     return NtCurrentTeb()->HasFiberData;
 }
 
 /*
- * @unimplemented
+ * @implemented
  */
 DWORD
 WINAPI
 FlsAlloc(PFLS_CALLBACK_FUNCTION lpCallback)
 {
-   (void)lpCallback;
+    DWORD dwFlsIndex;
+    PPEB Peb = NtCurrentPeb();
+    PVOID *ppFlsSlots;
+
+    RtlAcquirePebLock();
+
+    ppFlsSlots = NtCurrentTeb()->FlsData;
 
-   UNIMPLEMENTED;
-   SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-   return FLS_OUT_OF_INDEXES;
+    if (!Peb->FlsCallback &&
+        !(Peb->FlsCallback = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY,
+                                             FLS_MAXIMUM_AVAILABLE * sizeof(PVOID))))
+    {
+        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+        dwFlsIndex = FLS_OUT_OF_INDEXES;
+    }
+    else
+    {
+        dwFlsIndex = RtlFindClearBitsAndSet(Peb->FlsBitmap, 1, 1);
+        if (dwFlsIndex != FLS_OUT_OF_INDEXES)
+        {
+            if (!ppFlsSlots &&
+                !(ppFlsSlots = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY,
+                                               (FLS_MAXIMUM_AVAILABLE + 2) * sizeof(PVOID))))
+            {
+                RtlClearBits(Peb->FlsBitmap, dwFlsIndex, 1);
+                dwFlsIndex = FLS_OUT_OF_INDEXES;
+                SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+            }
+            else
+            {
+                if (!NtCurrentTeb()->FlsData)
+                    NtCurrentTeb()->FlsData = ppFlsSlots;
+
+                if (lpCallback)
+                    DPRINT1("FlsAlloc: Got lpCallback 0x%p, UNIMPLEMENTED!", lpCallback);
+
+                ppFlsSlots[dwFlsIndex + 2] = NULL; /* clear the value */
+                Peb->FlsCallback[dwFlsIndex] = lpCallback;
+            }
+        }
+        else
+        {
+            SetLastError(ERROR_NO_MORE_ITEMS);
+        }
+    }
+    RtlReleasePebLock();
+    return dwFlsIndex;
 }
 
 
 /*
- * @unimplemented
+ * @implemented
  */
 BOOL
 WINAPI
 FlsFree(DWORD dwFlsIndex)
 {
-    (void)dwFlsIndex;
+    BOOL ret;
+    PPEB Peb = NtCurrentPeb();
+    PVOID *ppFlsSlots;
+
+    if (dwFlsIndex >= FLS_MAXIMUM_AVAILABLE)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
 
-    UNIMPLEMENTED;
-    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-    return FALSE;
+    RtlAcquirePebLock();
+
+    ppFlsSlots = NtCurrentTeb()->FlsData;
+    ret = RtlAreBitsSet(Peb->FlsBitmap, dwFlsIndex, 1);
+    if (ret)
+    {
+        RtlClearBits(Peb->FlsBitmap, dwFlsIndex, 1);
+        /* FIXME: call Fls callback */
+        /* FIXME: add equivalent of ThreadZeroTlsCell here */
+        if (ppFlsSlots)
+            ppFlsSlots[dwFlsIndex + 2] = NULL;
+    }
+    else
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+    }
+    RtlReleasePebLock();
+    return ret;
 }
 
 
@@ -327,22 +414,16 @@ WINAPI
 FlsGetValue(DWORD dwFlsIndex)
 {
     PVOID *ppFlsSlots;
-    PVOID pRetVal;
-
-    if(dwFlsIndex >= 128) goto l_InvalidParam;
 
     ppFlsSlots = NtCurrentTeb()->FlsData;
+    if (!dwFlsIndex || dwFlsIndex >= FLS_MAXIMUM_AVAILABLE || !ppFlsSlots)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return NULL;
+    }
 
-    if(ppFlsSlots == NULL) goto l_InvalidParam;
-
-    SetLastError(0);
-    pRetVal = ppFlsSlots[dwFlsIndex + 2];
-
-    return pRetVal;
-
-l_InvalidParam:
-    SetLastError(ERROR_INVALID_PARAMETER);
-    return NULL;
+    SetLastError(ERROR_SUCCESS);
+    return ppFlsSlots[dwFlsIndex + 2];
 }
 
 
@@ -351,46 +432,26 @@ l_InvalidParam:
  */
 BOOL
 WINAPI
-FlsSetValue(DWORD dwFlsIndex, PVOID lpFlsData)
+FlsSetValue(DWORD dwFlsIndex,
+            PVOID lpFlsData)
 {
     PVOID *ppFlsSlots;
-    TEB *pTeb = NtCurrentTeb();
-
-    if(dwFlsIndex >= 128) goto l_InvalidParam;
-
-    ppFlsSlots = pTeb->FlsData;
 
-    if (ppFlsSlots == NULL)
+    if (!dwFlsIndex || dwFlsIndex >= FLS_MAXIMUM_AVAILABLE)
     {
-        PEB *pPeb = pTeb->ProcessEnvironmentBlock;
-
-        ppFlsSlots = RtlAllocateHeap(pPeb->ProcessHeap,
-                                     HEAP_ZERO_MEMORY,
-                                     (128 + 2) * sizeof(PVOID));
-        if(ppFlsSlots == NULL) goto l_OutOfMemory;
-
-        pTeb->FlsData = ppFlsSlots;
-
-        RtlAcquirePebLock();
-
-        /* TODO: initialization */
-
-        RtlReleasePebLock();
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
     }
-
+    if (!NtCurrentTeb()->FlsData &&
+        !(NtCurrentTeb()->FlsData = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY,
+                                                    (FLS_MAXIMUM_AVAILABLE + 2) * sizeof(PVOID))))
+    {
+        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+        return FALSE;
+    }
+    ppFlsSlots = NtCurrentTeb()->FlsData;
     ppFlsSlots[dwFlsIndex + 2] = lpFlsData;
-
     return TRUE;
-
-l_OutOfMemory:
-    SetLastError(ERROR_NOT_ENOUGH_MEMORY);
-    goto l_Fail;
-
-l_InvalidParam:
-    SetLastError(ERROR_INVALID_PARAMETER);
-
-l_Fail:
-    return FALSE;
 }
 
 /* EOF */