diff --git a/dlls/kernel/kernel32.spec b/dlls/kernel/kernel32.spec index bda898971c2..4abaf6e05d4 100644 --- a/dlls/kernel/kernel32.spec +++ b/dlls/kernel/kernel32.spec @@ -182,7 +182,9 @@ @ stdcall ConnectNamedPipe(long ptr) ConnectNamedPipe @ stdcall ContinueDebugEvent(long long long) ContinueDebugEvent @ stdcall ConvertDefaultLocale (long) ConvertDefaultLocale -@ stub ConvertThreadToFiber +@ stdcall ConvertFiberToThread() ConvertFiberToThread +@ stdcall ConvertThreadToFiber(ptr) ConvertThreadToFiber +@ stdcall ConvertThreadToFiberEx(ptr long) ConvertThreadToFiberEx @ stdcall ConvertToGlobalHandle(long) ConvertToGlobalHandle @ stdcall CopyFileA(str str long) CopyFileA @ stdcall CopyFileW(wstr wstr long) CopyFileW @@ -983,10 +985,11 @@ @ stdcall CancelWaitableTimer(long) CancelWaitableTimer @ stdcall CopyFileExA (str str ptr ptr ptr long) CopyFileExA @ stdcall CopyFileExW (wstr wstr ptr ptr ptr long) CopyFileExW -@ stub CreateFiber +@ stdcall CreateFiber(long ptr ptr) CreateFiber +@ stdcall CreateFiberEx(long long long ptr ptr) CreateFiberEx @ stdcall CreateWaitableTimerA(ptr long str) CreateWaitableTimerA @ stdcall CreateWaitableTimerW(ptr long wstr) CreateWaitableTimerW -@ stub DeleteFiber +@ stdcall DeleteFiber(ptr) DeleteFiber @ stub DuplicateConsoleHandle @ stdcall FindFirstFileExA(str long ptr long ptr long)FindFirstFileExA @ stdcall FindFirstFileExW(wstr long ptr long ptr long)FindFirstFileExW @@ -1018,7 +1021,7 @@ @ stdcall SetThreadPriorityBoost(long long) SetThreadPriorityBoost @ stdcall SetWaitableTimer(long ptr long ptr ptr long) SetWaitableTimer @ stub SignalObjectAndWait -@ stub SwitchToFiber +@ stdcall SwitchToFiber(ptr) SwitchToFiber @ stdcall SwitchToThread() SwitchToThread @ stdcall TryEnterCriticalSection(ptr) ntdll.RtlTryEnterCriticalSection @ stdcall VirtualAllocEx(long ptr long long long) VirtualAllocEx diff --git a/dlls/ntdll/Makefile.in b/dlls/ntdll/Makefile.in index 9fcd4c6e8dd..06fab0c0b2d 100644 --- a/dlls/ntdll/Makefile.in +++ b/dlls/ntdll/Makefile.in @@ -59,6 +59,7 @@ C_SRCS = \ $(TOPOBJDIR)/relay32/snoop.c \ $(TOPOBJDIR)/scheduler/client.c \ $(TOPOBJDIR)/scheduler/critsection.c \ + $(TOPOBJDIR)/scheduler/fiber.c \ $(TOPOBJDIR)/scheduler/handle.c \ $(TOPOBJDIR)/scheduler/pipe.c \ $(TOPOBJDIR)/scheduler/process.c \ diff --git a/include/thread.h b/include/thread.h index 9ee3a2331be..fa381fd1395 100644 --- a/include/thread.h +++ b/include/thread.h @@ -29,6 +29,7 @@ struct __EXCEPTION_FRAME; struct _SECURITY_ATTRIBUTES; struct tagSYSLEVEL; struct server_buffer_info; +struct fiber_data; /* Thread exception block @@ -52,7 +53,7 @@ typedef struct _TEB void *stack_low; /* 12- 08 Stack low-water mark */ HTASK16 htask16; /* 1-- 0c Win16 task handle */ WORD stack_sel; /* 1-- 0e 16-bit stack selector */ - DWORD selman_list; /* 1-n 10 Selector manager list */ + struct fiber_data *fiber; /* -2- 10 Current fiber data (Win95: selector manager list) */ DWORD user_ptr; /* 12n 14 User pointer */ /* end of NT_TIB */ struct _TEB *self; /* 12- 18 Pointer to this structure */ @@ -73,8 +74,8 @@ typedef struct _TEB void (*startup)(void); /* --3 48 Thread startup routine */ int thread_errno; /* --3 4c Per-thread errno (was: ring0_thread) */ int thread_h_errno; /* --3 50 Per-thread h_errno (was: ptr to tdbx structure) */ - void *stack_base; /* 1-- 54 Base of the stack */ - void *signal_stack; /* --3 58 Signal stack (was: exit_stack) */ + void *signal_stack; /* --3 54 Signal stack (was: stack_base) */ + void *exit_stack; /* 1-n 58 Exit stack */ void *emu_data; /* --n 5c Related to 80387 emulation */ DWORD last_error; /* 1-- 60 Last error code */ HANDLE debug_cb; /* 1-n 64 Debugger context block */ @@ -121,7 +122,7 @@ typedef struct _TEB DWORD pad6[624]; /* --n 238 */ UNICODE_STRING StaticUnicodeString; /* -2- bf8 used by advapi32 */ USHORT StaticUnicodeBuffer[261]; /* -2- c00 used by advapi32 */ - DWORD pad7; /* --n e0c */ + void *stack_base; /* -2- e0c Base of the stack */ LPVOID tls_array[64]; /* -2- e10 Thread local storage */ DWORD pad8[3]; /* --n f10 */ PVOID ReservedForNtRpc; /* -2- f1c used by rpcrt4 */ diff --git a/include/winbase.h b/include/winbase.h index eff0512c55c..196c1c29bb9 100644 --- a/include/winbase.h +++ b/include/winbase.h @@ -1178,9 +1178,14 @@ BOOL WINAPI CopyFileExW(LPCWSTR, LPCWSTR, LPPROGRESS_ROUTINE, LPVOID, LPB #define CopyFileEx WINELIB_NAME_AW(CopyFileEx) BOOL WINAPI CopySid(DWORD,PSID,PSID); INT WINAPI CompareFileTime(const FILETIME*,const FILETIME*); +BOOL WINAPI ConvertFiberToThread(void); +LPVOID WINAPI ConvertThreadToFiber(LPVOID); +LPVOID WINAPI ConvertThreadToFiberEx(LPVOID,DWORD); HANDLE WINAPI CreateEventA(LPSECURITY_ATTRIBUTES,BOOL,BOOL,LPCSTR); HANDLE WINAPI CreateEventW(LPSECURITY_ATTRIBUTES,BOOL,BOOL,LPCWSTR); #define CreateEvent WINELIB_NAME_AW(CreateEvent) +LPVOID WINAPI CreateFiber(SIZE_T,LPFIBER_START_ROUTINE,LPVOID); +LPVOID WINAPI CreateFiberEx(SIZE_T,SIZE_T,DWORD,LPFIBER_START_ROUTINE,LPVOID); HANDLE WINAPI CreateFileA(LPCSTR,DWORD,DWORD,LPSECURITY_ATTRIBUTES,DWORD,DWORD,HANDLE); HANDLE WINAPI CreateFileW(LPCWSTR,DWORD,DWORD,LPSECURITY_ATTRIBUTES,DWORD,DWORD,HANDLE); #define CreateFile WINELIB_NAME_AW(CreateFile) @@ -1217,6 +1222,7 @@ BOOL WINAPI DebugActiveProcessStop(DWORD); void WINAPI DebugBreak(void); BOOL WINAPI DebugBreakProcess(HANDLE); BOOL WINAPI DebugSetProcessKillOnExit(BOOL); +void WINAPI DeleteFiber(LPVOID); BOOL WINAPI DeleteTimerQueueEx(HANDLE,HANDLE); BOOL WINAPI DeleteTimerQueueTimer(HANDLE,HANDLE,HANDLE); BOOL WINAPI DeregisterEventSource(HANDLE); @@ -1501,6 +1507,7 @@ DWORD WINAPI SignalObjectAndWait(HANDLE,HANDLE,DWORD,BOOL); VOID WINAPI Sleep(DWORD); DWORD WINAPI SleepEx(DWORD,BOOL); DWORD WINAPI SuspendThread(HANDLE); +void WINAPI SwitchToFiber(LPVOID); BOOL WINAPI SwitchToThread(void); BOOL WINAPI SystemTimeToFileTime(const SYSTEMTIME*,LPFILETIME); DWORD WINAPI TlsAlloc(void); diff --git a/include/winnt.h b/include/winnt.h index 96301779580..5cd3b6ba752 100644 --- a/include/winnt.h +++ b/include/winnt.h @@ -1676,6 +1676,12 @@ extern inline struct _TEB * WINAPI NtCurrentTeb(void) extern struct _TEB * WINAPI NtCurrentTeb(void); #endif +#ifdef NONAMELESSUNION +#define GetCurrentFiber() (((NT_TIB *)NtCurrentTeb())->u.FiberData) +#else +#define GetCurrentFiber() (((NT_TIB *)NtCurrentTeb())->FiberData) +#endif +#define GetFiberData() (*(void **)GetCurrentFiber()) /* * File formats definitions diff --git a/scheduler/fiber.c b/scheduler/fiber.c new file mode 100644 index 00000000000..570aa41f87c --- /dev/null +++ b/scheduler/fiber.c @@ -0,0 +1,187 @@ +/* + * 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 "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 ); + } +}