/*++ 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