| /* | |
| * 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 <setjmp.h> | |
| #include "winbase.h" | |
| #include "winerror.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) | |
| { | |
| struct fiber_data *fiber = NtCurrentTeb()->fiber; | |
| LPFIBER_START_ROUTINE start = fiber->start; | |
| fiber->start = NULL; | |
| start( fiber->param ); | |
| ExitThread( 1 ); | |
| } | |
| /*********************************************************************** | |
| * 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 ); | |
| else | |
| longjmp( new_fiber->jmpbuf, 1 ); | |
| } | |
| } |