/* * Fiber support * * Copyright 2002 Alexandre Julliard * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * FIXME: * - proper handling of 16-bit stack and signal stack */ #include "config.h" #include "wine/port.h" #include #include #define NONAMELESSUNION #include "windef.h" #include "winbase.h" #include "winerror.h" #include "wine/exception.h" #include "wine/library.h" #include "thread.h" struct fiber_data { LPVOID param; /* 00 fiber param */ void *except; /* 04 saved exception handlers list */ void *stack_base; /* 08 top of fiber stack */ void *stack_limit; /* 0c fiber stack low-water mark */ void *stack_allocation; /* 10 base of the fiber stack allocation */ sigjmp_buf jmpbuf; /* 14 setjmp buffer (on Windows: CONTEXT) */ DWORD flags; /* fiber flags */ LPFIBER_START_ROUTINE start; /* start routine */ }; /* call the fiber initial function once we have switched stack */ static void start_fiber( void *arg ) { struct fiber_data *fiber = arg; LPFIBER_START_ROUTINE start = fiber->start; __TRY { fiber->start = NULL; start( fiber->param ); ExitThread( 1 ); } __EXCEPT(UnhandledExceptionFilter) { TerminateThread( GetCurrentThread(), GetExceptionCode() ); } __ENDTRY } /*********************************************************************** * CreateFiber (KERNEL32.@) */ LPVOID WINAPI CreateFiber( SIZE_T stack, LPFIBER_START_ROUTINE start, LPVOID param ) { return CreateFiberEx( stack, 0, 0, start, param ); } /*********************************************************************** * CreateFiberEx (KERNEL32.@) */ LPVOID WINAPI CreateFiberEx( SIZE_T stack_commit, SIZE_T stack_reserve, DWORD flags, LPFIBER_START_ROUTINE start, LPVOID param ) { struct fiber_data *fiber; if (!(fiber = HeapAlloc( GetProcessHeap(), 0, sizeof(*fiber) ))) { SetLastError( ERROR_NOT_ENOUGH_MEMORY ); return NULL; } /* FIXME: should use the thread stack allocation routines here */ if (!stack_reserve) stack_reserve = 1024*1024; if(!(fiber->stack_allocation = VirtualAlloc( 0, stack_reserve, MEM_COMMIT, PAGE_EXECUTE_READWRITE ))) { HeapFree( GetProcessHeap(), 0, fiber ); return NULL; } fiber->stack_base = (char *)fiber->stack_allocation + stack_reserve; fiber->stack_limit = fiber->stack_allocation; fiber->param = param; fiber->except = (void *)-1; fiber->start = start; fiber->flags = flags; return fiber; } /*********************************************************************** * DeleteFiber (KERNEL32.@) */ void WINAPI DeleteFiber( LPVOID fiber_ptr ) { struct fiber_data *fiber = fiber_ptr; if (!fiber) return; if (fiber == NtCurrentTeb()->Tib.u.FiberData) { HeapFree( GetProcessHeap(), 0, fiber ); ExitThread(1); } VirtualFree( fiber->stack_allocation, 0, MEM_RELEASE ); HeapFree( GetProcessHeap(), 0, fiber ); } /*********************************************************************** * ConvertThreadToFiber (KERNEL32.@) */ LPVOID WINAPI ConvertThreadToFiber( LPVOID param ) { return ConvertThreadToFiberEx( param, 0 ); } /*********************************************************************** * ConvertThreadToFiberEx (KERNEL32.@) */ LPVOID WINAPI ConvertThreadToFiberEx( LPVOID param, DWORD flags ) { struct fiber_data *fiber; if (!(fiber = HeapAlloc( GetProcessHeap(), 0, sizeof(*fiber) ))) { SetLastError( ERROR_NOT_ENOUGH_MEMORY ); return NULL; } fiber->param = param; fiber->except = NtCurrentTeb()->Tib.ExceptionList; fiber->stack_base = NtCurrentTeb()->Tib.StackBase; fiber->stack_limit = NtCurrentTeb()->Tib.StackLimit; fiber->stack_allocation = NtCurrentTeb()->DeallocationStack; fiber->start = NULL; fiber->flags = flags; NtCurrentTeb()->Tib.u.FiberData = fiber; return fiber; } /*********************************************************************** * ConvertFiberToThread (KERNEL32.@) */ BOOL WINAPI ConvertFiberToThread(void) { struct fiber_data *fiber = NtCurrentTeb()->Tib.u.FiberData; if (fiber) { NtCurrentTeb()->Tib.u.FiberData = NULL; HeapFree( GetProcessHeap(), 0, fiber ); } return TRUE; } /*********************************************************************** * SwitchToFiber (KERNEL32.@) */ void WINAPI SwitchToFiber( LPVOID fiber ) { struct fiber_data *new_fiber = fiber; struct fiber_data *current_fiber = NtCurrentTeb()->Tib.u.FiberData; current_fiber->except = NtCurrentTeb()->Tib.ExceptionList; current_fiber->stack_limit = NtCurrentTeb()->Tib.StackLimit; /* stack_allocation and stack_base never change */ /* FIXME: should save floating point context if requested in fiber->flags */ if (!sigsetjmp( current_fiber->jmpbuf, 1 )) { NtCurrentTeb()->Tib.u.FiberData = new_fiber; NtCurrentTeb()->Tib.ExceptionList = new_fiber->except; NtCurrentTeb()->Tib.StackBase = new_fiber->stack_base; NtCurrentTeb()->Tib.StackLimit = new_fiber->stack_limit; NtCurrentTeb()->DeallocationStack = new_fiber->stack_allocation; if (new_fiber->start) /* first time */ wine_switch_to_stack( start_fiber, new_fiber, new_fiber->stack_base ); else siglongjmp( new_fiber->jmpbuf, 1 ); } }