367 lines
8.2 KiB
C
367 lines
8.2 KiB
C
/*++
|
|
|
|
Copyright (c) 2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
fiber.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the Win32 fiber services.
|
|
|
|
--*/
|
|
|
|
#include "basedll.h"
|
|
#pragma hdrstop
|
|
#include "dm.h"
|
|
|
|
//
|
|
// Per thread fiber data pointer.
|
|
//
|
|
__declspec(thread) LPVOID XapiCurrentFiber;
|
|
|
|
//
|
|
// For threads that have been converted to fibers, this per thread structure
|
|
// holds its fiber data.
|
|
//
|
|
__declspec(thread) XFIBER XapiThreadFiberData;
|
|
|
|
VOID
|
|
XapiFiberStartup(
|
|
LPFIBER_START_ROUTINE lpStartAddress
|
|
)
|
|
{
|
|
__try {
|
|
|
|
lpStartAddress(GetFiberData());
|
|
|
|
} __except (UnhandledExceptionFilter(GetExceptionInformation())) {
|
|
|
|
//
|
|
// UnhandledExceptionFilter will return either EXCEPTION_CONTINUE_SEARCH,
|
|
// in which case, the exception search will stop since we're the top of
|
|
// the exception stack, or it will return EXCEPTION_CONTINUE_EXECUTION.
|
|
// We'll never execute this handler.
|
|
//
|
|
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
RIP("Fiber should not return.");
|
|
|
|
KeBugCheck(0);
|
|
}
|
|
|
|
LPVOID
|
|
WINAPI
|
|
CreateFiber(
|
|
IN DWORD dwStackSize,
|
|
IN LPFIBER_START_ROUTINE lpStartAddress,
|
|
IN LPVOID lpParameter
|
|
)
|
|
{
|
|
PVOID StackBase;
|
|
PXFIBER Fiber;
|
|
PULONG_PTR Context;
|
|
|
|
//
|
|
// Allocate the stack for the fiber.
|
|
//
|
|
|
|
if (dwStackSize == 0) {
|
|
dwStackSize = XeImageHeader()->SizeOfStackCommit;
|
|
}
|
|
|
|
if (dwStackSize < KERNEL_STACK_SIZE) {
|
|
dwStackSize = KERNEL_STACK_SIZE;
|
|
}
|
|
|
|
dwStackSize = ROUND_TO_PAGES(dwStackSize);
|
|
|
|
StackBase = MmCreateKernelStack(dwStackSize, FALSE);
|
|
|
|
if (StackBase == NULL) {
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Allocate the fiber structure at the top of the fiber stack.
|
|
//
|
|
|
|
Fiber = (PXFIBER)StackBase - 1;
|
|
|
|
//
|
|
// Initialize the fiber state.
|
|
//
|
|
|
|
Fiber->FiberData = lpParameter;
|
|
Fiber->StackBase = StackBase;
|
|
Fiber->StackLimit = (PUCHAR)StackBase - dwStackSize;
|
|
|
|
//
|
|
// Initialize the start context for the fiber. The start context consists
|
|
// of the parameter to XapiFiberStartup, a dummy return address for the
|
|
// 'call' to XapiFiberStartup (we use XapiFiberStartup itself in order to
|
|
// make the kernel debugger happy), a dummy parameter to SwitchToFiber, the
|
|
// return address for SwitchToFiber, and the initial non-volatile context.
|
|
//
|
|
|
|
Context = (PULONG_PTR)Fiber - 1;
|
|
|
|
*(Context--) = (ULONG_PTR)lpStartAddress;
|
|
*(Context--) = (ULONG_PTR)XapiFiberStartup;
|
|
*(Context--) = 0;
|
|
*(Context--) = (ULONG_PTR)XapiFiberStartup;
|
|
*(Context--) = 0;
|
|
*(Context--) = 0;
|
|
*(Context--) = 0;
|
|
*(Context--) = 0;
|
|
*(Context) = (ULONG_PTR)EXCEPTION_CHAIN_END;
|
|
|
|
Fiber->KernelStack = Context;
|
|
|
|
#if 0
|
|
// Tell the debugger we've created the fiber
|
|
if(KeGetCurrentPrcb()->DebugMonitorData) {
|
|
_asm {
|
|
mov eax, BREAKPOINT_CREATE_FIBER
|
|
mov ecx, Fiber
|
|
mov edx, lpStartAddress
|
|
int 2dh
|
|
int 3
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return Fiber;
|
|
}
|
|
|
|
VOID
|
|
WINAPI
|
|
DeleteFiber(
|
|
IN LPVOID lpFiber
|
|
)
|
|
{
|
|
PXFIBER Fiber;
|
|
|
|
RIP_ON_NOT_TRUE("DeleteFiber()", (lpFiber != NULL));
|
|
|
|
Fiber = (PXFIBER)lpFiber;
|
|
|
|
//
|
|
// Unlike Win32, we cannot delete the currently executing fiber. Win32
|
|
// would call ExitThread here, but if we do that, then that doesn't end up
|
|
// freeing the fiber's stack, but ends up killing a thread that called
|
|
// ConvertThreadToFiber.
|
|
//
|
|
|
|
if (Fiber->KernelStack == NULL) {
|
|
RIP("DeleteFiber() - Cannot delete an active fiber.");
|
|
}
|
|
|
|
//
|
|
// Fibers created from ConvertThreadToFiber use the stack of the calling
|
|
// thread. We can't delete this fiber without destroying the thread. We
|
|
// could choose to delete just the fiber structure, but that alters the
|
|
// behavior of the original API too much.
|
|
//
|
|
|
|
if (Fiber->StackLimit == NULL) {
|
|
RIP("DeleteFiber() - Cannot delete a fiber from ConvertThreadToFiber.");
|
|
}
|
|
|
|
#if 0
|
|
// Tell the debugger this fiber is going away
|
|
if(KeGetCurrentPrcb()->DebugMonitorData) {
|
|
_asm {
|
|
mov eax, BREAKPOINT_DELETE_FIBER
|
|
mov ecx, Fiber
|
|
int 2dh
|
|
int 3
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Delete the fiber's stack. The fiber structure is allocated as a part of
|
|
// the stack, so Fiber is not valid after this point.
|
|
//
|
|
|
|
MmDeleteKernelStack(Fiber->StackBase, Fiber->StackLimit);
|
|
}
|
|
|
|
LPVOID
|
|
WINAPI
|
|
ConvertThreadToFiber(
|
|
IN LPVOID lpParameter
|
|
)
|
|
{
|
|
PXFIBER Fiber;
|
|
|
|
//
|
|
// Check that the current thread isn't already hosting a fiber.
|
|
//
|
|
|
|
if (XapiCurrentFiber != NULL) {
|
|
RIP("ConvertThreadToFiber() - Cannot convert a thread to a fiber multiple times.");
|
|
}
|
|
|
|
//
|
|
// Initialize the fiber state.
|
|
//
|
|
|
|
Fiber = &XapiThreadFiberData;
|
|
|
|
Fiber->FiberData = lpParameter;
|
|
Fiber->StackBase = KeGetCurrentThread()->StackBase;
|
|
Fiber->StackLimit = NULL;
|
|
|
|
#if 0
|
|
// Tell the debugger how to find a fiber
|
|
DmTell(DMTELL_FIBERTLS, (PVOID)((ULONG_PTR)&XapiCurrentFiber -
|
|
(ULONG_PTR)KeGetCurrentThread()->TlsData));
|
|
|
|
// Tell the debugger we've created the fiber
|
|
if(KeGetCurrentPrcb()->DebugMonitorData) {
|
|
_asm {
|
|
mov eax, BREAKPOINT_CREATE_FIBER
|
|
mov ecx, Fiber
|
|
xor edx, edx
|
|
int 2dh
|
|
int 3
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Make this fiber the current fiber for the thread.
|
|
//
|
|
|
|
XapiCurrentFiber = Fiber;
|
|
|
|
return Fiber;
|
|
}
|
|
|
|
__declspec(naked)
|
|
VOID
|
|
WINAPI
|
|
#if DBG
|
|
XapiSwitchToFiberRetail(
|
|
#else
|
|
SwitchToFiber(
|
|
#endif
|
|
LPVOID lpFiber
|
|
)
|
|
{
|
|
__asm {
|
|
|
|
//
|
|
// Fetch the offset to the TLS data and the base pointer to the TLS
|
|
// data.
|
|
//
|
|
|
|
mov edx,_tls_index
|
|
mov ecx,fs:[NT_TIB.StackBase]
|
|
|
|
//
|
|
// Load the pointer to the new fiber.
|
|
//
|
|
|
|
mov eax,[esp+4]
|
|
|
|
//
|
|
// Save the old fiber's nonvolatile state and exception list.
|
|
//
|
|
|
|
push ebp
|
|
push esi
|
|
push edi
|
|
push ebx
|
|
push DWORD PTR fs:[NT_TIB.ExceptionList]
|
|
|
|
//
|
|
// Load the pointer to the old fiber from XapiCurrentFiber.
|
|
//
|
|
|
|
mov edx,[ecx+edx*4]
|
|
mov ecx,XapiCurrentFiber[edx]
|
|
|
|
//
|
|
// Switch from the old fiber's stack to the new fiber's stack.
|
|
//
|
|
|
|
mov [ecx+XFIBER.KernelStack],esp
|
|
mov esp,[eax+XFIBER.KernelStack]
|
|
|
|
#if DBG
|
|
//
|
|
// Null out the kernel stack pointer so that we can use this as a flag
|
|
// in SwitchToFiber to detect a fiber that's already running.
|
|
//
|
|
|
|
mov DWORD PTR [eax+XFIBER.KernelStack], 0
|
|
#endif
|
|
|
|
//
|
|
// Update XapiCurrentFiber to point at the new fiber.
|
|
//
|
|
|
|
mov XapiCurrentFiber[edx],eax
|
|
|
|
//
|
|
// Restore the new fiber's nonvolatile state and exception list.
|
|
//
|
|
|
|
pop DWORD PTR fs:[NT_TIB.ExceptionList]
|
|
pop ebx
|
|
pop edi
|
|
pop esi
|
|
pop ebp
|
|
|
|
ret 4
|
|
}
|
|
}
|
|
|
|
#if DBG
|
|
|
|
VOID
|
|
WINAPI
|
|
SwitchToFiber(
|
|
LPVOID lpFiber
|
|
)
|
|
{
|
|
PXFIBER Fiber;
|
|
|
|
RIP_ON_NOT_TRUE("SwitchToFiber()", (lpFiber != NULL));
|
|
|
|
//
|
|
// Check that the current thread has called ConvertThreadToFiber.
|
|
//
|
|
|
|
if (XapiCurrentFiber == NULL) {
|
|
RIP("SwitchToFiber() - Thread hasn't called ConvertThreadToFiber().");
|
|
}
|
|
|
|
//
|
|
// Check that the fiber isn't already running on another thread. We'll
|
|
// allow a fiber to switch to itself on the same thread (Win32 documents
|
|
// this as "unpredictable".)
|
|
//
|
|
|
|
Fiber = (PXFIBER)lpFiber;
|
|
|
|
if ((Fiber->KernelStack == NULL) && (XapiCurrentFiber != Fiber)) {
|
|
RIP("SwitchToFiber() - Fiber is already active on another thread.");
|
|
}
|
|
|
|
//
|
|
// Jump to the retail version of this routine.
|
|
//
|
|
|
|
XapiSwitchToFiberRetail(lpFiber);
|
|
}
|
|
|
|
#endif
|