/* * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * * FIXME: * - proper handling of 16-bit stack and signal stack */ #include "config.h" #include "wine/port.h" #include <setjmp.h> #include <stdarg.h> #define NONAMELESSUNION #include "windef.h" #include "winbase.h" #include "winerror.h" #include "winternl.h" #include "wine/exception.h" #include "wine/library.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(fiber); 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 */ void **fls_slots; /* fiber storage slots */ }; /* 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_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; fiber->fls_slots = NULL; 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->fls_slots ); 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; fiber->fls_slots = NtCurrentTeb()->FlsSlots; 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; current_fiber->fls_slots = NtCurrentTeb()->FlsSlots; /* stack_allocation and stack_base never change */ /* FIXME: should save floating point context if requested in fiber->flags */ if (!sigsetjmp( current_fiber->jmpbuf, 0 )) { 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; NtCurrentTeb()->FlsSlots = new_fiber->fls_slots; if (new_fiber->start) /* first time */ wine_switch_to_stack( start_fiber, new_fiber, new_fiber->stack_base ); else siglongjmp( new_fiber->jmpbuf, 1 ); } } /*********************************************************************** * FlsAlloc (KERNEL32.@) */ DWORD WINAPI FlsAlloc( PFLS_CALLBACK_FUNCTION callback ) { DWORD index; PEB * const peb = NtCurrentTeb()->Peb; RtlAcquirePebLock(); if (!peb->FlsCallback && !(peb->FlsCallback = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 8 * sizeof(peb->FlsBitmapBits) * sizeof(void*) ))) { SetLastError( ERROR_NOT_ENOUGH_MEMORY ); index = FLS_OUT_OF_INDEXES; } else { index = RtlFindClearBitsAndSet( peb->FlsBitmap, 1, 0 ); if (index != ~0U) { if (!NtCurrentTeb()->FlsSlots && !(NtCurrentTeb()->FlsSlots = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 8 * sizeof(peb->FlsBitmapBits) * sizeof(void*) ))) { RtlClearBits( peb->FlsBitmap, index, 1 ); index = FLS_OUT_OF_INDEXES; SetLastError( ERROR_NOT_ENOUGH_MEMORY ); } else { NtCurrentTeb()->FlsSlots[index] = 0; /* clear the value */ peb->FlsCallback[index] = callback; } } else SetLastError( ERROR_NO_MORE_ITEMS ); } RtlReleasePebLock(); return index; } /*********************************************************************** * FlsFree (KERNEL32.@) */ BOOL WINAPI FlsFree( DWORD index ) { BOOL ret; RtlAcquirePebLock(); ret = RtlAreBitsSet( NtCurrentTeb()->Peb->FlsBitmap, index, 1 ); if (ret) RtlClearBits( NtCurrentTeb()->Peb->FlsBitmap, index, 1 ); if (ret) { /* FIXME: call Fls callback */ /* FIXME: add equivalent of ThreadZeroTlsCell here */ if (NtCurrentTeb()->FlsSlots) NtCurrentTeb()->FlsSlots[index] = 0; } else SetLastError( ERROR_INVALID_PARAMETER ); RtlReleasePebLock(); return TRUE; } /*********************************************************************** * FlsGetValue (KERNEL32.@) */ PVOID WINAPI FlsGetValue( DWORD index ) { if (index >= 8 * sizeof(NtCurrentTeb()->Peb->FlsBitmapBits) || !NtCurrentTeb()->FlsSlots) { SetLastError( ERROR_INVALID_PARAMETER ); return NULL; } SetLastError( ERROR_SUCCESS ); return NtCurrentTeb()->FlsSlots[index]; } /*********************************************************************** * FlsSetValue (KERNEL32.@) */ BOOL WINAPI FlsSetValue( DWORD index, PVOID data ) { if (index >= 8 * sizeof(NtCurrentTeb()->Peb->FlsBitmapBits)) { SetLastError( ERROR_INVALID_PARAMETER ); return FALSE; } if (!NtCurrentTeb()->FlsSlots && !(NtCurrentTeb()->FlsSlots = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 8 * sizeof(NtCurrentTeb()->Peb->FlsBitmapBits) * sizeof(void*) ))) { SetLastError( ERROR_NOT_ENOUGH_MEMORY ); return FALSE; } NtCurrentTeb()->FlsSlots[index] = data; return TRUE; }