Release 970928
[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 #include "debug.h"
16 #include "stddebug.h"
17
18 THDB *pCurrentThread = NULL;
19 static K32OBJ_LIST THREAD_List;
20
21 /***********************************************************************
22  *           THREAD_GetPtr
23  *
24  * Return a pointer to a thread object. The object count must be decremented
25  * when no longer used.
26  */
27 static THDB *THREAD_GetPtr( HANDLE32 handle )
28 {
29     THDB *thread;
30
31     if (handle == 0xfffffffe)  /* Self-thread handle */
32     {
33         thread = THREAD_Current();
34         K32OBJ_IncCount( &thread->header );
35     }
36     else thread = (THDB *)PROCESS_GetObjPtr( handle, K32OBJ_THREAD );
37     return thread;
38 }
39
40
41 /***********************************************************************
42  *           THREAD_Current
43  *
44  * Return the current thread THDB pointer.
45  */
46 THDB *THREAD_Current(void)
47 {
48     /* FIXME: should probably use %fs register here */
49     assert( pCurrentThread );
50     return pCurrentThread;
51 }
52
53
54 /***********************************************************************
55  *           THREAD_Create
56  */
57 THDB *THREAD_Create( PDB32 *pdb, DWORD stack_size,
58                      LPTHREAD_START_ROUTINE start_addr )
59 {
60     DWORD old_prot;
61     THDB *thdb = HeapAlloc( SystemHeap, HEAP_ZERO_MEMORY, sizeof(THDB) );
62     if (!thdb) return NULL;
63     thdb->header.type     = K32OBJ_THREAD;
64     thdb->header.refcount = 1;
65     thdb->process         = pdb;
66     thdb->teb.except      = (void *)-1;
67     thdb->teb.htask16     = 0; /* FIXME */
68     thdb->teb.stack_sel   = 0; /* FIXME */
69     thdb->teb.self        = &thdb->teb;
70     thdb->teb.tls_ptr     = thdb->tls_array;
71     thdb->process2        = pdb;
72     thdb->exit_code       = 0x103; /* STILL_ACTIVE */
73
74     /* Allocate the stack */
75
76     if (!stack_size) stack_size = 1024 * 1024;  /* default size = 1Mb */
77     thdb->stack_base = VirtualAlloc( NULL, stack_size, MEM_COMMIT,
78                                      PAGE_EXECUTE_READWRITE );
79     if (!thdb->stack_base) goto error;
80     /* Set a guard page at the bottom of the stack */
81     VirtualProtect( thdb->stack_base, 1, PAGE_EXECUTE_READWRITE | PAGE_GUARD,
82                     &old_prot );
83     thdb->teb.stack_top   = (char *)thdb->stack_base + stack_size;
84     thdb->teb.stack_low   = thdb->stack_base;
85     thdb->exit_stack      = thdb->teb.stack_top;
86
87     /* Allocate the TEB selector (%fs register) */
88
89     thdb->teb_sel = SELECTOR_AllocBlock( &thdb->teb, 0x1000, SEGMENT_DATA,
90                                          TRUE, FALSE );
91     if (!thdb->teb_sel) goto error;
92
93     /* Initialize the thread context */
94
95     thdb->pcontext        = &thdb->context;
96     thdb->context.SegCs   = WINE_CODE_SELECTOR;
97     thdb->context.SegDs   = WINE_DATA_SELECTOR;
98     thdb->context.SegEs   = WINE_DATA_SELECTOR;
99     thdb->context.SegFs   = thdb->teb_sel;
100     thdb->context.SegGs   = WINE_DATA_SELECTOR;
101     thdb->context.SegSs   = WINE_DATA_SELECTOR;
102     thdb->context.Eip     = (DWORD)start_addr;
103     thdb->context.Esp     = (DWORD)thdb->teb.stack_top;
104
105     /* Add the thread to the linked list */
106
107     K32OBJ_AddTail( &THREAD_List, &thdb->header );
108
109     return thdb;
110
111 error:
112     if (thdb->teb_sel) SELECTOR_FreeBlock( thdb->teb_sel, 1 );
113     if (thdb->stack_base) VirtualFree( thdb->stack_base, 0, MEM_RELEASE );
114     HeapFree( SystemHeap, 0, thdb );
115     return NULL;
116 }
117
118
119 /***********************************************************************
120  *           THREAD_Destroy
121  */
122 void THREAD_Destroy( K32OBJ *ptr )
123 {
124     THDB *thdb = (THDB *)ptr;
125
126     assert( ptr->type == K32OBJ_THREAD );
127     ptr->type = K32OBJ_UNKNOWN;
128
129     /* Note: when we get here, the thread has already been removed */
130     /* from the thread list */
131
132     /* Free the associated memory */
133
134     SELECTOR_FreeBlock( thdb->teb_sel, 1 );
135     HeapFree( SystemHeap, 0, thdb );
136
137 }
138
139
140 /***********************************************************************
141  *           THREAD_SwitchThread
142  *
143  * Return the thread we want to switch to, and switch the contexts.
144  */
145 THDB *THREAD_SwitchThread( CONTEXT *context )
146 {
147     K32OBJ *cur;
148     THDB *next;
149     if (!pCurrentThread) return NULL;
150     cur = K32OBJ_RemoveHead( &THREAD_List );
151     K32OBJ_AddTail( &THREAD_List, cur );
152     K32OBJ_DecCount( cur );
153     next = (THDB *)THREAD_List.head;
154     if (next != pCurrentThread)
155     {
156         pCurrentThread->context = *context;
157         pCurrentThread = next;
158         *context = pCurrentThread->context;
159     }
160     return pCurrentThread;
161 }
162
163
164 /***********************************************************************
165  *           CreateThread   (KERNEL32.63)
166  *
167  * The only thing missing here is actually getting the thread to run ;-)
168  */
169 HANDLE32 WINAPI CreateThread( LPSECURITY_ATTRIBUTES attribs, DWORD stack,
170                               LPTHREAD_START_ROUTINE start, LPVOID param,
171                               DWORD flags, LPDWORD id )
172 {
173     HANDLE32 handle;
174     THDB *thread = THREAD_Create( pCurrentProcess, stack, start );
175     if (!thread) return INVALID_HANDLE_VALUE32;
176     handle = PROCESS_AllocHandle( &thread->header, 0 );
177     if (handle == INVALID_HANDLE_VALUE32)
178     {
179         THREAD_Destroy( &thread->header );
180         return INVALID_HANDLE_VALUE32;
181     }
182     *id = (DWORD)thread;
183     fprintf( stderr, "CreateThread: stub\n" );
184     return handle;
185 }
186
187
188 /***********************************************************************
189  *           GetCurrentThread   (KERNEL32.200)
190  */
191 HANDLE32 WINAPI GetCurrentThread(void)
192 {
193     return 0xFFFFFFFE;
194 }
195
196
197 /***********************************************************************
198  *           GetCurrentThreadId   (KERNEL32.201)
199  * Returns crypted (xor'ed) pointer to THDB in Win95.
200  */
201 DWORD WINAPI GetCurrentThreadId(void)
202 {
203     return (DWORD)THREAD_Current();
204 }
205
206
207 /**********************************************************************
208  *           GetLastError   (KERNEL.148) (KERNEL32.227)
209  */
210 DWORD WINAPI GetLastError(void)
211 {
212     THDB *thread = THREAD_Current();
213     return thread->last_error;
214 }
215
216
217 /**********************************************************************
218  *           SetLastError   (KERNEL.147) (KERNEL32.497)
219  */
220 void WINAPI SetLastError( DWORD error )
221 {
222     THDB *thread;
223     if (!pCurrentThread) return;  /* FIXME */
224     thread = THREAD_Current();
225     thread->last_error = error;
226 }
227
228
229 /**********************************************************************
230  *           SetLastErrorEx   (USER32.484)
231  */
232 void WINAPI SetLastErrorEx( DWORD error, DWORD type )
233 {
234     /* FIXME: what about 'type'? */
235     SetLastError( error );
236 }
237
238
239 /**********************************************************************
240  *           TlsAlloc   (KERNEL32.530)
241  */
242 DWORD WINAPI TlsAlloc(void)
243 {
244     DWORD i, mask, ret = 0;
245     THDB *thread = THREAD_Current();
246     DWORD *bits = thread->process->tls_bits;
247     EnterCriticalSection( &thread->process->crit_section );
248     if (*bits == 0xffffffff)
249     {
250         bits++;
251         ret = 32;
252         if (*bits == 0xffffffff)
253         {
254             LeaveCriticalSection( &thread->process->crit_section );
255             SetLastError( ERROR_NO_MORE_ITEMS );
256             return 0xffffffff;
257         }
258     }
259     for (i = 0, mask = 1; i < 32; i++, mask <<= 1) if (!(*bits & mask)) break;
260     *bits |= mask;
261     LeaveCriticalSection( &thread->process->crit_section );
262     return ret + i;
263 }
264
265
266 /**********************************************************************
267  *           TlsFree   (KERNEL32.531)
268  */
269 BOOL32 WINAPI TlsFree( DWORD index )
270 {
271     DWORD mask;
272     THDB *thread = THREAD_Current();
273     DWORD *bits = thread->process->tls_bits;
274     if (index >= 64)
275     {
276         SetLastError( ERROR_INVALID_PARAMETER );
277         return FALSE;
278     }
279     EnterCriticalSection( &thread->process->crit_section );
280     if (index >= 32) bits++;
281     mask = (1 << (index & 31));
282     if (!(*bits & mask))  /* already free? */
283     {
284         LeaveCriticalSection( &thread->process->crit_section );
285         SetLastError( ERROR_INVALID_PARAMETER );
286         return FALSE;
287     }
288     *bits &= ~mask;
289     thread->tls_array[index] = 0;
290     /* FIXME: should zero all other thread values */
291     LeaveCriticalSection( &thread->process->crit_section );
292     return TRUE;
293 }
294
295
296 /**********************************************************************
297  *           TlsGetValue   (KERNEL32.532)
298  */
299 LPVOID WINAPI TlsGetValue( DWORD index )
300 {
301     THDB *thread = THREAD_Current();
302     if (index >= 64)
303     {
304         SetLastError( ERROR_INVALID_PARAMETER );
305         return NULL;
306     }
307     SetLastError( ERROR_SUCCESS );
308     return thread->tls_array[index];
309 }
310
311
312 /**********************************************************************
313  *           TlsSetValue   (KERNEL32.533)
314  */
315 BOOL32 WINAPI TlsSetValue( DWORD index, LPVOID value )
316 {
317     THDB *thread = THREAD_Current();
318     if (index >= 64)
319     {
320         SetLastError( ERROR_INVALID_PARAMETER );
321         return FALSE;
322     }
323     thread->tls_array[index] = value;
324     return TRUE;
325 }
326
327
328 /***********************************************************************
329  *           GetThreadContext   (KERNEL32.294)
330  */
331 BOOL32 WINAPI GetThreadContext( HANDLE32 handle, CONTEXT *context )
332 {
333     THDB *thread = (THDB*)PROCESS_GetObjPtr( handle, K32OBJ_THREAD );
334     if (!thread) return FALSE;
335     *context = thread->context;
336     K32OBJ_DecCount( &thread->header );
337     return TRUE;
338 }
339
340
341 /**********************************************************************
342  *           NtCurrentTeb   (NTDLL.89)
343  */
344 void WINAPI NtCurrentTeb( CONTEXT *context )
345 {
346     EAX_reg(context) = GetSelectorBase( FS_reg(context) );
347 }
348
349
350 /**********************************************************************
351  *           GetThreadPriority   (KERNEL32.296)
352  */
353 INT32 WINAPI GetThreadPriority(HANDLE32 hthread)
354 {
355     THDB *thread;
356     INT32 ret;
357     
358     if (!(thread = THREAD_GetPtr( hthread ))) return 0;
359     ret = thread->delta_priority;
360     K32OBJ_DecCount( &thread->header );
361     return ret;
362 }
363
364
365 /**********************************************************************
366  *           SetThreadPriority   (KERNEL32.514)
367  */
368 BOOL32 WINAPI SetThreadPriority(HANDLE32 hthread,INT32 priority)
369 {
370     THDB *thread;
371     
372     if (!(thread = THREAD_GetPtr( hthread ))) return FALSE;
373     thread->delta_priority = priority;
374     K32OBJ_DecCount( &thread->header );
375     return TRUE;
376 }
377
378 /**********************************************************************
379  *           TerminateThread   (KERNEL32)
380  */
381 BOOL32 WINAPI TerminateThread(DWORD threadid,DWORD exitcode)
382 {
383     fprintf(stdnimp,"TerminateThread(0x%08lx,%ld), STUB!\n",threadid,exitcode);
384     return TRUE;
385 }
386
387 /**********************************************************************
388  *           GetExitCodeThread   (KERNEL32)
389  */
390 BOOL32 WINAPI GetExitCodeThread(HANDLE32 hthread,LPDWORD exitcode)
391 {
392     THDB *thread;
393     
394     if (!(thread = THREAD_GetPtr( hthread ))) return FALSE;
395     if (exitcode) *exitcode = thread->exit_code;
396     K32OBJ_DecCount( &thread->header );
397     return TRUE;
398 }
399
400 /**********************************************************************
401  *           ResumeThread   (KERNEL32)
402  */
403 BOOL32 WINAPI ResumeThread(DWORD threadid)
404 {
405     fprintf(stdnimp,"ResumeThread(0x%08lx), STUB!\n",threadid);
406     return TRUE;
407 }
408
409 /**********************************************************************
410  *           SuspendThread   (KERNEL32)
411  */
412 BOOL32 WINAPI SuspendThread(DWORD threadid)
413 {
414     fprintf(stdnimp,"SuspendThread(0x%08lx), STUB!\n",threadid);
415     return TRUE;
416 }