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