Release 1.5.29.
[wine] / dlls / kernel32 / 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * FIXME:
21  * - proper handling of 16-bit stack and signal stack
22  */
23
24 /* Fortify source chokes on siglongjmp stack switching, so disable it */
25 #undef _FORTIFY_SOURCE
26 #define _FORTIFY_SOURCE 0
27
28 #include "config.h"
29 #include "wine/port.h"
30
31 #include <setjmp.h>
32 #include <stdarg.h>
33
34 #define NONAMELESSUNION
35 #include "windef.h"
36 #include "winbase.h"
37 #include "winerror.h"
38 #include "winternl.h"
39 #include "wine/exception.h"
40 #include "wine/library.h"
41
42 struct fiber_data
43 {
44     LPVOID                param;             /* 00 fiber param */
45     void                 *except;            /* 04 saved exception handlers list */
46     void                 *stack_base;        /* 08 top of fiber stack */
47     void                 *stack_limit;       /* 0c fiber stack low-water mark */
48     void                 *stack_allocation;  /* 10 base of the fiber stack allocation */
49     sigjmp_buf            jmpbuf;            /* 14 setjmp buffer (on Windows: CONTEXT) */
50     DWORD                 flags;             /*    fiber flags */
51     LPFIBER_START_ROUTINE start;             /*    start routine */
52     void                **fls_slots;         /*    fiber storage slots */
53 };
54
55
56 /* call the fiber initial function once we have switched stack */
57 static void start_fiber( void *arg )
58 {
59     struct fiber_data *fiber = arg;
60     LPFIBER_START_ROUTINE start = fiber->start;
61
62     __TRY
63     {
64         fiber->start = NULL;
65         start( fiber->param );
66         ExitThread( 1 );
67     }
68     __EXCEPT(UnhandledExceptionFilter)
69     {
70         TerminateThread( GetCurrentThread(), GetExceptionCode() );
71     }
72     __ENDTRY
73 }
74
75
76 /***********************************************************************
77  *           CreateFiber   (KERNEL32.@)
78  */
79 LPVOID WINAPI CreateFiber( SIZE_T stack, LPFIBER_START_ROUTINE start, LPVOID param )
80 {
81     return CreateFiberEx( stack, 0, 0, start, param );
82 }
83
84
85 /***********************************************************************
86  *           CreateFiberEx   (KERNEL32.@)
87  */
88 LPVOID WINAPI CreateFiberEx( SIZE_T stack_commit, SIZE_T stack_reserve, DWORD flags,
89                              LPFIBER_START_ROUTINE start, LPVOID param )
90 {
91     struct fiber_data *fiber;
92
93     if (!(fiber = HeapAlloc( GetProcessHeap(), 0, sizeof(*fiber) )))
94     {
95         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
96         return NULL;
97     }
98
99     /* FIXME: should use the thread stack allocation routines here */
100     if (!stack_reserve) stack_reserve = 1024*1024;
101     if(!(fiber->stack_allocation = VirtualAlloc( 0, stack_reserve, MEM_COMMIT, PAGE_READWRITE )))
102     {
103         HeapFree( GetProcessHeap(), 0, fiber );
104         return NULL;
105     }
106     fiber->stack_base  = (char *)fiber->stack_allocation + stack_reserve;
107     fiber->stack_limit = fiber->stack_allocation;
108     fiber->param       = param;
109     fiber->except      = (void *)-1;
110     fiber->start       = start;
111     fiber->flags       = flags;
112     fiber->fls_slots   = NULL;
113     return fiber;
114 }
115
116
117 /***********************************************************************
118  *           DeleteFiber   (KERNEL32.@)
119  */
120 void WINAPI DeleteFiber( LPVOID fiber_ptr )
121 {
122     struct fiber_data *fiber = fiber_ptr;
123
124     if (!fiber) return;
125     if (fiber == NtCurrentTeb()->Tib.u.FiberData)
126     {
127         HeapFree( GetProcessHeap(), 0, fiber );
128         ExitThread(1);
129     }
130     VirtualFree( fiber->stack_allocation, 0, MEM_RELEASE );
131     HeapFree( GetProcessHeap(), 0, fiber->fls_slots );
132     HeapFree( GetProcessHeap(), 0, fiber );
133 }
134
135
136 /***********************************************************************
137  *           ConvertThreadToFiber   (KERNEL32.@)
138  */
139 LPVOID WINAPI ConvertThreadToFiber( LPVOID param )
140 {
141     return ConvertThreadToFiberEx( param, 0 );
142 }
143
144
145 /***********************************************************************
146  *           ConvertThreadToFiberEx   (KERNEL32.@)
147  */
148 LPVOID WINAPI ConvertThreadToFiberEx( LPVOID param, DWORD flags )
149 {
150     struct fiber_data *fiber;
151
152     if (!(fiber = HeapAlloc( GetProcessHeap(), 0, sizeof(*fiber) )))
153     {
154         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
155         return NULL;
156     }
157     fiber->param            = param;
158     fiber->except           = NtCurrentTeb()->Tib.ExceptionList;
159     fiber->stack_base       = NtCurrentTeb()->Tib.StackBase;
160     fiber->stack_limit      = NtCurrentTeb()->Tib.StackLimit;
161     fiber->stack_allocation = NtCurrentTeb()->DeallocationStack;
162     fiber->start            = NULL;
163     fiber->flags            = flags;
164     fiber->fls_slots        = NtCurrentTeb()->FlsSlots;
165     NtCurrentTeb()->Tib.u.FiberData = fiber;
166     return fiber;
167 }
168
169
170 /***********************************************************************
171  *           ConvertFiberToThread   (KERNEL32.@)
172  */
173 BOOL WINAPI ConvertFiberToThread(void)
174 {
175     struct fiber_data *fiber = NtCurrentTeb()->Tib.u.FiberData;
176
177     if (fiber)
178     {
179         NtCurrentTeb()->Tib.u.FiberData = NULL;
180         HeapFree( GetProcessHeap(), 0, fiber );
181     }
182     return TRUE;
183 }
184
185
186 /***********************************************************************
187  *           SwitchToFiber   (KERNEL32.@)
188  */
189 void WINAPI SwitchToFiber( LPVOID fiber )
190 {
191     struct fiber_data *new_fiber = fiber;
192     struct fiber_data *current_fiber = NtCurrentTeb()->Tib.u.FiberData;
193
194     current_fiber->except      = NtCurrentTeb()->Tib.ExceptionList;
195     current_fiber->stack_limit = NtCurrentTeb()->Tib.StackLimit;
196     current_fiber->fls_slots   = NtCurrentTeb()->FlsSlots;
197     /* stack_allocation and stack_base never change */
198
199     /* FIXME: should save floating point context if requested in fiber->flags */
200     if (!sigsetjmp( current_fiber->jmpbuf, 0 ))
201     {
202         NtCurrentTeb()->Tib.u.FiberData   = new_fiber;
203         NtCurrentTeb()->Tib.ExceptionList = new_fiber->except;
204         NtCurrentTeb()->Tib.StackBase     = new_fiber->stack_base;
205         NtCurrentTeb()->Tib.StackLimit    = new_fiber->stack_limit;
206         NtCurrentTeb()->DeallocationStack = new_fiber->stack_allocation;
207         NtCurrentTeb()->FlsSlots          = new_fiber->fls_slots;
208         if (new_fiber->start)  /* first time */
209             wine_switch_to_stack( start_fiber, new_fiber, new_fiber->stack_base );
210         else
211             siglongjmp( new_fiber->jmpbuf, 1 );
212     }
213 }
214
215 /***********************************************************************
216  *           FlsAlloc   (KERNEL32.@)
217  */
218 DWORD WINAPI FlsAlloc( PFLS_CALLBACK_FUNCTION callback )
219 {
220     DWORD index;
221     PEB * const peb = NtCurrentTeb()->Peb;
222
223     RtlAcquirePebLock();
224     if (!peb->FlsCallback &&
225         !(peb->FlsCallback = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
226                                         8 * sizeof(peb->FlsBitmapBits) * sizeof(void*) )))
227     {
228         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
229         index = FLS_OUT_OF_INDEXES;
230     }
231     else
232     {
233         index = RtlFindClearBitsAndSet( peb->FlsBitmap, 1, 0 );
234         if (index != ~0U)
235         {
236             if (!NtCurrentTeb()->FlsSlots &&
237                 !(NtCurrentTeb()->FlsSlots = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
238                                                         8 * sizeof(peb->FlsBitmapBits) * sizeof(void*) )))
239             {
240                 RtlClearBits( peb->FlsBitmap, index, 1 );
241                 index = FLS_OUT_OF_INDEXES;
242                 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
243             }
244             else
245             {
246                 NtCurrentTeb()->FlsSlots[index] = 0; /* clear the value */
247                 peb->FlsCallback[index] = callback;
248             }
249         }
250         else SetLastError( ERROR_NO_MORE_ITEMS );
251     }
252     RtlReleasePebLock();
253     return index;
254 }
255
256 /***********************************************************************
257  *           FlsFree   (KERNEL32.@)
258  */
259 BOOL WINAPI FlsFree( DWORD index )
260 {
261     BOOL ret;
262
263     RtlAcquirePebLock();
264     ret = RtlAreBitsSet( NtCurrentTeb()->Peb->FlsBitmap, index, 1 );
265     if (ret) RtlClearBits( NtCurrentTeb()->Peb->FlsBitmap, index, 1 );
266     if (ret)
267     {
268         /* FIXME: call Fls callback */
269         /* FIXME: add equivalent of ThreadZeroTlsCell here */
270         if (NtCurrentTeb()->FlsSlots) NtCurrentTeb()->FlsSlots[index] = 0;
271     }
272     else SetLastError( ERROR_INVALID_PARAMETER );
273     RtlReleasePebLock();
274     return ret;
275 }
276
277 /***********************************************************************
278  *           FlsGetValue   (KERNEL32.@)
279  */
280 PVOID WINAPI FlsGetValue( DWORD index )
281 {
282     if (index >= 8 * sizeof(NtCurrentTeb()->Peb->FlsBitmapBits) || !NtCurrentTeb()->FlsSlots)
283     {
284         SetLastError( ERROR_INVALID_PARAMETER );
285         return NULL;
286     }
287     SetLastError( ERROR_SUCCESS );
288     return NtCurrentTeb()->FlsSlots[index];
289 }
290
291 /***********************************************************************
292  *           FlsSetValue   (KERNEL32.@)
293  */
294 BOOL WINAPI FlsSetValue( DWORD index, PVOID data )
295 {
296     if (index >= 8 * sizeof(NtCurrentTeb()->Peb->FlsBitmapBits))
297     {
298         SetLastError( ERROR_INVALID_PARAMETER );
299         return FALSE;
300     }
301     if (!NtCurrentTeb()->FlsSlots &&
302         !(NtCurrentTeb()->FlsSlots = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
303                                         8 * sizeof(NtCurrentTeb()->Peb->FlsBitmapBits) * sizeof(void*) )))
304     {
305         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
306         return FALSE;
307     }
308     NtCurrentTeb()->FlsSlots[index] = data;
309     return TRUE;
310 }
311
312 /***********************************************************************
313  *           IsThreadAFiber   (KERNEL32.@)
314  */
315 BOOL WINAPI IsThreadAFiber(void)
316 {
317     return NtCurrentTeb()->Tib.u.FiberData != NULL;
318 }