VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/win/thread-win.cpp@ 96124

Last change on this file since 96124 was 96051, checked in by vboxsync, 2 years ago

IPRT/thread-win.cpp: Set the TLS entry to NULL earlier.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 18.7 KB
Line 
1/* $Id: thread-win.cpp 96051 2022-08-05 10:30:49Z vboxsync $ */
2/** @file
3 * IPRT - Threads, Windows.
4 */
5
6/*
7 * Copyright (C) 2006-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_THREAD
32#include <iprt/nt/nt-and-windows.h>
33
34#ifndef IPRT_NO_CRT
35# include <errno.h>
36# include <process.h>
37#endif
38
39#include <iprt/thread.h>
40#include "internal/iprt.h"
41
42#include <iprt/asm-amd64-x86.h>
43#include <iprt/assert.h>
44#include <iprt/cpuset.h>
45#include <iprt/err.h>
46#include <iprt/ldr.h>
47#include <iprt/log.h>
48#include <iprt/mem.h>
49#include <iprt/param.h>
50#include "internal/thread.h"
51#include "internal-r3-win.h"
52
53
54/*********************************************************************************************************************************
55* Structures and Typedefs *
56*********************************************************************************************************************************/
57/** SetThreadDescription */
58typedef HRESULT (WINAPI *PFNSETTHREADDESCRIPTION)(HANDLE hThread, WCHAR *pwszName); /* Since W10 1607 */
59
60/** CoInitializeEx */
61typedef HRESULT (WINAPI *PFNCOINITIALIZEEX)(LPVOID, DWORD);
62/** CoUninitialize */
63typedef void (WINAPI *PFNCOUNINITIALIZE)(void);
64/** OleUninitialize */
65typedef void (WINAPI *PFNOLEUNINITIALIZE)(void);
66
67
68
69/*********************************************************************************************************************************
70* Global Variables *
71*********************************************************************************************************************************/
72/** The TLS index allocated for storing the RTTHREADINT pointer. */
73static DWORD g_dwSelfTLS = TLS_OUT_OF_INDEXES;
74/** Pointer to SetThreadDescription (KERNEL32.DLL) if available. */
75static PFNSETTHREADDESCRIPTION g_pfnSetThreadDescription = NULL;
76
77/** Pointer to CoInitializeEx (OLE32.DLL / combase.dll) if available. */
78static PFNCOINITIALIZEEX volatile g_pfnCoInitializeEx = NULL;
79/** Pointer to CoUninitialize (OLE32.DLL / combase.dll) if available. */
80static PFNCOUNINITIALIZE volatile g_pfnCoUninitialize = NULL;
81/** Pointer to OleUninitialize (OLE32.DLL / combase.dll) if available. */
82static PFNOLEUNINITIALIZE volatile g_pfnOleUninitialize = NULL;
83
84
85/*********************************************************************************************************************************
86* Internal Functions *
87*********************************************************************************************************************************/
88static void rtThreadWinTellDebuggerThreadName(uint32_t idThread, const char *pszName);
89DECLINLINE(void) rtThreadWinSetThreadName(PRTTHREADINT pThread, DWORD idThread);
90
91
92DECLHIDDEN(int) rtThreadNativeInit(void)
93{
94 g_dwSelfTLS = TlsAlloc();
95 if (g_dwSelfTLS == TLS_OUT_OF_INDEXES)
96 return VERR_NO_TLS_FOR_SELF;
97
98 g_pfnSetThreadDescription = (PFNSETTHREADDESCRIPTION)GetProcAddress(g_hModKernel32, "SetThreadDescription");
99 return VINF_SUCCESS;
100}
101
102
103DECLHIDDEN(void) rtThreadNativeReInitObtrusive(void)
104{
105 /* nothing to do here. */
106}
107
108
109DECLHIDDEN(void) rtThreadNativeDetach(void)
110{
111 /*
112 * Deal with alien threads.
113 */
114 PRTTHREADINT pThread = (PRTTHREADINT)TlsGetValue(g_dwSelfTLS);
115 if ( pThread
116 && (pThread->fIntFlags & RTTHREADINT_FLAGS_ALIEN))
117 {
118 rtThreadTerminate(pThread, 0);
119 TlsSetValue(g_dwSelfTLS, NULL);
120 }
121}
122
123
124DECLHIDDEN(void) rtThreadNativeDestroy(PRTTHREADINT pThread)
125{
126 if (pThread == (PRTTHREADINT)TlsGetValue(g_dwSelfTLS))
127 TlsSetValue(g_dwSelfTLS, NULL);
128
129 if ((HANDLE)pThread->hThread != INVALID_HANDLE_VALUE)
130 {
131 CloseHandle((HANDLE)pThread->hThread);
132 pThread->hThread = (uintptr_t)INVALID_HANDLE_VALUE;
133 }
134}
135
136
137DECLHIDDEN(int) rtThreadNativeAdopt(PRTTHREADINT pThread)
138{
139 if (!TlsSetValue(g_dwSelfTLS, pThread))
140 return VERR_FAILED_TO_SET_SELF_TLS;
141 rtThreadWinSetThreadName(pThread, GetCurrentThreadId());
142 return VINF_SUCCESS;
143}
144
145
146DECLHIDDEN(void) rtThreadNativeInformDebugger(PRTTHREADINT pThread)
147{
148 rtThreadWinTellDebuggerThreadName((uint32_t)(uintptr_t)pThread->Core.Key, pThread->szName);
149}
150
151
152/**
153 * Communicates the thread name to the debugger, if we're begin debugged that
154 * is.
155 *
156 * See http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx for debugger
157 * interface details.
158 *
159 * @param idThread The thread ID. UINT32_MAX for current thread.
160 * @param pszName The name.
161 */
162static void rtThreadWinTellDebuggerThreadName(uint32_t idThread, const char *pszName)
163{
164 struct
165 {
166 uint32_t uType;
167 const char *pszName;
168 uint32_t idThread;
169 uint32_t fFlags;
170 } Pkg = { 0x1000, pszName, idThread, 0 };
171 __try
172 {
173 RaiseException(0x406d1388, 0, sizeof(Pkg)/sizeof(ULONG_PTR), (ULONG_PTR *)&Pkg);
174 }
175 __except(EXCEPTION_CONTINUE_EXECUTION)
176 {
177
178 }
179}
180
181
182/**
183 * Sets the thread name as best as we can.
184 */
185DECLINLINE(void) rtThreadWinSetThreadName(PRTTHREADINT pThread, DWORD idThread)
186{
187 if (IsDebuggerPresent())
188 rtThreadWinTellDebuggerThreadName(idThread, &pThread->szName[0]);
189
190 /* The SetThreadDescription API introduced in windows 10 1607 / server 2016
191 allows setting the thread name while the debugger isn't attached. Works
192 with WinDbgX, VisualStudio 2017 v15.6+, and presumeably some recent windbg
193 version. */
194 if (g_pfnSetThreadDescription)
195 {
196 /* The name should be ASCII, so we just need to expand 'char' to 'WCHAR'. */
197 WCHAR wszName[RTTHREAD_NAME_LEN];
198 for (size_t i = 0; i < RTTHREAD_NAME_LEN; i++)
199 wszName[i] = pThread->szName[i];
200
201 HRESULT hrc = g_pfnSetThreadDescription(GetCurrentThread(), wszName);
202 Assert(SUCCEEDED(hrc)); RT_NOREF(hrc);
203 }
204}
205
206
207/**
208 * Bitch about dangling COM and OLE references, dispose of them
209 * afterwards so we don't end up deadlocked somewhere below
210 * OLE32!DllMain.
211 */
212static void rtThreadNativeUninitComAndOle(void)
213{
214#if 1 /* experimental code */
215 /*
216 * Read the counters.
217 */
218 struct MySOleTlsData
219 {
220 void *apvReserved0[2]; /**< x86=0x00 W7/64=0x00 */
221 DWORD adwReserved0[3]; /**< x86=0x08 W7/64=0x10 */
222 void *apvReserved1[1]; /**< x86=0x14 W7/64=0x20 */
223 DWORD cComInits; /**< x86=0x18 W7/64=0x28 */
224 DWORD cOleInits; /**< x86=0x1c W7/64=0x2c */
225 DWORD dwReserved1; /**< x86=0x20 W7/64=0x30 */
226 void *apvReserved2[4]; /**< x86=0x24 W7/64=0x38 */
227 DWORD adwReserved2[1]; /**< x86=0x34 W7/64=0x58 */
228 void *pvCurrentCtx; /**< x86=0x38 W7/64=0x60 */
229 IUnknown *pCallState; /**< x86=0x3c W7/64=0x68 */
230 } *pOleTlsData = NULL; /* outside the try/except for debugging */
231 DWORD cComInits = 0;
232 DWORD cOleInits = 0;
233 __try
234 {
235 void *pvTeb = NtCurrentTeb();
236# ifdef RT_ARCH_AMD64
237 pOleTlsData = *(struct MySOleTlsData **)((uintptr_t)pvTeb + 0x1758); /*TEB.ReservedForOle*/
238# elif RT_ARCH_X86
239 pOleTlsData = *(struct MySOleTlsData **)((uintptr_t)pvTeb + 0x0f80); /*TEB.ReservedForOle*/
240# else
241# error "Port me!"
242# endif
243 if (pOleTlsData)
244 {
245 cComInits = pOleTlsData->cComInits;
246 cOleInits = pOleTlsData->cOleInits;
247 }
248 }
249 __except(EXCEPTION_EXECUTE_HANDLER)
250 {
251 AssertFailedReturnVoid();
252 }
253
254 /*
255 * Assert sanity. If any of these breaks, the structure layout above is
256 * probably not correct any longer.
257 */
258 AssertMsgReturnVoid(cComInits < 1000, ("%u (%#x)\n", cComInits, cComInits));
259 AssertMsgReturnVoid(cOleInits < 1000, ("%u (%#x)\n", cOleInits, cOleInits));
260 AssertMsgReturnVoid(cComInits >= cOleInits, ("cComInits=%#x cOleInits=%#x\n", cComInits, cOleInits));
261
262 /*
263 * Do the uninitializing.
264 */
265 if (cComInits)
266 {
267 AssertMsgFailed(("cComInits=%u (%#x) cOleInits=%u (%#x) - dangling COM/OLE inits!\n",
268 cComInits, cComInits, cOleInits, cOleInits));
269
270 PFNOLEUNINITIALIZE pfnOleUninitialize = g_pfnOleUninitialize;
271 PFNCOUNINITIALIZE pfnCoUninitialize = g_pfnCoUninitialize;
272 if (pfnCoUninitialize && pfnOleUninitialize)
273 { /* likely */ }
274 else
275 {
276 HMODULE hOle32 = GetModuleHandle("ole32.dll");
277 AssertReturnVoid(hOle32 != NULL);
278
279 pfnOleUninitialize = (PFNOLEUNINITIALIZE)GetProcAddress(hOle32, "OleUninitialize");
280 AssertReturnVoid(pfnOleUninitialize);
281
282 pfnCoUninitialize = (PFNCOUNINITIALIZE)GetProcAddress(hOle32, "CoUninitialize");
283 AssertReturnVoid(pfnCoUninitialize);
284 }
285
286 while (cOleInits-- > 0)
287 {
288 pfnOleUninitialize();
289 cComInits--;
290 }
291
292 while (cComInits-- > 0)
293 pfnCoUninitialize();
294 }
295#endif
296}
297
298
299/**
300 * Implements the RTTHREADFLAGS_COM_MTA and RTTHREADFLAGS_COM_STA flags.
301 *
302 * @returns true if COM uninitialization should be done, false if not.
303 * @param fFlags The thread flags.
304 */
305static bool rtThreadNativeWinCoInitialize(unsigned fFlags)
306{
307 /*
308 * Resolve the ole32 init and uninit functions dynamically.
309 */
310 PFNCOINITIALIZEEX pfnCoInitializeEx = g_pfnCoInitializeEx;
311 PFNCOUNINITIALIZE pfnCoUninitialize = g_pfnCoUninitialize;
312 if (pfnCoInitializeEx && pfnCoUninitialize)
313 { /* likely */ }
314 else
315 {
316 RTLDRMOD hModOle32 = NIL_RTLDRMOD;
317 int rc = RTLdrLoadSystem("ole32.dll", true /*fNoUnload*/, &hModOle32);
318 AssertRCReturn(rc, false);
319
320 PFNOLEUNINITIALIZE pfnOleUninitialize;
321 pfnOleUninitialize = (PFNOLEUNINITIALIZE)RTLdrGetFunction(hModOle32, "OleUninitialize");
322 pfnCoUninitialize = (PFNCOUNINITIALIZE )RTLdrGetFunction(hModOle32, "CoUninitialize");
323 pfnCoInitializeEx = (PFNCOINITIALIZEEX )RTLdrGetFunction(hModOle32, "CoInitializeEx");
324
325 RTLdrClose(hModOle32);
326 AssertReturn(pfnCoInitializeEx && pfnCoUninitialize, false);
327
328 if (pfnOleUninitialize && !g_pfnOleUninitialize)
329 g_pfnOleUninitialize = pfnOleUninitialize;
330 g_pfnCoInitializeEx = pfnCoInitializeEx;
331 g_pfnCoUninitialize = pfnCoUninitialize;
332 }
333
334 /*
335 * Do the initializating.
336 */
337 DWORD fComInit;
338 if (fFlags & RTTHREADFLAGS_COM_MTA)
339 fComInit = COINIT_MULTITHREADED | COINIT_SPEED_OVER_MEMORY | COINIT_DISABLE_OLE1DDE;
340 else
341 fComInit = COINIT_APARTMENTTHREADED | COINIT_SPEED_OVER_MEMORY;
342 HRESULT hrc = pfnCoInitializeEx(NULL, fComInit);
343 AssertMsg(SUCCEEDED(hrc), ("%Rhrc fComInit=%#x\n", hrc, fComInit));
344 return SUCCEEDED(hrc);
345}
346
347
348/**
349 * Wrapper which unpacks the param stuff and calls thread function.
350 */
351#ifndef IPRT_NO_CRT
352static unsigned __stdcall rtThreadNativeMain(void *pvArgs) RT_NOTHROW_DEF
353#else
354static DWORD __stdcall rtThreadNativeMain(void *pvArgs) RT_NOTHROW_DEF
355#endif
356{
357 DWORD dwThreadId = GetCurrentThreadId();
358 PRTTHREADINT pThread = (PRTTHREADINT)pvArgs;
359
360 if (!TlsSetValue(g_dwSelfTLS, pThread))
361 AssertReleaseMsgFailed(("failed to set self TLS. lasterr=%d thread '%s'\n", GetLastError(), pThread->szName));
362 rtThreadWinSetThreadName(pThread, dwThreadId);
363
364 bool fUninitCom = (pThread->fFlags & (RTTHREADFLAGS_COM_MTA | RTTHREADFLAGS_COM_STA)) != 0;
365 if (fUninitCom)
366 fUninitCom = rtThreadNativeWinCoInitialize(pThread->fFlags);
367
368 int rc = rtThreadMain(pThread, dwThreadId, &pThread->szName[0]);
369
370 TlsSetValue(g_dwSelfTLS, NULL); /* rtThreadMain already released the structure. */
371
372 if (fUninitCom && g_pfnCoUninitialize)
373 g_pfnCoUninitialize();
374
375 rtThreadNativeUninitComAndOle();
376#ifndef IPRT_NO_CRT
377 _endthreadex(rc);
378 return rc; /* not reached */
379#else
380 for (;;)
381 ExitThread(rc);
382#endif
383}
384
385
386DECLHIDDEN(int) rtThreadNativeCreate(PRTTHREADINT pThread, PRTNATIVETHREAD pNativeThread)
387{
388 AssertReturn(pThread->cbStack < ~(unsigned)0, VERR_INVALID_PARAMETER);
389
390 /*
391 * If a stack size is given, make sure it's not a multiple of 64KB so that we
392 * get one or more pages for overflow protection. (ASSUMES 64KB alloc align.)
393 */
394 unsigned cbStack = (unsigned)pThread->cbStack;
395 if (cbStack > 0 && RT_ALIGN_T(cbStack, _64K, unsigned) == cbStack)
396 cbStack += PAGE_SIZE;
397
398 /*
399 * Create the thread.
400 */
401 pThread->hThread = (uintptr_t)INVALID_HANDLE_VALUE;
402#ifndef IPRT_NO_CRT
403 unsigned uThreadId = 0;
404 uintptr_t hThread = _beginthreadex(NULL /*pSecAttrs*/, cbStack, rtThreadNativeMain, pThread, 0 /*fFlags*/, &uThreadId);
405 if (hThread != 0 && hThread != ~0U)
406 {
407 pThread->hThread = hThread;
408 *pNativeThread = uThreadId;
409 return VINF_SUCCESS;
410 }
411 return RTErrConvertFromErrno(errno);
412#else
413 DWORD idThread = 0;
414 HANDLE hThread = CreateThread(NULL /*pSecAttrs*/, cbStack, rtThreadNativeMain, pThread, 0 /*fFlags*/, &idThread);
415 if (hThread != NULL)
416 {
417 pThread->hThread = (uintptr_t)hThread;
418 *pNativeThread = idThread;
419 return VINF_SUCCESS;
420 }
421 return RTErrConvertFromWin32(GetLastError());
422#endif
423}
424
425
426DECLHIDDEN(bool) rtThreadNativeIsAliveKludge(PRTTHREADINT pThread)
427{
428 PPEB_COMMON pPeb = NtCurrentPeb();
429 if (!pPeb || !pPeb->Ldr || !pPeb->Ldr->ShutdownInProgress)
430 return true;
431 DWORD rcWait = WaitForSingleObject((HANDLE)pThread->hThread, 0);
432 return rcWait != WAIT_OBJECT_0;
433}
434
435
436RTDECL(RTTHREAD) RTThreadSelf(void)
437{
438 PRTTHREADINT pThread = (PRTTHREADINT)TlsGetValue(g_dwSelfTLS);
439 /** @todo import alien threads ? */
440 return pThread;
441}
442
443
444#if 0 /* noone is using this ... */
445/**
446 * Returns the processor number the current thread was running on during this call
447 *
448 * @returns processor nr
449 */
450static int rtThreadGetCurrentProcessorNumber(void)
451{
452 static bool fInitialized = false;
453 static DWORD (WINAPI *pfnGetCurrentProcessorNumber)(void) = NULL;
454 if (!fInitialized)
455 {
456 HMODULE hmodKernel32 = GetModuleHandle("kernel32.dll");
457 if (hmodKernel32)
458 pfnGetCurrentProcessorNumber = (DWORD (WINAPI*)(void))GetProcAddress(hmodKernel32, "GetCurrentProcessorNumber");
459 fInitialized = true;
460 }
461 if (pfnGetCurrentProcessorNumber)
462 return pfnGetCurrentProcessorNumber();
463 return -1;
464}
465#endif
466
467
468RTR3DECL(int) RTThreadSetAffinity(PCRTCPUSET pCpuSet)
469{
470 DWORD_PTR fNewMask = pCpuSet ? RTCpuSetToU64(pCpuSet) : ~(DWORD_PTR)0;
471 DWORD_PTR dwRet = SetThreadAffinityMask(GetCurrentThread(), fNewMask);
472 if (dwRet)
473 return VINF_SUCCESS;
474
475 int iLastError = GetLastError();
476 AssertMsgFailed(("SetThreadAffinityMask failed, LastError=%d\n", iLastError));
477 return RTErrConvertFromWin32(iLastError);
478}
479
480
481RTR3DECL(int) RTThreadGetAffinity(PRTCPUSET pCpuSet)
482{
483 /*
484 * Haven't found no query api, but the set api returns the old mask, so let's use that.
485 */
486 DWORD_PTR dwIgnored;
487 DWORD_PTR dwProcAff = 0;
488 if (GetProcessAffinityMask(GetCurrentProcess(), &dwProcAff, &dwIgnored))
489 {
490 HANDLE hThread = GetCurrentThread();
491 DWORD_PTR dwRet = SetThreadAffinityMask(hThread, dwProcAff);
492 if (dwRet)
493 {
494 DWORD_PTR dwSet = SetThreadAffinityMask(hThread, dwRet);
495 Assert(dwSet == dwProcAff); NOREF(dwRet);
496
497 RTCpuSetFromU64(pCpuSet, (uint64_t)dwSet);
498 return VINF_SUCCESS;
499 }
500 }
501
502 int iLastError = GetLastError();
503 AssertMsgFailed(("SetThreadAffinityMask or GetProcessAffinityMask failed, LastError=%d\n", iLastError));
504 return RTErrConvertFromWin32(iLastError);
505}
506
507
508RTR3DECL(int) RTThreadGetExecutionTimeMilli(uint64_t *pKernelTime, uint64_t *pUserTime)
509{
510 uint64_t u64CreationTime, u64ExitTime, u64KernelTime, u64UserTime;
511
512 if (GetThreadTimes(GetCurrentThread(), (LPFILETIME)&u64CreationTime, (LPFILETIME)&u64ExitTime, (LPFILETIME)&u64KernelTime, (LPFILETIME)&u64UserTime))
513 {
514 *pKernelTime = u64KernelTime / 10000; /* GetThreadTimes returns time in 100 ns units */
515 *pUserTime = u64UserTime / 10000; /* GetThreadTimes returns time in 100 ns units */
516 return VINF_SUCCESS;
517 }
518
519 int iLastError = GetLastError();
520 AssertMsgFailed(("GetThreadTimes failed, LastError=%d\n", iLastError));
521 return RTErrConvertFromWin32(iLastError);
522}
523
524
525/**
526 * Gets the native thread handle for a IPRT thread.
527 *
528 * @returns The thread handle. INVALID_HANDLE_VALUE on failure.
529 * @param hThread The IPRT thread handle.
530 *
531 * @note Windows only.
532 * @note Only valid after parent returns from the thread creation call.
533 */
534RTDECL(uintptr_t) RTThreadGetNativeHandle(RTTHREAD hThread)
535{
536 PRTTHREADINT pThread = rtThreadGet(hThread);
537 if (pThread)
538 {
539 uintptr_t hHandle = pThread->hThread;
540 rtThreadRelease(pThread);
541 return hHandle;
542 }
543 return (uintptr_t)INVALID_HANDLE_VALUE;
544}
545RT_EXPORT_SYMBOL(RTThreadGetNativeHandle);
546
547
548RTDECL(int) RTThreadPoke(RTTHREAD hThread)
549{
550 AssertReturn(hThread != RTThreadSelf(), VERR_INVALID_PARAMETER);
551 if (g_pfnNtAlertThread)
552 {
553 PRTTHREADINT pThread = rtThreadGet(hThread);
554 AssertReturn(pThread, VERR_INVALID_HANDLE);
555
556 NTSTATUS rcNt = g_pfnNtAlertThread((HANDLE)pThread->hThread);
557
558 rtThreadRelease(pThread);
559 if (NT_SUCCESS(rcNt))
560 return VINF_SUCCESS;
561 return RTErrConvertFromNtStatus(rcNt);
562 }
563 return VERR_NOT_IMPLEMENTED;
564}
565
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette