/* * 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 #include "winbase.h" #include "winerror.h" #include "wine/exception.h" #include "thread.h" struct fiber_data { LPVOID param; /* 00 fiber param */ void *except; /* 04 saved exception handlers list */ void *stack_top; /* 08 top of fiber stack */ void *stack_low; /* 0c fiber stack low-water mark */ void *stack_base; /* 10 base of the fiber stack */ jmp_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_base = VirtualAlloc( 0, stack_reserve, MEM_COMMIT, PAGE_EXECUTE_READWRITE ))) { HeapFree( GetProcessHeap(), 0, fiber ); return NULL; } fiber->stack_top = (char *)fiber->stack_base + stack_reserve; fiber->stack_low = fiber->stack_base; 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()->fiber) { HeapFree( GetProcessHeap(), 0, fiber ); ExitThread(1); } VirtualFree( fiber->stack_base, 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()->except; fiber->stack_top = NtCurrentTeb()->stack_top; fiber->stack_low = NtCurrentTeb()->stack_low; fiber->stack_base = NtCurrentTeb()->stack_base; fiber->start = NULL; fiber->flags = flags; NtCurrentTeb()->fiber = fiber; return fiber; } /*********************************************************************** * ConvertFiberToThread (KERNEL32.@) */ BOOL WINAPI ConvertFiberToThread(void) { struct fiber_data *fiber = NtCurrentTeb()->fiber; if (fiber) { NtCurrentTeb()->fiber = 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()->fiber; current_fiber->except = NtCurrentTeb()->except; current_fiber->stack_low = NtCurrentTeb()->stack_low; /* stack_base and stack_top never change */ /* FIXME: should save floating point context if requested in fiber->flags */ if (!setjmp( current_fiber->jmpbuf )) { NtCurrentTeb()->fiber = new_fiber; NtCurrentTeb()->except = new_fiber->except; NtCurrentTeb()->stack_top = new_fiber->stack_top; NtCurrentTeb()->stack_low = new_fiber->stack_low; NtCurrentTeb()->stack_base = new_fiber->stack_base; if (new_fiber->start) /* first time */ SYSDEPS_SwitchToThreadStack( start_fiber, new_fiber ); else longjmp( new_fiber->jmpbuf, 1 ); } }