Link all remaining files that contain kernel APIs into kernel32.dll
[wine] / dlls / kernel / fiber.c
1 /*
2  * Fiber support
3  *
4  * Copyright 2002 Alexandre Julliard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  * FIXME:
21  * - proper handling of 16-bit stack and signal stack
22  */
23
24 #include <setjmp.h>
25 #include <stdarg.h>
26
27 #define NONAMELESSUNION
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winerror.h"
31 #include "wine/exception.h"
32 #include "wine/library.h"
33 #include "thread.h"
34
35 struct fiber_data
36 {
37     LPVOID                param;             /* 00 fiber param */
38     void                 *except;            /* 04 saved exception handlers list */
39     void                 *stack_base;        /* 08 top of fiber stack */
40     void                 *stack_limit;       /* 0c fiber stack low-water mark */
41     void                 *stack_allocation;  /* 10 base of the fiber stack allocation */
42     jmp_buf               jmpbuf;            /* 14 setjmp buffer (on Windows: CONTEXT) */
43     DWORD                 flags;             /*    fiber flags */
44     LPFIBER_START_ROUTINE start;             /*    start routine */
45 };
46
47
48 /* call the fiber initial function once we have switched stack */
49 static void start_fiber( void *arg )
50 {
51     struct fiber_data *fiber = arg;
52     LPFIBER_START_ROUTINE start = fiber->start;
53
54     __TRY
55     {
56         fiber->start = NULL;
57         start( fiber->param );
58         ExitThread( 1 );
59     }
60     __EXCEPT(UnhandledExceptionFilter)
61     {
62         TerminateThread( GetCurrentThread(), GetExceptionCode() );
63     }
64     __ENDTRY
65 }
66
67
68 /***********************************************************************
69  *           CreateFiber   (KERNEL32.@)
70  */
71 LPVOID WINAPI CreateFiber( SIZE_T stack, LPFIBER_START_ROUTINE start, LPVOID param )
72 {
73     return CreateFiberEx( stack, 0, 0, start, param );
74 }
75
76
77 /***********************************************************************
78  *           CreateFiberEx   (KERNEL32.@)
79  */
80 LPVOID WINAPI CreateFiberEx( SIZE_T stack_commit, SIZE_T stack_reserve, DWORD flags,
81                              LPFIBER_START_ROUTINE start, LPVOID param )
82 {
83     struct fiber_data *fiber;
84
85     if (!(fiber = HeapAlloc( GetProcessHeap(), 0, sizeof(*fiber) )))
86     {
87         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
88         return NULL;
89     }
90
91     /* FIXME: should use the thread stack allocation routines here */
92     if (!stack_reserve) stack_reserve = 1024*1024;
93     if(!(fiber->stack_allocation = VirtualAlloc( 0, stack_reserve, MEM_COMMIT, PAGE_EXECUTE_READWRITE )))
94     {
95         HeapFree( GetProcessHeap(), 0, fiber );
96         return NULL;
97     }
98     fiber->stack_base  = (char *)fiber->stack_allocation + stack_reserve;
99     fiber->stack_limit = fiber->stack_allocation;
100     fiber->param       = param;
101     fiber->except      = (void *)-1;
102     fiber->start       = start;
103     fiber->flags       = flags;
104     return fiber;
105 }
106
107
108 /***********************************************************************
109  *           DeleteFiber   (KERNEL32.@)
110  */
111 void WINAPI DeleteFiber( LPVOID fiber_ptr )
112 {
113     struct fiber_data *fiber = fiber_ptr;
114
115     if (!fiber) return;
116     if (fiber == NtCurrentTeb()->Tib.u.FiberData)
117     {
118         HeapFree( GetProcessHeap(), 0, fiber );
119         ExitThread(1);
120     }
121     VirtualFree( fiber->stack_allocation, 0, MEM_RELEASE );
122     HeapFree( GetProcessHeap(), 0, fiber );
123 }
124
125
126 /***********************************************************************
127  *           ConvertThreadToFiber   (KERNEL32.@)
128  */
129 LPVOID WINAPI ConvertThreadToFiber( LPVOID param )
130 {
131     return ConvertThreadToFiberEx( param, 0 );
132 }
133
134
135 /***********************************************************************
136  *           ConvertThreadToFiberEx   (KERNEL32.@)
137  */
138 LPVOID WINAPI ConvertThreadToFiberEx( LPVOID param, DWORD flags )
139 {
140     struct fiber_data *fiber;
141
142     if (!(fiber = HeapAlloc( GetProcessHeap(), 0, sizeof(*fiber) )))
143     {
144         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
145         return NULL;
146     }
147     fiber->param            = param;
148     fiber->except           = NtCurrentTeb()->Tib.ExceptionList;
149     fiber->stack_base       = NtCurrentTeb()->Tib.StackBase;
150     fiber->stack_limit      = NtCurrentTeb()->Tib.StackLimit;
151     fiber->stack_allocation = NtCurrentTeb()->DeallocationStack;
152     fiber->start            = NULL;
153     fiber->flags            = flags;
154     NtCurrentTeb()->Tib.u.FiberData = fiber;
155     return fiber;
156 }
157
158
159 /***********************************************************************
160  *           ConvertFiberToThread   (KERNEL32.@)
161  */
162 BOOL WINAPI ConvertFiberToThread(void)
163 {
164     struct fiber_data *fiber = NtCurrentTeb()->Tib.u.FiberData;
165
166     if (fiber)
167     {
168         NtCurrentTeb()->Tib.u.FiberData = NULL;
169         HeapFree( GetProcessHeap(), 0, fiber );
170     }
171     return TRUE;
172 }
173
174
175 /***********************************************************************
176  *           SwitchToFiber   (KERNEL32.@)
177  */
178 void WINAPI SwitchToFiber( LPVOID fiber )
179 {
180     struct fiber_data *new_fiber = fiber;
181     struct fiber_data *current_fiber = NtCurrentTeb()->Tib.u.FiberData;
182
183     current_fiber->except      = NtCurrentTeb()->Tib.ExceptionList;
184     current_fiber->stack_limit = NtCurrentTeb()->Tib.StackLimit;
185     /* stack_allocation and stack_base never change */
186
187     /* FIXME: should save floating point context if requested in fiber->flags */
188     if (!setjmp( current_fiber->jmpbuf ))
189     {
190         NtCurrentTeb()->Tib.u.FiberData   = new_fiber;
191         NtCurrentTeb()->Tib.ExceptionList = new_fiber->except;
192         NtCurrentTeb()->Tib.StackBase     = new_fiber->stack_base;
193         NtCurrentTeb()->Tib.StackLimit    = new_fiber->stack_limit;
194         NtCurrentTeb()->DeallocationStack = new_fiber->stack_allocation;
195         if (new_fiber->start)  /* first time */
196             wine_switch_to_stack( start_fiber, new_fiber, new_fiber->stack_base );
197         else
198             longjmp( new_fiber->jmpbuf, 1 );
199     }
200 }