Release 980329
[wine/multimedia.git] / scheduler / thread.c
blob8774b837e920ba9c0d960f5f68ff46bdf5460313
1 /*
2 * Win32 threads
4 * Copyright 1996 Alexandre Julliard
5 */
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"
20 #ifndef __i386__
21 THDB *pCurrentThread;
22 #endif
24 static BOOL32 THREAD_Signaled( K32OBJ *obj, DWORD thread_id );
25 static BOOL32 THREAD_Satisfied( K32OBJ *obj, DWORD thread_id );
26 static void THREAD_AddWait( K32OBJ *obj, DWORD thread_id );
27 static void THREAD_RemoveWait( K32OBJ *obj, DWORD thread_id );
28 static void THREAD_Destroy( K32OBJ *obj );
30 const K32OBJ_OPS THREAD_Ops =
32 THREAD_Signaled, /* signaled */
33 THREAD_Satisfied, /* satisfied */
34 THREAD_AddWait, /* add_wait */
35 THREAD_RemoveWait, /* remove_wait */
36 NULL, /* read */
37 NULL, /* write */
38 THREAD_Destroy /* destroy */
42 /* Is threading code initialized? */
43 BOOL32 THREAD_InitDone = FALSE;
45 /**********************************************************************
46 * THREAD_GetPtr
48 * Return a pointer to a thread object. The object count must be decremented
49 * when no longer used.
51 THDB *THREAD_GetPtr( HANDLE32 handle, DWORD access )
53 THDB *thread;
55 if (handle == 0xfffffffe) /* Self-thread handle */
57 thread = THREAD_Current();
58 K32OBJ_IncCount( &thread->header );
60 else thread = (THDB *)HANDLE_GetObjPtr( handle, K32OBJ_THREAD, access );
61 return thread;
65 /***********************************************************************
66 * THREAD_Current
68 * Return the current thread THDB pointer.
70 THDB *THREAD_Current(void)
72 if (!THREAD_InitDone) return NULL;
73 return (THDB *)((char *)NtCurrentTeb() - (int)&((THDB *)0)->teb);
77 /***********************************************************************
78 * THREAD_AddQueue
80 * Add a thread to a queue.
82 void THREAD_AddQueue( THREAD_QUEUE *queue, THDB *thread )
84 THREAD_ENTRY *entry = HeapAlloc( SystemHeap, HEAP_NO_SERIALIZE,
85 sizeof(*entry) );
86 assert(entry);
87 SYSTEM_LOCK();
88 entry->thread = thread;
89 if (*queue)
91 entry->next = (*queue)->next;
92 (*queue)->next = entry;
94 else entry->next = entry;
95 *queue = entry;
96 SYSTEM_UNLOCK();
99 /***********************************************************************
100 * THREAD_RemoveQueue
102 * Remove a thread from a queue.
104 void THREAD_RemoveQueue( THREAD_QUEUE *queue, THDB *thread )
106 THREAD_ENTRY *entry = *queue;
107 SYSTEM_LOCK();
108 if (entry->next == entry) /* Only one element in the queue */
110 assert( entry->thread == thread );
111 *queue = NULL;
113 else
115 THREAD_ENTRY *next;
116 while (entry->next->thread != thread)
118 entry = entry->next;
119 assert( entry != *queue ); /* Have we come all the way around? */
121 if ((next = entry->next) == *queue) *queue = entry;
122 entry->next = entry->next->next;
123 entry = next; /* This is the one we want to free */
125 HeapFree( SystemHeap, 0, entry );
126 SYSTEM_UNLOCK();
130 /***********************************************************************
131 * THREAD_Create
133 THDB *THREAD_Create( PDB32 *pdb, DWORD stack_size,
134 LPTHREAD_START_ROUTINE start_addr, LPVOID param )
136 DWORD old_prot;
137 WORD cs, ds;
139 THDB *thdb = HeapAlloc( SystemHeap, HEAP_ZERO_MEMORY, sizeof(THDB) );
140 if (!thdb) return NULL;
141 thdb->header.type = K32OBJ_THREAD;
142 thdb->header.refcount = 1;
143 thdb->process = pdb;
144 thdb->teb.except = (void *)-1;
145 thdb->teb.htask16 = 0; /* FIXME */
146 thdb->teb.stack_sel = 0; /* FIXME */
147 thdb->teb.self = &thdb->teb;
148 thdb->teb.tls_ptr = thdb->tls_array;
149 thdb->teb.process = pdb;
150 thdb->wait_list = &thdb->wait_struct;
151 thdb->exit_code = 0x103; /* STILL_ACTIVE */
152 thdb->entry_point = start_addr;
153 thdb->entry_arg = param;
155 /* Allocate the stack */
157 /* FIXME:
158 * If stacksize smaller than 1 MB, allocate 1MB
159 * (one program wanted only 10 kB, which is recommendable, but some WINE
160 * functions, noteably in the files subdir, push HUGE structures and
161 * arrays on the stack. They probably shouldn't.)
162 * If stacksize larger than 16 MB, warn the user. (We could shrink the stack
163 * but this could give more or less unexplainable crashes.)
165 if (stack_size<1024*1024)
166 stack_size = 1024 * 1024;
167 if (stack_size >= 16*1024*1024)
168 fprintf(stderr,"Warning:Thread stack size is %ld MB.\n",stack_size/1024/1024);
169 thdb->stack_base = VirtualAlloc( NULL, stack_size, MEM_COMMIT,
170 PAGE_EXECUTE_READWRITE );
171 if (!thdb->stack_base) goto error;
172 /* Set a guard page at the bottom of the stack */
173 VirtualProtect( thdb->stack_base, 1, PAGE_EXECUTE_READWRITE | PAGE_GUARD,
174 &old_prot );
175 thdb->teb.stack_top = (char *)thdb->stack_base + stack_size;
176 thdb->teb.stack_low = thdb->stack_base;
177 thdb->exit_stack = thdb->teb.stack_top;
179 /* Allocate the TEB selector (%fs register) */
181 thdb->teb_sel = SELECTOR_AllocBlock( &thdb->teb, 0x1000, SEGMENT_DATA,
182 TRUE, FALSE );
183 if (!thdb->teb_sel) goto error;
185 /* Allocate the event */
187 if (!(thdb->event = EVENT_Create( TRUE, FALSE ))) goto error;
189 /* Initialize the thread context */
191 GET_CS(cs);
192 GET_DS(ds);
193 thdb->pcontext = &thdb->context;
194 thdb->context.SegCs = cs;
195 thdb->context.SegDs = ds;
196 thdb->context.SegEs = ds;
197 thdb->context.SegGs = ds;
198 thdb->context.SegSs = ds;
199 thdb->context.SegFs = thdb->teb_sel;
200 thdb->context.Eip = (DWORD)start_addr;
201 thdb->context.Esp = (DWORD)thdb->teb.stack_top;
202 PE_InitTls( thdb );
203 return thdb;
205 error:
206 if (thdb->event) K32OBJ_DecCount( thdb->event );
207 if (thdb->teb_sel) SELECTOR_FreeBlock( thdb->teb_sel, 1 );
208 if (thdb->stack_base) VirtualFree( thdb->stack_base, 0, MEM_RELEASE );
209 HeapFree( SystemHeap, 0, thdb );
210 return NULL;
214 /***********************************************************************
215 * THREAD_Signaled
217 static BOOL32 THREAD_Signaled( K32OBJ *obj, DWORD thread_id )
219 THDB *thdb = (THDB *)obj;
220 assert( obj->type == K32OBJ_THREAD );
221 return K32OBJ_OPS( thdb->event )->signaled( thdb->event, thread_id );
225 /***********************************************************************
226 * THREAD_Satisfied
228 * Wait on this object has been satisfied.
230 static BOOL32 THREAD_Satisfied( K32OBJ *obj, DWORD thread_id )
232 THDB *thdb = (THDB *)obj;
233 assert( obj->type == K32OBJ_THREAD );
234 return K32OBJ_OPS( thdb->event )->satisfied( thdb->event, thread_id );
238 /***********************************************************************
239 * THREAD_AddWait
241 * Add thread to object wait queue.
243 static void THREAD_AddWait( K32OBJ *obj, DWORD thread_id )
245 THDB *thdb = (THDB *)obj;
246 assert( obj->type == K32OBJ_THREAD );
247 return K32OBJ_OPS( thdb->event )->add_wait( thdb->event, thread_id );
251 /***********************************************************************
252 * THREAD_RemoveWait
254 * Remove thread from object wait queue.
256 static void THREAD_RemoveWait( K32OBJ *obj, DWORD thread_id )
258 THDB *thdb = (THDB *)obj;
259 assert( obj->type == K32OBJ_THREAD );
260 return K32OBJ_OPS( thdb->event )->remove_wait( thdb->event, thread_id );
264 /***********************************************************************
265 * THREAD_Destroy
267 static void THREAD_Destroy( K32OBJ *ptr )
269 THDB *thdb = (THDB *)ptr;
271 assert( ptr->type == K32OBJ_THREAD );
272 ptr->type = K32OBJ_UNKNOWN;
274 /* Free the associated memory */
276 #ifdef __i386__
278 /* Check if we are deleting the current thread */
279 WORD fs;
280 GET_FS( fs );
281 if (fs == thdb->teb_sel)
283 GET_DS( fs );
284 SET_FS( fs );
287 #endif
288 K32OBJ_DecCount( thdb->event );
289 SELECTOR_FreeBlock( thdb->teb_sel, 1 );
290 HeapFree( SystemHeap, 0, thdb );
295 /***********************************************************************
296 * CreateThread (KERNEL32.63)
298 HANDLE32 WINAPI CreateThread( LPSECURITY_ATTRIBUTES attribs, DWORD stack,
299 LPTHREAD_START_ROUTINE start, LPVOID param,
300 DWORD flags, LPDWORD id )
302 HANDLE32 handle;
303 THDB *thread = THREAD_Create( PROCESS_Current(), stack, start, param );
304 if (!thread) return INVALID_HANDLE_VALUE32;
305 handle = HANDLE_Alloc( &thread->header, THREAD_ALL_ACCESS, FALSE );
306 if (handle == INVALID_HANDLE_VALUE32) goto error;
307 if (SYSDEPS_SpawnThread( thread ) == -1) goto error;
308 *id = THDB_TO_THREAD_ID( thread );
309 return handle;
311 error:
312 K32OBJ_DecCount( &thread->header );
313 return INVALID_HANDLE_VALUE32;
317 /***********************************************************************
318 * ExitThread (KERNEL32.215)
320 void WINAPI ExitThread( DWORD code )
322 THDB *thdb = THREAD_Current();
323 LONG count;
325 SYSTEM_LOCK();
326 thdb->exit_code = code;
327 EVENT_Set( thdb->event );
329 /* Abandon all owned mutexes */
330 while (thdb->mutex_list) MUTEX_Abandon( thdb->mutex_list );
332 /* FIXME: should free the stack somehow */
333 #if 0
334 /* FIXME: We cannot do this; once the current thread is destroyed,
335 synchronization primitives do not work properly. */
336 K32OBJ_DecCount( &thdb->header );
337 #endif
338 /* Completely unlock the system lock just in case */
339 count = SYSTEM_LOCK_COUNT();
340 while (count--) SYSTEM_UNLOCK();
341 SYSDEPS_ExitThread();
345 /***********************************************************************
346 * GetCurrentThread (KERNEL32.200)
348 HANDLE32 WINAPI GetCurrentThread(void)
350 return 0xFFFFFFFE;
354 /***********************************************************************
355 * GetCurrentThreadId (KERNEL32.201)
357 DWORD WINAPI GetCurrentThreadId(void)
359 return THDB_TO_THREAD_ID( THREAD_Current() );
363 /**********************************************************************
364 * GetLastError (KERNEL.148) (KERNEL32.227)
366 DWORD WINAPI GetLastError(void)
368 THDB *thread = THREAD_Current();
369 return thread->last_error;
373 /**********************************************************************
374 * SetLastError (KERNEL.147) (KERNEL32.497)
376 void WINAPI SetLastError( DWORD error )
378 THDB *thread = THREAD_Current();
379 /* This one must work before we have a thread (FIXME) */
380 if (thread) thread->last_error = error;
384 /**********************************************************************
385 * SetLastErrorEx (USER32.484)
387 void WINAPI SetLastErrorEx( DWORD error, DWORD type )
389 /* FIXME: what about 'type'? */
390 SetLastError( error );
394 /**********************************************************************
395 * THREAD_TlsAlloc
397 DWORD THREAD_TlsAlloc(THDB *thread)
399 DWORD i, mask, ret = 0;
400 DWORD *bits = thread->process->tls_bits;
401 EnterCriticalSection( &thread->process->crit_section );
402 if (*bits == 0xffffffff)
404 bits++;
405 ret = 32;
406 if (*bits == 0xffffffff)
408 LeaveCriticalSection( &thread->process->crit_section );
409 SetLastError( ERROR_NO_MORE_ITEMS );
410 return 0xffffffff;
413 for (i = 0, mask = 1; i < 32; i++, mask <<= 1) if (!(*bits & mask)) break;
414 *bits |= mask;
415 LeaveCriticalSection( &thread->process->crit_section );
416 return ret + i;
419 /**********************************************************************
420 * TlsAlloc (KERNEL32.530)
422 * Allocates a thread local storage index
424 * RETURNS
425 * TLS Index
426 * 0xFFFFFFFF: Failure
428 DWORD WINAPI TlsAlloc(void)
430 return THREAD_TlsAlloc(THREAD_Current());
434 /**********************************************************************
435 * TlsFree (KERNEL32.531)
437 BOOL32 WINAPI TlsFree( DWORD index )
439 DWORD mask;
440 THDB *thread = THREAD_Current();
441 DWORD *bits = thread->process->tls_bits;
442 if (index >= 64)
444 SetLastError( ERROR_INVALID_PARAMETER );
445 return FALSE;
447 EnterCriticalSection( &thread->process->crit_section );
448 if (index >= 32) bits++;
449 mask = (1 << (index & 31));
450 if (!(*bits & mask)) /* already free? */
452 LeaveCriticalSection( &thread->process->crit_section );
453 SetLastError( ERROR_INVALID_PARAMETER );
454 return FALSE;
456 *bits &= ~mask;
457 thread->tls_array[index] = 0;
458 /* FIXME: should zero all other thread values */
459 LeaveCriticalSection( &thread->process->crit_section );
460 return TRUE;
464 /**********************************************************************
465 * TlsGetValue (KERNEL32.532)
466 * RETURNS
467 * !0: Value stored in calling thread's TLS slot for index
468 * 0: Check GetLastError() for error code
470 LPVOID WINAPI TlsGetValue(
471 DWORD index /* TLS index to retrieve value for */
473 THDB *thread = THREAD_Current();
474 if (index >= 64)
476 SetLastError( ERROR_INVALID_PARAMETER );
477 return NULL;
479 SetLastError( ERROR_SUCCESS );
480 return thread->tls_array[index];
484 /**********************************************************************
485 * TlsSetValue (KERNEL32.533)
486 * RETURNS
487 * TRUE: Successful
488 * FALSE: Failure
490 BOOL32 WINAPI TlsSetValue(
491 DWORD index, /* TLS index to set value for */
492 LPVOID value /* value to be stored */
494 THDB *thread = THREAD_Current();
495 if (index >= 64)
497 SetLastError( ERROR_INVALID_PARAMETER );
498 return FALSE;
500 thread->tls_array[index] = value;
501 return TRUE;
505 /***********************************************************************
506 * GetThreadContext (KERNEL32.294)
508 BOOL32 WINAPI GetThreadContext( HANDLE32 handle, CONTEXT *context )
510 THDB *thread = THREAD_GetPtr( handle, THREAD_GET_CONTEXT );
511 if (!thread) return FALSE;
512 *context = thread->context;
513 K32OBJ_DecCount( &thread->header );
514 return TRUE;
518 /**********************************************************************
519 * GetThreadPriority (KERNEL32.296)
521 INT32 WINAPI GetThreadPriority(HANDLE32 hthread)
523 THDB *thread;
524 INT32 ret;
526 if (!(thread = THREAD_GetPtr( hthread, THREAD_QUERY_INFORMATION )))
527 return 0;
528 ret = thread->delta_priority;
529 K32OBJ_DecCount( &thread->header );
530 return ret;
534 /**********************************************************************
535 * SetThreadPriority (KERNEL32.514)
537 BOOL32 WINAPI SetThreadPriority(HANDLE32 hthread,INT32 priority)
539 THDB *thread;
541 if (!(thread = THREAD_GetPtr( hthread, THREAD_SET_INFORMATION )))
542 return FALSE;
543 thread->delta_priority = priority;
544 K32OBJ_DecCount( &thread->header );
545 return TRUE;
548 /**********************************************************************
549 * TerminateThread (KERNEL32)
551 BOOL32 WINAPI TerminateThread(HANDLE32 handle,DWORD exitcode)
553 FIXME(thread,"(0x%08x,%ld): STUB!\n",handle,exitcode);
554 return TRUE;
557 /**********************************************************************
558 * GetExitCodeThread (KERNEL32)
560 BOOL32 WINAPI GetExitCodeThread(HANDLE32 hthread,LPDWORD exitcode)
562 THDB *thread;
564 if (!(thread = THREAD_GetPtr( hthread, THREAD_QUERY_INFORMATION )))
565 return FALSE;
566 if (exitcode) *exitcode = thread->exit_code;
567 K32OBJ_DecCount( &thread->header );
568 return TRUE;
571 /**********************************************************************
572 * ResumeThread (KERNEL32)
574 BOOL32 WINAPI ResumeThread( HANDLE32 handle )
576 FIXME(thread,"(0x%08x): STUB!\n",handle);
577 return TRUE;
580 /**********************************************************************
581 * SuspendThread (KERNEL32)
583 BOOL32 WINAPI SuspendThread( HANDLE32 handle )
585 FIXME(thread,"(0x%08x): STUB!\n",handle);
586 return TRUE;
589 /**********************************************************************
590 * GetThreadTimes (KERNEL32)
592 BOOL32 WINAPI GetThreadTimes(
593 HANDLE32 thread,
594 LPFILETIME creationtime,
595 LPFILETIME exittime,
596 LPFILETIME kerneltime,
597 LPFILETIME usertime
599 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
600 return FALSE;