source: trunk/src/kernel32/thread.cpp@ 9910

Last change on this file since 9910 was 9910, checked in by sandervl, 22 years ago

KSO: logging changes; fast PID & TID retrieval; added functions to set/clear the odin environment (fs, exception handler)

File size: 17.7 KB
Line 
1/* $Id: thread.cpp,v 1.51 2003-03-06 10:22:27 sandervl Exp $ */
2
3/*
4 * Win32 Thread API functions
5 *
6 * TODO: Initialize threadInfo structure during thread creation
7 *
8 * Copyright 1998 Sander van Leeuwen (sandervl@xs4all.nl)
9 *
10 * Project Odin Software License can be found in LICENSE.TXT
11 *
12 */
13
14/*****************************************************************************
15 * Includes *
16 *****************************************************************************/
17
18#include <odin.h>
19#include <odinwrap.h>
20#include <os2sel.h>
21
22#include <os2win.h>
23#include <stdarg.h>
24#include <string.h>
25#include "thread.h"
26#include <misc.h>
27#include <cpuhlp.h>
28#include <wprocess.h>
29#include <windllbase.h>
30#include <winexebase.h>
31#include "exceptutil.h"
32#include "oslibmisc.h"
33#include "oslibdos.h"
34#include <handlemanager.h>
35#include <codepage.h>
36
37#include <FastInfoBlocks.h>
38
39#define DBG_LOCALLOG DBG_thread
40#include "dbglocal.h"
41
42ODINDEBUGCHANNEL(KERNEL32-THREAD)
43
44static ULONG priorityclass = NORMAL_PRIORITY_CLASS;
45
46//******************************************************************************
47//******************************************************************************
48DWORD WIN32API GetCurrentThreadId()
49{
50 // check cached identifier
51 TEB *teb = GetThreadTEB();
52 if(teb != NULL && teb->o.odin.threadId != 0xFFFFFFFF)
53 {
54 // this is set in InitializeTIB() already.
55 return teb->o.odin.threadId;
56 }
57
58//// dprintf(("GetCurrentThreadId\n"));
59 return MAKE_THREADID(O32_GetCurrentProcessId(), O32_GetCurrentThreadId());
60}
61//******************************************************************************
62//******************************************************************************
63HANDLE WIN32API GetCurrentThread()
64{
65 TEB *teb;
66
67 teb = GetThreadTEB();
68 if(teb == 0) {
69 DebugInt3();
70 SetLastError(ERROR_INVALID_HANDLE); //todo
71 return 0;
72 }
73 return teb->o.odin.hThread;
74}
75//******************************************************************************
76// these two debugging functions allow access to a
77// calldepth counter inside the TEB block of each thread
78//******************************************************************************
79ULONG WIN32API dbg_GetThreadCallDepth()
80{
81#ifdef DEBUG
82 TEB *teb;
83
84 teb = GetThreadTEB();
85 if(teb == NULL)
86 return 0;
87 else
88 return teb->o.odin.dbgCallDepth;
89#else
90 return 0;
91#endif
92}
93//******************************************************************************
94//******************************************************************************
95void WIN32API dbg_IncThreadCallDepth()
96{
97#ifdef DEBUG
98 TEB *teb;
99
100 teb = GetThreadTEB();
101 if(teb != NULL)
102 teb->o.odin.dbgCallDepth++;
103#endif
104}
105//******************************************************************************
106#define MAX_CALLSTACK_SIZE 128
107#ifdef DEBUG
108static char *pszLastCaller = NULL;
109#endif
110//******************************************************************************
111void WIN32API dbg_ThreadPushCall(char *pszCaller)
112{
113#ifdef DEBUG
114 TEB *teb;
115
116 // embedded dbg_IncThreadCallDepth
117 teb = GetThreadTEB();
118 if(teb == NULL)
119 return;
120
121 // add caller name to call stack trace
122 int iIndex = teb->o.odin.dbgCallDepth;
123
124 // allocate callstack on demand
125 if (teb->o.odin.arrstrCallStack == NULL)
126 teb->o.odin.arrstrCallStack = (PVOID*)malloc( sizeof(LPSTR) * MAX_CALLSTACK_SIZE);
127
128 // insert entry
129 if (iIndex < MAX_CALLSTACK_SIZE)
130 teb->o.odin.arrstrCallStack[iIndex] = (PVOID)pszCaller;
131
132 teb->o.odin.dbgCallDepth++;
133
134 pszLastCaller = pszCaller;
135#endif
136}
137//******************************************************************************
138//******************************************************************************
139void WIN32API dbg_DecThreadCallDepth()
140{
141#ifdef DEBUG
142 TEB *teb;
143
144 teb = GetThreadTEB();
145 if(teb != NULL)
146 --(teb->o.odin.dbgCallDepth);
147#endif
148}
149//******************************************************************************
150//******************************************************************************
151void WIN32API dbg_ThreadPopCall()
152{
153#ifdef DEBUG
154 TEB *teb;
155
156 // embedded dbg_DecThreadCallDepth
157 teb = GetThreadTEB();
158 if(teb == NULL)
159 return;
160
161 --(teb->o.odin.dbgCallDepth);
162
163 // add caller name to call stack trace
164 int iIndex = teb->o.odin.dbgCallDepth;
165
166 // insert entry
167 if (teb->o.odin.arrstrCallStack)
168 if (iIndex < MAX_CALLSTACK_SIZE)
169 teb->o.odin.arrstrCallStack[iIndex] = NULL;
170#endif
171}
172//******************************************************************************
173//******************************************************************************
174char* WIN32API dbg_GetLastCallerName()
175{
176#ifdef DEBUG
177 // retrieve last caller name from stack
178 TEB *teb;
179
180 // embedded dbg_DecThreadCallDepth
181 teb = GetThreadTEB();
182 if(teb != NULL)
183 {
184 int iIndex = teb->o.odin.dbgCallDepth - 1;
185 if ( (iIndex > 0) &&
186 (iIndex < MAX_CALLSTACK_SIZE) )
187 {
188 return (char*)teb->o.odin.arrstrCallStack[iIndex];
189 }
190 }
191#endif
192
193 return NULL;
194}
195//******************************************************************************
196//******************************************************************************
197VOID WIN32API ExitThread(DWORD exitcode)
198{
199 EXCEPTION_FRAME *exceptFrame;
200 TEB *teb;
201
202 dprintf(("ExitThread %x (%x)", GetCurrentThread(), exitcode));
203
204 teb = GetThreadTEB();
205 if(teb != 0) {
206 exceptFrame = (EXCEPTION_FRAME *)teb->o.odin.exceptFrame;
207 }
208 else DebugInt3();
209
210 HMSetThreadTerminated(GetCurrentThread());
211 Win32DllBase::detachThreadFromAllDlls(); //send DLL_THREAD_DETACH message to all dlls
212 Win32DllBase::tlsDetachThreadFromAllDlls(); //destroy TLS structures of all dlls
213 if(WinExe) WinExe->tlsDetachThread(); //destroy TLS structure of main exe
214
215 if(teb) DestroyTEB(teb);
216
217 if(exceptFrame) OS2UnsetExceptionHandler((void *)exceptFrame);
218
219 O32_ExitThread(exitcode);
220}
221/*****************************************************************************
222 * Name : DWORD SetThreadAffinityMask
223 * Purpose : The SetThreadAffinityMask function sets a processor affinity
224 * mask for a specified thread.
225 * A thread affinity mask is a bit vector in which each bit
226 * represents the processors that a thread is allowed to run on.
227 * A thread affinity mask must be a proper subset of the process
228 * affinity mask for the containing process of a thread. A thread
229 * is only allowed to run on the processors its process is allowed to run on.
230 * Parameters: HANDLE hThread handle to the thread of interest
231 * DWORD dwThreadAffinityMask a thread affinity mask
232 * Variables :
233 * Result : TRUE / FALSE
234 * Remark :
235 * Status : Fully functional
236 *
237 * Author : SvL
238 *****************************************************************************/
239
240DWORD WIN32API SetThreadAffinityMask(HANDLE hThread,
241 DWORD dwThreadAffinityMask)
242{
243 dprintf(("KERNEL32: SetThreadAffinityMask(%08xh,%08xh)", hThread, dwThreadAffinityMask));
244
245 if(hThread != GetCurrentThread()) {
246 dprintf(("WARNING: Setting the affinity mask for another thread than the current one is not supported!!"));
247 return FALSE;
248 }
249 return OSLibDosSetThreadAffinity(dwThreadAffinityMask);
250}
251//******************************************************************************
252//******************************************************************************
253VOID WIN32API Sleep(DWORD mSecs)
254{
255 dprintf2(("KERNEL32: Sleep %d", mSecs));
256 OSLibDosSleep(mSecs);
257}
258//******************************************************************************
259//******************************************************************************
260DWORD WIN32API GetPriorityClass(HANDLE hProcess)
261{
262 dprintf(("KERNEL32: GetPriorityClass %x", hProcess));
263 return priorityclass;
264// return O32_GetPriorityClass(hProcess);
265}
266//******************************************************************************
267//******************************************************************************
268BOOL WIN32API SetPriorityClass(HANDLE hProcess, DWORD dwPriority)
269{
270 dprintf(("KERNEL32: SetPriorityClass %x %x", hProcess, dwPriority));
271 priorityclass = dwPriority;
272 return TRUE;
273// return O32_SetPriorityClass(hProcess, dwPriority);
274}
275//******************************************************************************
276//******************************************************************************
277Win32Thread::Win32Thread(LPTHREAD_START_ROUTINE pUserCallback, LPVOID lpData, DWORD dwFlags, HANDLE hThread)
278{
279 lpUserData = lpData;
280 pCallback = pUserCallback;
281 this->dwFlags = dwFlags;
282 this->hThread = hThread;
283
284 teb = CreateTEB(hThread, 0xFFFFFFFF);
285 if(teb == NULL) {
286 DebugInt3();
287 }
288}
289//******************************************************************************
290//******************************************************************************
291DWORD OPEN32API Win32ThreadProc(LPVOID lpData)
292{
293 EXCEPTION_FRAME exceptFrame;
294 Win32Thread *me = (Win32Thread *)lpData;
295 ULONG threadCallback = (ULONG)me->pCallback;
296 LPVOID userdata = me->lpUserData;
297 DWORD rc;
298 TEB *winteb = (TEB *)me->teb;
299
300 delete(me); //only called once
301
302 if(InitializeThread(winteb) == FALSE) {
303 dprintf(("Win32ThreadProc: InitializeTIB failed!!"));
304 DebugInt3();
305 return 0;
306 }
307 dprintf(("Win32ThreadProc: Thread handle 0x%x, thread id %d", GetCurrentThread(), GetCurrentThreadId()));
308
309 winteb->flags = me->dwFlags;
310
311 winteb->entry_point = (void *)threadCallback;
312 winteb->entry_arg = (void *)userdata;
313
314 winteb->o.odin.hab = OSLibWinInitialize();
315 dprintf(("Thread HAB %x", winteb->o.odin.hab));
316 winteb->o.odin.hmq = OSLibWinQueryMsgQueue(winteb->o.odin.hab);
317 rc = OSLibWinSetCp(winteb->o.odin.hmq, GetDisplayCodepage());
318 dprintf(("WinSetCP was %sOK", rc ? "" : "not "));
319
320 dprintf(("Win32ThreadProc: hab %x hmq %x", winteb->o.odin.hab, winteb->o.odin.hmq));
321 dprintf(("Stack top 0x%x, stack end 0x%x", winteb->stack_top, winteb->stack_low));
322
323 //Note: The Win32 exception structure referenced by FS:[0] is the same
324 // in OS/2
325 OS2SetExceptionHandler((void *)&exceptFrame);
326 winteb->o.odin.exceptFrame = (ULONG)&exceptFrame;
327
328 //Determine if thread callback is inside a PE dll; if true, then force
329 //switch to win32 TIB (FS selector)
330 //(necessary for Opera when loading win32 plugins that create threads)
331 Win32DllBase *dll;
332 dll = Win32DllBase::findModuleByAddr(threadCallback);
333 if(dll && dll->isPEImage()) {
334 dprintf(("Win32ThreadProc: Force win32 TIB switch"));
335 SetWin32TIB(TIB_SWITCH_FORCE_WIN32);
336 }
337 else SetWin32TIB(TIB_SWITCH_DEFAULT); //executable type determines whether or not FS is changed
338
339 DWORD dwProcessAffinityMask, dwSystemAffinityMask;
340
341 //Change the affinity mask of this thread to the mask for the whole process
342 if(GetProcessAffinityMask(GetCurrentProcess(), &dwProcessAffinityMask, &dwSystemAffinityMask) == TRUE) {
343 SetThreadAffinityMask(GetCurrentThread(), dwProcessAffinityMask);
344 }
345
346 if(WinExe) WinExe->tlsAttachThread(); //setup TLS structure of main exe
347 Win32DllBase::tlsAttachThreadToAllDlls(); //setup TLS structures of all dlls
348 Win32DllBase::attachThreadToAllDlls(); //send DLL_THREAD_ATTACH message to all dlls
349
350 //Set FPU control word to 0x27F (same as in NT)
351 CONTROL87(0x27F, 0xFFF);
352 rc = AsmCallThreadHandler(threadCallback, userdata);
353
354 if(fExitProcess) {
355 OSLibDosExitThread(rc);
356 }
357 else {
358 HMSetThreadTerminated(GetCurrentThread());
359 winteb->o.odin.exceptFrame = 0;
360 Win32DllBase::detachThreadFromAllDlls(); //send DLL_THREAD_DETACH message to all dlls
361 Win32DllBase::tlsDetachThreadFromAllDlls(); //destroy TLS structures of all dlls
362 if(WinExe) WinExe->tlsDetachThread(); //destroy TLS structure of main exe
363 DestroyTEB(winteb); //destroys TIB and restores FS
364 OS2UnsetExceptionHandler((void *)&exceptFrame);
365 }
366
367 return rc;
368}
369//******************************************************************************
370//******************************************************************************
371
372/**
373 * Enter odin context with this thread.
374 *
375 * Is called when an OS/2 process is calling into an Odin32 DLL.
376 * This may be called also in a nested fashion and supports that.
377 * The conterpart of ODIN_ThreadLeaveOdinContext().
378 *
379 * @returns The old FS selector.
380 * @returns 0 if TEB creation failed.
381 * @param pExceptionRegRec OS/2 Exception Registration Record (2 ULONGs)
382 * must be located on the callers stack.
383 * @param fForceFSSwitch If set we will force switching to Odin32 FS selector.
384 * If clear it depends on defaults.
385 */
386USHORT WIN32API ODIN_ThreadEnterOdinContext(void *pExceptionRegRec, BOOL fForceFSSwitch)
387{
388 USHORT selFSOld = 0;
389
390 /*
391 * Get TEB pointer, create it if necessary.
392 * @todo Check if this really is the thread which the TEB was created
393 * for. If not create the TEB.
394 */
395 TEB *pTeb = GetThreadTEB();
396 if (!pTeb)
397 {
398 BOOL fMainThread = fibGetTid() == 1;
399 HANDLE hThreadMain = HMCreateThread(NULL, 0, 0, 0, 0, 0, fMainThread);
400 pTeb = CreateTEB(hThreadMain, fibGetTid());
401 if (!pTeb || InitializeThread(pTeb, fMainThread) == FALSE)
402 {
403 dprintf(("ODIN_ThreadEnterOdinContext: Failed to create TEB!"));
404 }
405 }
406
407 /*
408 * Install the Odin32 exception handler.
409 * Note: The Win32 exception structure referenced by FS:[0] is the same in OS/2
410 */
411 if (pExceptionRegRec)
412 OS2SetExceptionHandler(pExceptionRegRec);
413 if ( pTeb
414 && !pTeb->o.odin.exceptFrame) /* if allready present, we'll keep the first one. */
415 pTeb->o.odin.exceptFrame = (ULONG)pExceptionRegRec;
416
417 /*
418 * Switch Selector if TIB was created.
419 */
420 if (pTeb)
421 selFSOld = SetWin32TIB(fForceFSSwitch ? TIB_SWITCH_FORCE_WIN32 : TIB_SWITCH_DEFAULT);
422
423 return selFSOld;
424}
425
426
427/**
428 * Leave odin context with this thread.
429 *
430 * Is called when an OS/2 process is returning from an Odin32 DLL.
431 * This may be called also in a nested fashion and supports that.
432 * The conterpart of ODIN_ThreadEnterOdinContext().
433 *
434 * @returns The old FS selector.
435 * @returns 0 if TEB creation failed.
436 * @param pExceptionRegRec OS/2 Exception Registration Record (2 ULONGs)
437 * must be located on the callers stack.
438 * @param fForceFSSwitch If set we will force switching to Odin32 FS selector.
439 * If clear it depends on defaults.
440 */
441void WIN32API ODIN_ThreadLeaveOdinContext(void *pExceptionRegRec, USHORT selFSOld)
442{
443 /*
444 * Install the Odin32 exception handler.
445 * Note: The Win32 exception structure referenced by FS:[0] is the same in OS/2
446 */
447 if (pExceptionRegRec)
448 OS2UnsetExceptionHandler(pExceptionRegRec);
449 TEB *pTeb = GetThreadTEB();
450 if ( pTeb
451 && pTeb->o.odin.exceptFrame == (ULONG)pExceptionRegRec)
452 pTeb->o.odin.exceptFrame = 0;
453
454 /*
455 * Switch Back FS Selector.
456 */
457 if (selFSOld)
458 SetFS(selFSOld);
459}
460
461
462/**
463 * Leave odin context to call back into OS/2 code.
464 *
465 * Is called when and Odin32 Dll/Exe calls back into the OS/2 code.
466 * The conterpart of ODIN_ThreadEnterOdinContextNested().
467 *
468 * @returns The old FS selector.
469 * @returns 0 on failure.
470 * @param pExceptionRegRec New OS/2 exception handler frame which are to be registered
471 * before the Odin handler in the chain.
472 * Must be located on the callers stack.
473 * @param fRemoveOdinExcpt Remove the odin exception handler.
474 */
475USHORT WIN32API ODIN_ThreadLeaveOdinContextNested(void *pExceptionRegRec, BOOL fRemoveOdinExcpt)
476{
477 /*
478 * Set OS/2 FS Selector.
479 */
480 USHORT selFSOld = RestoreOS2FS();
481
482 /*
483 * Remove the Odin exception handler (if requested).
484 */
485 if (fRemoveOdinExcpt)
486 {
487 TEB *pTeb = GetThreadTEB();
488 if (pTeb)
489 OS2UnsetExceptionHandler((void*)pTeb->o.odin.exceptFrame);
490 /* else: no TEB created propbably no exception handler to remove. */
491 }
492
493 /*
494 * Change exception handler if required.
495 */
496 if (pExceptionRegRec)
497 {
498 extern unsigned long _System DosSetExceptionHandler(void *);
499 DosSetExceptionHandler(pExceptionRegRec);
500 }
501
502 return selFSOld;
503}
504
505
506/**
507 * Re-enter Odin context after being back in OS/2 code.
508 *
509 * Is called when returning to Odin from OS/2 code.
510 * The conterpart of ODIN_ThreadLeaveOdinContextNested().
511 *
512 * @param pExceptionRegRec The exception registration record for the OS/2
513 * exception handler used with Nested Leave. NULL
514 * if not used.
515 * @param fRestoreOdinExcpt Restore the Odin exception handler.
516 * This flag must not be set unless fRemoveOdinExcpt
517 * was set when leaving the Odin context!
518 * @param selFSOld The Odin FS selector returned by the Nested Leave api.
519 *
520 */
521void WIN32API ODIN_ThreadEnterOdinContextNested(void *pExceptionRegRec, BOOL fRestoreOdinExcpt, USHORT selFSOld)
522{
523 /*
524 * Remove the exception handler registered in ODIN_ThreadLeaveOdinContextNested
525 */
526 if (pExceptionRegRec)
527 OS2UnsetExceptionHandler(pExceptionRegRec);
528
529 /*
530 * Restore Odin exception handler (if requested).
531 */
532 if (fRestoreOdinExcpt)
533 {
534 TEB *pTeb = GetThreadTEB();
535 if (pTeb)
536 OS2SetExceptionHandler((void*)pTeb->o.odin.exceptFrame);
537 }
538
539 /*
540 * Switch Back FS Selector.
541 */
542 if (selFSOld)
543 SetFS(selFSOld);
544}
545
546
Note: See TracBrowser for help on using the repository browser.