Release 970202
[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 "winnt.h"
14
15 THDB *pCurrentThread = NULL;
16
17 /***********************************************************************
18  *           THREAD_GetPtr
19  *
20  * Return a pointer to a thread object. The object count must be decremented
21  * when no longer used.
22  */
23 static THDB *THREAD_GetPtr( HANDLE32 handle )
24 {
25     THDB *thread;
26
27     if (handle == 0xfffffffe)  /* Self-thread handle */
28     {
29         thread = (THDB *)GetCurrentThreadId();
30         K32OBJ_IncCount( &thread->header );
31     }
32     else thread = (THDB *)PROCESS_GetObjPtr( handle, K32OBJ_THREAD );
33     return thread;
34 }
35
36
37 /***********************************************************************
38  *           THREAD_Create
39  */
40 THDB *THREAD_Create( PDB32 *pdb, DWORD stack_size,
41                      LPTHREAD_START_ROUTINE start_addr )
42 {
43     THDB *thdb = HeapAlloc( SystemHeap, HEAP_ZERO_MEMORY, sizeof(THDB) );
44     if (!thdb) return NULL;
45     thdb->header.type     = K32OBJ_THREAD;
46     thdb->header.refcount = 1;
47     thdb->process         = pdb;
48     thdb->teb.except      = (void *)-1;
49     thdb->teb.htask16     = 0; /* FIXME */
50     thdb->teb.stack_sel   = 0; /* FIXME */
51     thdb->teb.self        = &thdb->teb;
52     thdb->teb.tls_ptr     = thdb->tls_array;
53     thdb->process2        = pdb;
54     thdb->exit_code       = 0x103; /* STILL_ACTIVE */
55
56     /* Allocate the stack */
57
58     if (!stack_size) stack_size = 1024 * 1024;  /* default size = 1Mb */
59     thdb->stack_base = VirtualAlloc( NULL, stack_size, MEM_COMMIT,
60                                      PAGE_EXECUTE_READWRITE );
61     if (!thdb->stack_base) goto error;
62     /* Un-commit the first page (FIXME: should use PAGE_GUARD instead) */
63     VirtualFree( thdb->stack_base, 1, MEM_DECOMMIT );
64     thdb->teb.stack_top   = (char *)thdb->stack_base + stack_size;
65     thdb->teb.stack_low   = thdb->teb.stack_top;
66     thdb->exit_stack      = thdb->teb.stack_top;
67
68     /* Allocate the TEB selector (%fs register) */
69
70     thdb->teb_sel = SELECTOR_AllocBlock( &thdb->teb, 0x1000, SEGMENT_DATA,
71                                          TRUE, FALSE );
72     if (!thdb->teb_sel) goto error;
73
74     /* Initialize the thread context */
75
76     thdb->pcontext        = &thdb->context;
77     thdb->context.SegCs   = WINE_CODE_SELECTOR;
78     thdb->context.SegDs   = WINE_DATA_SELECTOR;
79     thdb->context.SegEs   = WINE_DATA_SELECTOR;
80     thdb->context.SegFs   = thdb->teb_sel;
81     thdb->context.SegGs   = WINE_DATA_SELECTOR;
82     thdb->context.SegSs   = WINE_DATA_SELECTOR;
83     thdb->context.Eip     = (DWORD)start_addr;
84     thdb->context.Esp     = (DWORD)thdb->teb.stack_top;
85
86     return thdb;
87
88 error:
89     if (thdb->teb_sel) SELECTOR_FreeBlock( thdb->teb_sel, 1 );
90     if (thdb->stack_base) VirtualFree( thdb->stack_base, 0, MEM_RELEASE );
91     HeapFree( SystemHeap, 0, thdb );
92     return NULL;
93 }
94
95
96 /***********************************************************************
97  *           THREAD_Destroy
98  */
99 void THREAD_Destroy( K32OBJ *ptr )
100 {
101     THDB *thdb = (THDB *)ptr;
102     assert( ptr->type == K32OBJ_THREAD );
103     ptr->type = K32OBJ_UNKNOWN;
104     SELECTOR_FreeBlock( thdb->teb_sel, 1 );
105     HeapFree( SystemHeap, 0, thdb );
106 }
107
108
109 /***********************************************************************
110  *           CreateThread   (KERNEL32.63)
111  *
112  * The only thing missing here is actually getting the thread to run ;-)
113  */
114 HANDLE32 CreateThread( LPSECURITY_ATTRIBUTES attribs, DWORD stack,
115                        LPTHREAD_START_ROUTINE start, LPVOID param,
116                        DWORD flags, LPDWORD id )
117 {
118     HANDLE32 handle;
119     THDB *thread = THREAD_Create( pCurrentProcess, stack, start );
120     if (!thread) return INVALID_HANDLE_VALUE32;
121     handle = PROCESS_AllocHandle( &thread->header, 0 );
122     if (handle == INVALID_HANDLE_VALUE32)
123     {
124         THREAD_Destroy( &thread->header );
125         return INVALID_HANDLE_VALUE32;
126     }
127     *id = (DWORD)thread;
128     fprintf( stderr, "CreateThread: stub\n" );
129     return handle;
130 }
131
132
133 /***********************************************************************
134  *           GetCurrentThread   (KERNEL32.200)
135  */
136 HANDLE32 GetCurrentThread(void)
137 {
138     return 0xFFFFFFFE;
139 }
140
141
142 /***********************************************************************
143  *           GetCurrentThreadId   (KERNEL32.201)
144  * Returns crypted (xor'ed) pointer to THDB in Win95.
145  */
146 DWORD GetCurrentThreadId(void)
147 {
148     /* FIXME: should probably use %fs register here */
149     assert( pCurrentThread );
150     return (DWORD)pCurrentThread;
151 }
152
153
154 /**********************************************************************
155  *           GetLastError   (KERNEL.148) (KERNEL32.227)
156  */
157 DWORD GetLastError(void)
158 {
159     THDB *thread = (THDB *)GetCurrentThreadId();
160     return thread->last_error;
161 }
162
163
164 /**********************************************************************
165  *           SetLastError   (KERNEL.147) (KERNEL32.497)
166  */
167 void SetLastError( DWORD error )
168 {
169     THDB *thread;
170     if (!pCurrentThread) return;  /* FIXME */
171     thread = (THDB *)GetCurrentThreadId();
172     thread->last_error = error;
173 }
174
175
176 /**********************************************************************
177  *           SetLastErrorEx   (USER32.484)
178  */
179 void SetLastErrorEx( DWORD error, DWORD type )
180 {
181     /* FIXME: what about 'type'? */
182     SetLastError( error );
183 }
184
185
186 /**********************************************************************
187  *           TlsAlloc   (KERNEL32.530)
188  */
189 DWORD TlsAlloc(void)
190 {
191     DWORD i, mask, ret = 0;
192     THDB *thread = (THDB *)GetCurrentThreadId();
193     DWORD *bits = thread->process->tls_bits;
194     EnterCriticalSection( &thread->process->crit_section );
195     if (*bits == 0xffffffff)
196     {
197         bits++;
198         ret = 32;
199         if (*bits == 0xffffffff)
200         {
201             LeaveCriticalSection( &thread->process->crit_section );
202             SetLastError( ERROR_NO_MORE_ITEMS );
203             return 0xffffffff;
204         }
205     }
206     for (i = 0, mask = 1; i < 32; i++, mask <<= 1) if (!(*bits & mask)) break;
207     *bits |= mask;
208     LeaveCriticalSection( &thread->process->crit_section );
209     return ret + i;
210 }
211
212
213 /**********************************************************************
214  *           TlsFree   (KERNEL32.531)
215  */
216 BOOL32 TlsFree( DWORD index )
217 {
218     DWORD mask;
219     THDB *thread = (THDB *)GetCurrentThreadId();
220     DWORD *bits = thread->process->tls_bits;
221     if (index >= 64)
222     {
223         SetLastError( ERROR_INVALID_PARAMETER );
224         return FALSE;
225     }
226     EnterCriticalSection( &thread->process->crit_section );
227     if (index >= 32) bits++;
228     mask = (1 << (index & 31));
229     if (!(*bits & mask))  /* already free? */
230     {
231         LeaveCriticalSection( &thread->process->crit_section );
232         SetLastError( ERROR_INVALID_PARAMETER );
233         return FALSE;
234     }
235     *bits &= ~mask;
236     thread->tls_array[index] = 0;
237     /* FIXME: should zero all other thread values */
238     LeaveCriticalSection( &thread->process->crit_section );
239     return TRUE;
240 }
241
242
243 /**********************************************************************
244  *           TlsGetValue   (KERNEL32.532)
245  */
246 LPVOID TlsGetValue( DWORD index )
247 {
248     THDB *thread = (THDB *)GetCurrentThreadId();
249     if (index >= 64)
250     {
251         SetLastError( ERROR_INVALID_PARAMETER );
252         return NULL;
253     }
254     SetLastError( ERROR_SUCCESS );
255     return thread->tls_array[index];
256 }
257
258
259 /**********************************************************************
260  *           TlsSetValue   (KERNEL32.533)
261  */
262 BOOL32 TlsSetValue( DWORD index, LPVOID value )
263 {
264     THDB *thread = (THDB *)GetCurrentThreadId();
265     if (index >= 64)
266     {
267         SetLastError( ERROR_INVALID_PARAMETER );
268         return FALSE;
269     }
270     thread->tls_array[index] = value;
271     return TRUE;
272 }
273
274
275 /***********************************************************************
276  *           GetThreadContext   (KERNEL32.294)
277  */
278 BOOL32 GetThreadContext( HANDLE32 handle, CONTEXT *context )
279 {
280     THDB *thread = (THDB*)PROCESS_GetObjPtr( handle, K32OBJ_THREAD );
281     if (!thread) return FALSE;
282     *context = thread->context;
283     K32OBJ_DecCount( &thread->header );
284     return TRUE;
285 }
286
287
288 /**********************************************************************
289  *           NtCurrentTeb   (NTDLL.89)
290  */
291 void NtCurrentTeb( CONTEXT *context )
292 {
293     EAX_reg(context) = GetSelectorBase( FS_reg(context) );
294 }
295
296
297 /**********************************************************************
298  *           GetThreadPriority   (KERNEL32.296)
299  */
300 INT32 GetThreadPriority(HANDLE32 hthread)
301 {
302     THDB *thread;
303     INT32 ret;
304     
305     if (!(thread = THREAD_GetPtr( hthread ))) return 0;
306     ret = thread->delta_priority;
307     K32OBJ_DecCount( &thread->header );
308     return ret;
309 }
310
311
312 /**********************************************************************
313  *           SetThreadPriority   (KERNEL32.514)
314  */
315 BOOL32 SetThreadPriority(HANDLE32 hthread,INT32 priority)
316 {
317     THDB *thread;
318     
319     if (!(thread = THREAD_GetPtr( hthread ))) return FALSE;
320     thread->delta_priority = priority;
321     K32OBJ_DecCount( &thread->header );
322     return TRUE;
323 }