4 * Copyright 1996 Alexandre Julliard
15 #include "selectors.h"
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 */
38 THREAD_Destroy
/* destroy */
42 /* Is threading code initialized? */
43 BOOL32 THREAD_InitDone
= FALSE
;
45 /**********************************************************************
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
)
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
);
65 /***********************************************************************
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 /***********************************************************************
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
,
88 entry
->thread
= thread
;
91 entry
->next
= (*queue
)->next
;
92 (*queue
)->next
= entry
;
94 else entry
->next
= entry
;
99 /***********************************************************************
102 * Remove a thread from a queue.
104 void THREAD_RemoveQueue( THREAD_QUEUE
*queue
, THDB
*thread
)
106 THREAD_ENTRY
*entry
= *queue
;
108 if (entry
->next
== entry
) /* Only one element in the queue */
110 assert( entry
->thread
== thread
);
116 while (entry
->next
->thread
!= thread
)
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
);
130 /***********************************************************************
133 THDB
*THREAD_Create( PDB32
*pdb
, DWORD stack_size
,
134 LPTHREAD_START_ROUTINE start_addr
, LPVOID param
)
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;
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 */
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
,
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
,
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 */
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
;
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
);
214 /***********************************************************************
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 /***********************************************************************
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 /***********************************************************************
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 /***********************************************************************
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 /***********************************************************************
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 */
278 /* Check if we are deleting the current thread */
281 if (fs
== thdb
->teb_sel
)
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
)
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
);
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();
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 */
334 /* FIXME: We cannot do this; once the current thread is destroyed,
335 synchronization primitives do not work properly. */
336 K32OBJ_DecCount( &thdb
->header
);
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)
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 /**********************************************************************
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)
406 if (*bits
== 0xffffffff)
408 LeaveCriticalSection( &thread
->process
->crit_section
);
409 SetLastError( ERROR_NO_MORE_ITEMS
);
413 for (i
= 0, mask
= 1; i
< 32; i
++, mask
<<= 1) if (!(*bits
& mask
)) break;
415 LeaveCriticalSection( &thread
->process
->crit_section
);
419 /**********************************************************************
420 * TlsAlloc (KERNEL32.530)
422 * Allocates a thread local storage 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
)
440 THDB
*thread
= THREAD_Current();
441 DWORD
*bits
= thread
->process
->tls_bits
;
444 SetLastError( ERROR_INVALID_PARAMETER
);
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
);
457 thread
->tls_array
[index
] = 0;
458 /* FIXME: should zero all other thread values */
459 LeaveCriticalSection( &thread
->process
->crit_section
);
464 /**********************************************************************
465 * TlsGetValue (KERNEL32.532)
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();
476 SetLastError( ERROR_INVALID_PARAMETER
);
479 SetLastError( ERROR_SUCCESS
);
480 return thread
->tls_array
[index
];
484 /**********************************************************************
485 * TlsSetValue (KERNEL32.533)
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();
497 SetLastError( ERROR_INVALID_PARAMETER
);
500 thread
->tls_array
[index
] = value
;
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
);
518 /**********************************************************************
519 * GetThreadPriority (KERNEL32.296)
521 INT32 WINAPI
GetThreadPriority(HANDLE32 hthread
)
526 if (!(thread
= THREAD_GetPtr( hthread
, THREAD_QUERY_INFORMATION
)))
528 ret
= thread
->delta_priority
;
529 K32OBJ_DecCount( &thread
->header
);
534 /**********************************************************************
535 * SetThreadPriority (KERNEL32.514)
537 BOOL32 WINAPI
SetThreadPriority(HANDLE32 hthread
,INT32 priority
)
541 if (!(thread
= THREAD_GetPtr( hthread
, THREAD_SET_INFORMATION
)))
543 thread
->delta_priority
= priority
;
544 K32OBJ_DecCount( &thread
->header
);
548 /**********************************************************************
549 * TerminateThread (KERNEL32)
551 BOOL32 WINAPI
TerminateThread(HANDLE32 handle
,DWORD exitcode
)
553 FIXME(thread
,"(0x%08x,%ld): STUB!\n",handle
,exitcode
);
557 /**********************************************************************
558 * GetExitCodeThread (KERNEL32)
560 BOOL32 WINAPI
GetExitCodeThread(HANDLE32 hthread
,LPDWORD exitcode
)
564 if (!(thread
= THREAD_GetPtr( hthread
, THREAD_QUERY_INFORMATION
)))
566 if (exitcode
) *exitcode
= thread
->exit_code
;
567 K32OBJ_DecCount( &thread
->header
);
571 /**********************************************************************
572 * ResumeThread (KERNEL32)
574 BOOL32 WINAPI
ResumeThread( HANDLE32 handle
)
576 FIXME(thread
,"(0x%08x): STUB!\n",handle
);
580 /**********************************************************************
581 * SuspendThread (KERNEL32)
583 BOOL32 WINAPI
SuspendThread( HANDLE32 handle
)
585 FIXME(thread
,"(0x%08x): STUB!\n",handle
);
589 /**********************************************************************
590 * GetThreadTimes (KERNEL32)
592 BOOL32 WINAPI
GetThreadTimes(
594 LPFILETIME creationtime
,
596 LPFILETIME kerneltime
,
599 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);