2004-01-23 21:16:04 +00:00
|
|
|
/* $Id: fiber.c,v 1.9 2004/01/23 21:16:04 ekohl Exp $
|
2000-08-14 14:34:12 +00:00
|
|
|
*
|
|
|
|
* FILE: lib/kernel32/thread/fiber.c
|
|
|
|
*
|
|
|
|
* ReactOS Kernel32.dll
|
|
|
|
*
|
|
|
|
*/
|
2003-01-15 21:24:36 +00:00
|
|
|
#include <k32.h>
|
|
|
|
|
2003-05-29 00:36:41 +00:00
|
|
|
#define NDEBUG
|
2004-01-23 21:16:04 +00:00
|
|
|
#include "../include/debug.h"
|
2000-08-14 14:34:12 +00:00
|
|
|
|
2003-05-29 00:36:41 +00:00
|
|
|
struct _FIBER /* Field offsets: */
|
|
|
|
{ /* 32 bit 64 bit */
|
|
|
|
/* this must be the first field */
|
|
|
|
LPVOID Parameter; /* 0x00 0x00 */
|
2000-08-14 14:34:12 +00:00
|
|
|
|
2003-05-29 00:36:41 +00:00
|
|
|
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 ---- */
|
2000-08-14 14:34:12 +00:00
|
|
|
|
2003-05-29 00:36:41 +00:00
|
|
|
/* general-purpose registers that must be preserved across calls */
|
|
|
|
DWORD Ebx; /* 0x24 ---- */
|
|
|
|
DWORD Esi; /* 0x28 ---- */
|
|
|
|
DWORD Edi; /* 0x2C ---- */
|
2000-08-14 14:34:12 +00:00
|
|
|
|
2003-05-29 00:36:41 +00:00
|
|
|
/* 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);
|
|
|
|
|
2003-07-10 18:50:51 +00:00
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
2003-05-29 00:36:41 +00:00
|
|
|
BOOL WINAPI ConvertFiberToThread(void)
|
2000-08-14 14:34:12 +00:00
|
|
|
{
|
2003-05-29 00:36:41 +00:00
|
|
|
PTEB pTeb = NtCurrentTeb();
|
2000-08-14 14:34:12 +00:00
|
|
|
|
2003-05-29 00:36:41 +00:00
|
|
|
/* the current thread isn't running a fiber: failure */
|
|
|
|
if(!pTeb->IsFiber)
|
|
|
|
{
|
|
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
|
|
return FALSE;
|
|
|
|
}
|
2000-08-14 14:34:12 +00:00
|
|
|
|
2003-05-29 00:36:41 +00:00
|
|
|
/* 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 */
|
2003-09-12 17:51:48 +00:00
|
|
|
return TRUE;
|
2003-05-29 00:36:41 +00:00
|
|
|
}
|
|
|
|
|
2003-07-10 18:50:51 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
2003-05-29 00:36:41 +00:00
|
|
|
LPVOID WINAPI ConvertThreadToFiber(LPVOID lpParameter)
|
2000-08-14 14:34:12 +00:00
|
|
|
{
|
2003-05-29 00:36:41 +00:00
|
|
|
return ConvertThreadToFiberEx(lpParameter, 0);
|
2000-08-14 14:34:12 +00:00
|
|
|
}
|
|
|
|
|
2003-07-10 18:50:51 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
2003-05-29 00:36:41 +00:00
|
|
|
LPVOID WINAPI ConvertThreadToFiberEx(LPVOID lpParameter, DWORD dwFlags)
|
|
|
|
{
|
|
|
|
PTEB pTeb = NtCurrentTeb();
|
|
|
|
PFIBER pfCurFiber;
|
2000-08-14 14:34:12 +00:00
|
|
|
|
2003-05-29 00:36:41 +00:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2003-07-10 18:50:51 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
2003-05-29 00:36:41 +00:00
|
|
|
LPVOID WINAPI CreateFiber
|
|
|
|
(
|
|
|
|
SIZE_T dwStackSize,
|
|
|
|
LPFIBER_START_ROUTINE lpStartAddress,
|
|
|
|
LPVOID lpParameter
|
|
|
|
)
|
2000-08-14 14:34:12 +00:00
|
|
|
{
|
2003-05-29 00:36:41 +00:00
|
|
|
return CreateFiberEx(dwStackSize, 0, 0, lpStartAddress, lpParameter);
|
2000-08-14 14:34:12 +00:00
|
|
|
}
|
|
|
|
|
2003-07-10 18:50:51 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
2003-05-29 00:36:41 +00:00
|
|
|
LPVOID WINAPI CreateFiberEx
|
|
|
|
(
|
|
|
|
SIZE_T dwStackCommitSize,
|
|
|
|
SIZE_T dwStackReserveSize,
|
|
|
|
DWORD dwFlags,
|
|
|
|
LPFIBER_START_ROUTINE lpStartAddress,
|
|
|
|
LPVOID lpParameter
|
|
|
|
)
|
|
|
|
{
|
|
|
|
PFIBER pfCurFiber;
|
|
|
|
NTSTATUS nErrCode;
|
|
|
|
PSIZE_T pnStackReserve = NULL;
|
|
|
|
PSIZE_T pnStackCommit = NULL;
|
|
|
|
USER_STACK usFiberStack;
|
|
|
|
CONTEXT ctxFiberContext;
|
|
|
|
PTEB pTeb = NtCurrentTeb();
|
2000-08-14 14:34:12 +00:00
|
|
|
|
2003-05-29 00:36:41 +00:00
|
|
|
/* 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 */
|
2003-07-22 20:10:04 +00:00
|
|
|
nErrCode = RtlRosInitializeContext
|
2003-05-29 00:36:41 +00:00
|
|
|
(
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2003-07-10 18:50:51 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
2003-05-29 00:36:41 +00:00
|
|
|
void WINAPI DeleteFiber(LPVOID lpFiber)
|
2000-08-14 14:34:12 +00:00
|
|
|
{
|
2003-05-29 00:36:41 +00:00
|
|
|
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
|
|
|
|
);
|
2000-08-14 14:34:12 +00:00
|
|
|
}
|
|
|
|
|
2003-07-10 18:50:51 +00:00
|
|
|
|
2003-05-29 00:36:41 +00:00
|
|
|
__declspec(noreturn) extern void WINAPI ThreadStartup
|
|
|
|
(
|
|
|
|
LPTHREAD_START_ROUTINE lpStartAddress,
|
|
|
|
LPVOID lpParameter
|
|
|
|
);
|
2000-08-14 14:34:12 +00:00
|
|
|
|
2003-07-10 18:50:51 +00:00
|
|
|
|
2003-05-29 00:36:41 +00:00
|
|
|
__declspec(noreturn) void WINAPI FiberStartup(PVOID lpStartAddress)
|
2000-08-14 14:34:12 +00:00
|
|
|
{
|
2003-05-29 00:36:41 +00:00
|
|
|
/* FIXME? this should be pretty accurate */
|
|
|
|
ThreadStartup(lpStartAddress, NtCurrentTeb()->Tib.Fib.FiberData);
|
2000-08-14 14:34:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* EOF */
|