Release 970804
[wine] / scheduler / thread.c
1 /*
2  * Win32 threads
3  *
4  * Copyright 1996 Alexandre Julliard
5  */
6
7 #include <assert.h>
8 #include <stdio.h>
9 #include "thread.h"
10 #include "winerror.h"
11 #include "heap.h"
12 #include "selectors.h"
13 #include "miscemu.h"
14 #include "winnt.h"
15
16 THDB *pCurrentThread = NULL;
17
18 /***********************************************************************
19  *           THREAD_GetPtr
20  *
21  * Return a pointer to a thread object. The object count must be decremented
22  * when no longer used.
23  */
24 static THDB *THREAD_GetPtr( HANDLE32 handle )
25 {
26     THDB *thread;
27
28     if (handle == 0xfffffffe)  /* Self-thread handle */
29     {
30         thread = (THDB *)GetCurrentThreadId();
31         K32OBJ_IncCount( &thread->header );
32     }
33     else thread = (THDB *)PROCESS_GetObjPtr( handle, K32OBJ_THREAD );
34     return thread;
35 }
36
37
38 /***********************************************************************
39  *           THREAD_Create
40  */
41 THDB *THREAD_Create( PDB32 *pdb, DWORD stack_size,
42                      LPTHREAD_START_ROUTINE start_addr )
43 {
44     DWORD old_prot;
45     THDB *thdb = HeapAlloc( SystemHeap, HEAP_ZERO_MEMORY, sizeof(THDB) );
46     if (!thdb) return NULL;
47     thdb->header.type     = K32OBJ_THREAD;
48     thdb->header.refcount = 1;
49     thdb->process         = pdb;
50     thdb->teb.except      = (void *)-1;
51     thdb->teb.htask16     = 0; /* FIXME */
52     thdb->teb.stack_sel   = 0; /* FIXME */
53     thdb->teb.self        = &thdb->teb;
54     thdb->teb.tls_ptr     = thdb->tls_array;
55     thdb->process2        = pdb;
56     thdb->exit_code       = 0x103; /* STILL_ACTIVE */
57
58     /* Allocate the stack */
59
60     if (!stack_size) stack_size = 1024 * 1024;  /* default size = 1Mb */
61     thdb->stack_base = VirtualAlloc( NULL, stack_size, MEM_COMMIT,
62                                      PAGE_EXECUTE_READWRITE );
63     if (!thdb->stack_base) goto error;
64     /* Set a guard page at the bottom of the stack */
65     VirtualProtect( thdb->stack_base, 1, PAGE_EXECUTE_READWRITE | PAGE_GUARD,
66                     &old_prot );
67     thdb->teb.stack_top   = (char *)thdb->stack_base + stack_size;
68     thdb->teb.stack_low   = thdb->stack_base;
69     thdb->exit_stack      = thdb->teb.stack_top;
70
71     /* Allocate the TEB selector (%fs register) */
72
73     thdb->teb_sel = SELECTOR_AllocBlock( &thdb->teb, 0x1000, SEGMENT_DATA,
74                                          TRUE, FALSE );
75     if (!thdb->teb_sel) goto error;
76
77     /* Initialize the thread context */
78
79     thdb->pcontext        = &thdb->context;
80     thdb->context.SegCs   = WINE_CODE_SELECTOR;
81     thdb->context.SegDs   = WINE_DATA_SELECTOR;
82     thdb->context.SegEs   = WINE_DATA_SELECTOR;
83     thdb->context.SegFs   = thdb->teb_sel;
84     thdb->context.SegGs   = WINE_DATA_SELECTOR;
85     thdb->context.SegSs   = WINE_DATA_SELECTOR;
86     thdb->context.Eip     = (DWORD)start_addr;
87     thdb->context.Esp     = (DWORD)thdb->teb.stack_top;
88
89     return thdb;
90
91 error:
92     if (thdb->teb_sel) SELECTOR_FreeBlock( thdb->teb_sel, 1 );
93     if (thdb->stack_base) VirtualFree( thdb->stack_base, 0, MEM_RELEASE );
94     HeapFree( SystemHeap, 0, thdb );
95     return NULL;
96 }
97
98
99 /***********************************************************************
100  *           THREAD_Destroy
101  */
102 void THREAD_Destroy( K32OBJ *ptr )
103 {
104     THDB *thdb = (THDB *)ptr;
105     assert( ptr->type == K32OBJ_THREAD );
106     ptr->type = K32OBJ_UNKNOWN;
107     SELECTOR_FreeBlock( thdb->teb_sel, 1 );
108     HeapFree( SystemHeap, 0, thdb );
109 }
110
111
112 /***********************************************************************
113  *           CreateThread   (KERNEL32.63)
114  *
115  * The only thing missing here is actually getting the thread to run ;-)
116  */
117 HANDLE32 CreateThread( LPSECURITY_ATTRIBUTES attribs, DWORD stack,
118                        LPTHREAD_START_ROUTINE start, LPVOID param,
119                        DWORD flags, LPDWORD id )
120 {
121     HANDLE32 handle;
122     THDB *thread = THREAD_Create( pCurrentProcess, stack, start );
123     if (!thread) return INVALID_HANDLE_VALUE32;
124     handle = PROCESS_AllocHandle( &thread->header, 0 );
125     if (handle == INVALID_HANDLE_VALUE32)
126     {
127         THREAD_Destroy( &thread->header );
128         return INVALID_HANDLE_VALUE32;
129     }
130     *id = (DWORD)thread;
131     fprintf( stderr, "CreateThread: stub\n" );
132     return handle;
133 }
134
135
136 /***********************************************************************
137  *           GetCurrentThread   (KERNEL32.200)
138  */
139 HANDLE32 GetCurrentThread(void)
140 {
141     return 0xFFFFFFFE;
142 }
143
144
145 /***********************************************************************
146  *           GetCurrentThreadId   (KERNEL32.201)
147  * Returns crypted (xor'ed) pointer to THDB in Win95.
148  */
149 DWORD GetCurrentThreadId(void)
150 {
151     /* FIXME: should probably use %fs register here */
152     assert( pCurrentThread );
153     return (DWORD)pCurrentThread;
154 }
155
156
157 /**********************************************************************
158  *           GetLastError   (KERNEL.148) (KERNEL32.227)
159  */
160 DWORD GetLastError(void)
161 {
162     THDB *thread = (THDB *)GetCurrentThreadId();
163     return thread->last_error;
164 }
165
166
167 /**********************************************************************
168  *           SetLastError   (KERNEL.147) (KERNEL32.497)
169  */
170 void SetLastError( DWORD error )
171 {
172     THDB *thread;
173     if (!pCurrentThread) return;  /* FIXME */
174     thread = (THDB *)GetCurrentThreadId();
175     thread->last_error = error;
176 }
177
178
179 /**********************************************************************
180  *           SetLastErrorEx   (USER32.484)
181  */
182 void SetLastErrorEx( DWORD error, DWORD type )
183 {
184     /* FIXME: what about 'type'? */
185     SetLastError( error );
186 }
187
188
189 /**********************************************************************
190  *           TlsAlloc   (KERNEL32.530)
191  */
192 DWORD TlsAlloc(void)
193 {
194     DWORD i, mask, ret = 0;
195     THDB *thread = (THDB *)GetCurrentThreadId();
196     DWORD *bits = thread->process->tls_bits;
197     EnterCriticalSection( &thread->process->crit_section );
198     if (*bits == 0xffffffff)
199     {
200         bits++;
201         ret = 32;
202         if (*bits == 0xffffffff)
203         {
204             LeaveCriticalSection( &thread->process->crit_section );
205             SetLastError( ERROR_NO_MORE_ITEMS );
206             return 0xffffffff;
207         }
208     }
209     for (i = 0, mask = 1; i < 32; i++, mask <<= 1) if (!(*bits & mask)) break;
210     *bits |= mask;
211     LeaveCriticalSection( &thread->process->crit_section );
212     return ret + i;
213 }
214
215
216 /**********************************************************************
217  *           TlsFree   (KERNEL32.531)
218  */
219 BOOL32 TlsFree( DWORD index )
220 {
221     DWORD mask;
222     THDB *thread = (THDB *)GetCurrentThreadId();
223     DWORD *bits = thread->process->tls_bits;
224     if (index >= 64)
225     {
226         SetLastError( ERROR_INVALID_PARAMETER );
227         return FALSE;
228     }
229     EnterCriticalSection( &thread->process->crit_section );
230     if (index >= 32) bits++;
231     mask = (1 << (index & 31));
232     if (!(*bits & mask))  /* already free? */
233     {
234         LeaveCriticalSection( &thread->process->crit_section );
235         SetLastError( ERROR_INVALID_PARAMETER );
236         return FALSE;
237     }
238     *bits &= ~mask;
239     thread->tls_array[index] = 0;
240     /* FIXME: should zero all other thread values */
241     LeaveCriticalSection( &thread->process->crit_section );
242     return TRUE;
243 }
244
245
246 /**********************************************************************
247  *           TlsGetValue   (KERNEL32.532)
248  */
249 LPVOID TlsGetValue( DWORD index )
250 {
251     THDB *thread = (THDB *)GetCurrentThreadId();
252     if (index >= 64)
253     {
254         SetLastError( ERROR_INVALID_PARAMETER );
255         return NULL;
256     }
257     SetLastError( ERROR_SUCCESS );
258     return thread->tls_array[index];
259 }
260
261
262 /**********************************************************************
263  *           TlsSetValue   (KERNEL32.533)
264  */
265 BOOL32 TlsSetValue( DWORD index, LPVOID value )
266 {
267     THDB *thread = (THDB *)GetCurrentThreadId();
268     if (index >= 64)
269     {
270         SetLastError( ERROR_INVALID_PARAMETER );
271         return FALSE;
272     }
273     thread->tls_array[index] = value;
274     return TRUE;
275 }
276
277
278 /***********************************************************************
279  *           GetThreadContext   (KERNEL32.294)
280  */
281 BOOL32 GetThreadContext( HANDLE32 handle, CONTEXT *context )
282 {
283     THDB *thread = (THDB*)PROCESS_GetObjPtr( handle, K32OBJ_THREAD );
284     if (!thread) return FALSE;
285     *context = thread->context;
286     K32OBJ_DecCount( &thread->header );
287     return TRUE;
288 }
289
290
291 /**********************************************************************
292  *           NtCurrentTeb   (NTDLL.89)
293  */
294 void NtCurrentTeb( CONTEXT *context )
295 {
296     EAX_reg(context) = GetSelectorBase( FS_reg(context) );
297 }
298
299
300 /**********************************************************************
301  *           GetThreadPriority   (KERNEL32.296)
302  */
303 INT32 GetThreadPriority(HANDLE32 hthread)
304 {
305     THDB *thread;
306     INT32 ret;
307     
308     if (!(thread = THREAD_GetPtr( hthread ))) return 0;
309     ret = thread->delta_priority;
310     K32OBJ_DecCount( &thread->header );
311     return ret;
312 }
313
314
315 /**********************************************************************
316  *           SetThreadPriority   (KERNEL32.514)
317  */
318 BOOL32 SetThreadPriority(HANDLE32 hthread,INT32 priority)
319 {
320     THDB *thread;
321     
322     if (!(thread = THREAD_GetPtr( hthread ))) return FALSE;
323     thread->delta_priority = priority;
324     K32OBJ_DecCount( &thread->header );
325     return TRUE;
326 }