Release 980104
[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 <signal.h>
10 #include <unistd.h>
11 #include "thread.h"
12 #include "process.h"
13 #include "winerror.h"
14 #include "heap.h"
15 #include "selectors.h"
16 #include "miscemu.h"
17 #include "winnt.h"
18 #include "debug.h"
19 #include "stddebug.h"
20
21 #ifdef HAVE_CLONE
22 # ifdef HAVE_SCHED_H
23 #  include <sched.h>
24 # endif
25 # ifndef CLONE_VM
26 #  define CLONE_VM      0x00000100
27 #  define CLONE_FS      0x00000200
28 #  define CLONE_FILES   0x00000400
29 #  define CLONE_SIGHAND 0x00000800
30 #  define CLONE_PID     0x00001000
31 /* If we didn't get the flags, we probably didn't get the prototype either */
32 extern int clone( int (*fn)(void *arg), void *stack, int flags, void *arg );
33 # endif  /* CLONE_VM */
34 #endif  /* HAVE_CLONE */
35
36 #ifndef __i386__
37 THDB *pCurrentThread;
38 #endif
39
40 static BOOL32 THREAD_Signaled( K32OBJ *obj, DWORD thread_id );
41 static void THREAD_Satisfied( K32OBJ *obj, DWORD thread_id );
42 static void THREAD_AddWait( K32OBJ *obj, DWORD thread_id );
43 static void THREAD_RemoveWait( K32OBJ *obj, DWORD thread_id );
44 static void THREAD_Destroy( K32OBJ *obj );
45
46 const K32OBJ_OPS THREAD_Ops =
47 {
48     THREAD_Signaled,    /* signaled */
49     THREAD_Satisfied,   /* satisfied */
50     THREAD_AddWait,     /* add_wait */
51     THREAD_RemoveWait,  /* remove_wait */
52     THREAD_Destroy      /* destroy */
53 };
54
55
56 /***********************************************************************
57  *           THREAD_GetPtr
58  *
59  * Return a pointer to a thread object. The object count must be decremented
60  * when no longer used.
61  */
62 static THDB *THREAD_GetPtr( HANDLE32 handle )
63 {
64     THDB *thread;
65
66     if (handle == 0xfffffffe)  /* Self-thread handle */
67     {
68         thread = THREAD_Current();
69         K32OBJ_IncCount( &thread->header );
70     }
71     else thread = (THDB *)PROCESS_GetObjPtr( handle, K32OBJ_THREAD );
72     return thread;
73 }
74
75
76 /**********************************************************************
77  *           NtCurrentTeb   (NTDLL.89)
78  */
79 TEB * WINAPI NtCurrentTeb(void)
80 {
81 #ifdef __i386__
82     TEB *teb;
83     WORD ds, fs;
84
85     /* Check if we have a current thread */
86     GET_DS( ds );
87     GET_FS( fs );
88     if (fs == ds) return NULL; /* FIXME: should be an assert */
89     __asm__( "movl %%fs:(24),%0" : "=r" (teb) );
90     return teb;
91 #else
92     if (!pCurrentThread) return NULL;
93     return &pCurrentThread->teb;
94 #endif  /* __i386__ */
95 }
96
97
98 /***********************************************************************
99  *           THREAD_Current
100  *
101  * Return the current thread THDB pointer.
102  */
103 THDB *THREAD_Current(void)
104 {
105     TEB *teb = NtCurrentTeb();
106     if (!teb) return NULL;
107     return (THDB *)((char *)teb - (int)&((THDB *)0)->teb);
108 }
109
110
111 /***********************************************************************
112  *           THREAD_AddQueue
113  *
114  * Add a thread to a queue.
115  */
116 void THREAD_AddQueue( THREAD_QUEUE *queue, THDB *thread )
117 {
118     THREAD_ENTRY *entry = HeapAlloc( SystemHeap, HEAP_NO_SERIALIZE,
119                                      sizeof(*entry) );
120     assert(entry);
121     SYSTEM_LOCK();
122     entry->thread = thread;
123     if (*queue)
124     {
125         entry->next = (*queue)->next;
126         (*queue)->next = entry;
127     }
128     else entry->next = entry;
129     *queue = entry;
130     SYSTEM_UNLOCK();
131 }
132
133 /***********************************************************************
134  *           THREAD_RemoveQueue
135  *
136  * Remove a thread from a queue.
137  */
138 void THREAD_RemoveQueue( THREAD_QUEUE *queue, THDB *thread )
139 {
140     THREAD_ENTRY *entry = *queue;
141     SYSTEM_LOCK();
142     if (entry->next == entry)  /* Only one element in the queue */
143     {
144         assert( entry->thread == thread );
145         *queue = NULL;
146     }
147     else
148     {
149         THREAD_ENTRY *next;
150         while (entry->next->thread != thread)
151         {
152             entry = entry->next;
153             assert( entry != *queue );  /* Have we come all the way around? */
154         }
155         if ((next = entry->next) == *queue) *queue = entry;
156         entry->next = entry->next->next;
157         entry = next;  /* This is the one we want to free */
158     }
159     HeapFree( SystemHeap, 0, entry );
160     SYSTEM_UNLOCK();
161 }
162
163
164 /***********************************************************************
165  *           THREAD_Create
166  */
167 THDB *THREAD_Create( PDB32 *pdb, DWORD stack_size,
168                      LPTHREAD_START_ROUTINE start_addr, LPVOID param )
169 {
170     DWORD old_prot;
171     WORD cs, ds;
172
173     THDB *thdb = HeapAlloc( SystemHeap, HEAP_ZERO_MEMORY, sizeof(THDB) );
174     if (!thdb) return NULL;
175     thdb->header.type     = K32OBJ_THREAD;
176     thdb->header.refcount = 1;
177     thdb->process         = pdb;
178     thdb->teb.except      = (void *)-1;
179     thdb->teb.htask16     = 0; /* FIXME */
180     thdb->teb.stack_sel   = 0; /* FIXME */
181     thdb->teb.self        = &thdb->teb;
182     thdb->teb.tls_ptr     = thdb->tls_array;
183     thdb->wait_list       = &thdb->wait_struct;
184     thdb->process2        = pdb;
185     thdb->exit_code       = 0x103; /* STILL_ACTIVE */
186     thdb->entry_point     = start_addr;
187     thdb->entry_arg       = param;
188
189     /* Allocate the stack */
190
191     if (!stack_size) stack_size = 1024 * 1024;  /* default size = 1Mb */
192     thdb->stack_base = VirtualAlloc( NULL, stack_size, MEM_COMMIT,
193                                      PAGE_EXECUTE_READWRITE );
194     if (!thdb->stack_base) goto error;
195     /* Set a guard page at the bottom of the stack */
196     VirtualProtect( thdb->stack_base, 1, PAGE_EXECUTE_READWRITE | PAGE_GUARD,
197                     &old_prot );
198     thdb->teb.stack_top   = (char *)thdb->stack_base + stack_size;
199     thdb->teb.stack_low   = thdb->stack_base;
200     thdb->exit_stack      = thdb->teb.stack_top;
201
202     /* Allocate the TEB selector (%fs register) */
203
204     thdb->teb_sel = SELECTOR_AllocBlock( &thdb->teb, 0x1000, SEGMENT_DATA,
205                                          TRUE, FALSE );
206     if (!thdb->teb_sel) goto error;
207
208     /* Allocate the event */
209
210     if (!(thdb->event = EVENT_Create( TRUE, FALSE ))) goto error;
211
212     /* Initialize the thread context */
213
214     GET_CS(cs);
215     GET_DS(ds);
216     thdb->pcontext        = &thdb->context;
217     thdb->context.SegCs   = cs;
218     thdb->context.SegDs   = ds;
219     thdb->context.SegEs   = ds;
220     thdb->context.SegGs   = ds;
221     thdb->context.SegSs   = ds;
222     thdb->context.SegFs   = thdb->teb_sel;
223     thdb->context.Eip     = (DWORD)start_addr;
224     thdb->context.Esp     = (DWORD)thdb->teb.stack_top;
225     return thdb;
226
227 error:
228     if (thdb->event) K32OBJ_DecCount( thdb->event );
229     if (thdb->teb_sel) SELECTOR_FreeBlock( thdb->teb_sel, 1 );
230     if (thdb->stack_base) VirtualFree( thdb->stack_base, 0, MEM_RELEASE );
231     HeapFree( SystemHeap, 0, thdb );
232     return NULL;
233 }
234
235
236 /***********************************************************************
237  *           THREAD_Start
238  *
239  * Startup routine for a new thread.
240  */
241 static void THREAD_Start( THDB *thdb )
242 {
243     LPTHREAD_START_ROUTINE func = (LPTHREAD_START_ROUTINE)thdb->entry_point;
244     thdb->unix_pid = getpid();
245     SET_FS( thdb->teb_sel );
246     ExitThread( func( thdb->entry_arg ) );
247 }
248
249
250 /***********************************************************************
251  *           THREAD_Signaled
252  */
253 static BOOL32 THREAD_Signaled( K32OBJ *obj, DWORD thread_id )
254 {
255     THDB *thdb = (THDB *)obj;
256     assert( obj->type == K32OBJ_THREAD );
257     return K32OBJ_OPS( thdb->event )->signaled( thdb->event, thread_id );
258 }
259
260
261 /***********************************************************************
262  *           THREAD_Satisfied
263  *
264  * Wait on this object has been satisfied.
265  */
266 static void THREAD_Satisfied( K32OBJ *obj, DWORD thread_id )
267 {
268     THDB *thdb = (THDB *)obj;
269     assert( obj->type == K32OBJ_THREAD );
270     return K32OBJ_OPS( thdb->event )->satisfied( thdb->event, thread_id );
271 }
272
273
274 /***********************************************************************
275  *           THREAD_AddWait
276  *
277  * Add thread to object wait queue.
278  */
279 static void THREAD_AddWait( K32OBJ *obj, DWORD thread_id )
280 {
281     THDB *thdb = (THDB *)obj;
282     assert( obj->type == K32OBJ_THREAD );
283     return K32OBJ_OPS( thdb->event )->add_wait( thdb->event, thread_id );
284 }
285
286
287 /***********************************************************************
288  *           THREAD_RemoveWait
289  *
290  * Remove thread from object wait queue.
291  */
292 static void THREAD_RemoveWait( K32OBJ *obj, DWORD thread_id )
293 {
294     THDB *thdb = (THDB *)obj;
295     assert( obj->type == K32OBJ_THREAD );
296     return K32OBJ_OPS( thdb->event )->remove_wait( thdb->event, thread_id );
297 }
298
299
300 /***********************************************************************
301  *           THREAD_Destroy
302  */
303 static void THREAD_Destroy( K32OBJ *ptr )
304 {
305     THDB *thdb = (THDB *)ptr;
306
307     assert( ptr->type == K32OBJ_THREAD );
308     ptr->type = K32OBJ_UNKNOWN;
309
310     /* Free the associated memory */
311
312 #ifdef __i386__
313     {
314         /* Check if we are deleting the current thread */
315         WORD fs;
316         GET_FS( fs );
317         if (fs == thdb->teb_sel)
318         {
319             GET_DS( fs );
320             SET_FS( fs );
321         }
322     }
323 #endif
324     K32OBJ_DecCount( thdb->event );
325     SELECTOR_FreeBlock( thdb->teb_sel, 1 );
326     HeapFree( SystemHeap, 0, thdb );
327
328 }
329
330
331 /***********************************************************************
332  *           CreateThread   (KERNEL32.63)
333  */
334 HANDLE32 WINAPI CreateThread( LPSECURITY_ATTRIBUTES attribs, DWORD stack,
335                               LPTHREAD_START_ROUTINE start, LPVOID param,
336                               DWORD flags, LPDWORD id )
337 {
338     HANDLE32 handle;
339     THDB *thread = THREAD_Create( pCurrentProcess, stack, start, param );
340     if (!thread) return INVALID_HANDLE_VALUE32;
341     handle = PROCESS_AllocHandle( &thread->header, 0 );
342     if (handle == INVALID_HANDLE_VALUE32)
343     {
344         K32OBJ_DecCount( &thread->header );
345         return INVALID_HANDLE_VALUE32;
346     }
347 #ifdef HAVE_CLONE
348     if (clone( (int (*)(void *))THREAD_Start, thread->teb.stack_top,
349                CLONE_VM | CLONE_FS | CLONE_FILES | SIGCHLD, thread ) < 0)
350     {
351         K32OBJ_DecCount( &thread->header );
352         return INVALID_HANDLE_VALUE32;
353     }
354 #else
355     fprintf( stderr, "CreateThread: stub\n" );
356 #endif  /* __linux__ */
357     *id = THDB_TO_THREAD_ID( thread );
358     return handle;
359 }
360
361
362 /***********************************************************************
363  *           ExitThread   (KERNEL32.215)
364  */
365 void WINAPI ExitThread( DWORD code )
366 {
367     THDB *thdb = THREAD_Current();
368     LONG count;
369
370     SYSTEM_LOCK();
371     thdb->exit_code = code;
372     EVENT_Set( thdb->event );
373     /* FIXME: should free the stack somehow */
374     K32OBJ_DecCount( &thdb->header );
375     /* Completely unlock the system lock just in case */
376     count = SYSTEM_LOCK_COUNT();
377     while (count--) SYSTEM_UNLOCK();
378     _exit( 0 );
379 }
380
381
382 /***********************************************************************
383  *           GetCurrentThread   (KERNEL32.200)
384  */
385 HANDLE32 WINAPI GetCurrentThread(void)
386 {
387     return 0xFFFFFFFE;
388 }
389
390
391 /***********************************************************************
392  *           GetCurrentThreadId   (KERNEL32.201)
393  */
394 DWORD WINAPI GetCurrentThreadId(void)
395 {
396     return THDB_TO_THREAD_ID( THREAD_Current() );
397 }
398
399
400 /**********************************************************************
401  *           GetLastError   (KERNEL.148) (KERNEL32.227)
402  */
403 DWORD WINAPI GetLastError(void)
404 {
405     THDB *thread = THREAD_Current();
406     return thread->last_error;
407 }
408
409
410 /**********************************************************************
411  *           SetLastError   (KERNEL.147) (KERNEL32.497)
412  */
413 void WINAPI SetLastError( DWORD error )
414 {
415     THDB *thread = THREAD_Current();
416     /* This one must work before we have a thread (FIXME) */
417     if (thread) thread->last_error = error;
418 }
419
420
421 /**********************************************************************
422  *           SetLastErrorEx   (USER32.484)
423  */
424 void WINAPI SetLastErrorEx( DWORD error, DWORD type )
425 {
426     /* FIXME: what about 'type'? */
427     SetLastError( error );
428 }
429
430
431 /**********************************************************************
432  *           TlsAlloc   (KERNEL32.530)
433  */
434 DWORD WINAPI TlsAlloc(void)
435 {
436     DWORD i, mask, ret = 0;
437     THDB *thread = THREAD_Current();
438     DWORD *bits = thread->process->tls_bits;
439     EnterCriticalSection( &thread->process->crit_section );
440     if (*bits == 0xffffffff)
441     {
442         bits++;
443         ret = 32;
444         if (*bits == 0xffffffff)
445         {
446             LeaveCriticalSection( &thread->process->crit_section );
447             SetLastError( ERROR_NO_MORE_ITEMS );
448             return 0xffffffff;
449         }
450     }
451     for (i = 0, mask = 1; i < 32; i++, mask <<= 1) if (!(*bits & mask)) break;
452     *bits |= mask;
453     LeaveCriticalSection( &thread->process->crit_section );
454     return ret + i;
455 }
456
457
458 /**********************************************************************
459  *           TlsFree   (KERNEL32.531)
460  */
461 BOOL32 WINAPI TlsFree( DWORD index )
462 {
463     DWORD mask;
464     THDB *thread = THREAD_Current();
465     DWORD *bits = thread->process->tls_bits;
466     if (index >= 64)
467     {
468         SetLastError( ERROR_INVALID_PARAMETER );
469         return FALSE;
470     }
471     EnterCriticalSection( &thread->process->crit_section );
472     if (index >= 32) bits++;
473     mask = (1 << (index & 31));
474     if (!(*bits & mask))  /* already free? */
475     {
476         LeaveCriticalSection( &thread->process->crit_section );
477         SetLastError( ERROR_INVALID_PARAMETER );
478         return FALSE;
479     }
480     *bits &= ~mask;
481     thread->tls_array[index] = 0;
482     /* FIXME: should zero all other thread values */
483     LeaveCriticalSection( &thread->process->crit_section );
484     return TRUE;
485 }
486
487
488 /**********************************************************************
489  *           TlsGetValue   (KERNEL32.532)
490  */
491 LPVOID WINAPI TlsGetValue( DWORD index )
492 {
493     THDB *thread = THREAD_Current();
494     if (index >= 64)
495     {
496         SetLastError( ERROR_INVALID_PARAMETER );
497         return NULL;
498     }
499     SetLastError( ERROR_SUCCESS );
500     return thread->tls_array[index];
501 }
502
503
504 /**********************************************************************
505  *           TlsSetValue   (KERNEL32.533)
506  */
507 BOOL32 WINAPI TlsSetValue( DWORD index, LPVOID value )
508 {
509     THDB *thread = THREAD_Current();
510     if (index >= 64)
511     {
512         SetLastError( ERROR_INVALID_PARAMETER );
513         return FALSE;
514     }
515     thread->tls_array[index] = value;
516     return TRUE;
517 }
518
519
520 /***********************************************************************
521  *           GetThreadContext   (KERNEL32.294)
522  */
523 BOOL32 WINAPI GetThreadContext( HANDLE32 handle, CONTEXT *context )
524 {
525     THDB *thread = THREAD_GetPtr( handle );
526     if (!thread) return FALSE;
527     *context = thread->context;
528     K32OBJ_DecCount( &thread->header );
529     return TRUE;
530 }
531
532
533 /**********************************************************************
534  *           GetThreadPriority   (KERNEL32.296)
535  */
536 INT32 WINAPI GetThreadPriority(HANDLE32 hthread)
537 {
538     THDB *thread;
539     INT32 ret;
540     
541     if (!(thread = THREAD_GetPtr( hthread ))) return 0;
542     ret = thread->delta_priority;
543     K32OBJ_DecCount( &thread->header );
544     return ret;
545 }
546
547
548 /**********************************************************************
549  *           SetThreadPriority   (KERNEL32.514)
550  */
551 BOOL32 WINAPI SetThreadPriority(HANDLE32 hthread,INT32 priority)
552 {
553     THDB *thread;
554     
555     if (!(thread = THREAD_GetPtr( hthread ))) return FALSE;
556     thread->delta_priority = priority;
557     K32OBJ_DecCount( &thread->header );
558     return TRUE;
559 }
560
561 /**********************************************************************
562  *           TerminateThread   (KERNEL32)
563  */
564 BOOL32 WINAPI TerminateThread(HANDLE32 handle,DWORD exitcode)
565 {
566     fprintf(stdnimp,"TerminateThread(0x%08x,%ld), STUB!\n",handle,exitcode);
567     return TRUE;
568 }
569
570 /**********************************************************************
571  *           GetExitCodeThread   (KERNEL32)
572  */
573 BOOL32 WINAPI GetExitCodeThread(HANDLE32 hthread,LPDWORD exitcode)
574 {
575     THDB *thread;
576     
577     if (!(thread = THREAD_GetPtr( hthread ))) return FALSE;
578     if (exitcode) *exitcode = thread->exit_code;
579     K32OBJ_DecCount( &thread->header );
580     return TRUE;
581 }
582
583 /**********************************************************************
584  *           ResumeThread   (KERNEL32)
585  */
586 BOOL32 WINAPI ResumeThread( HANDLE32 handle )
587 {
588     fprintf(stdnimp,"ResumeThread(0x%08x), STUB!\n",handle);
589     return TRUE;
590 }
591
592 /**********************************************************************
593  *           SuspendThread   (KERNEL32)
594  */
595 BOOL32 WINAPI SuspendThread( HANDLE32 handle )
596 {
597     fprintf(stdnimp,"SuspendThread(0x%08x), STUB!\n",handle);
598     return TRUE;
599 }