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