0
votes

Why does Windows 10 start extra threads in my program?

Lets see a simple code,

class AA
{
...
};

AA& getAA(){
  static AA aa;
  return aa;
}

class BB
{
  public:  BB() { getAA(); ... }
...
};
thread_local BB bb;

I added more information what I encounterd.

. With this simple code, I rewrite callstacks.

The exception is thrown occasionally at 0x00007FFB0B1272A6(ntdll.dll). Access violation writing location 0x000000024. The members of g_tss_mutex are zero except LockCount, which was 6, and LockSemaphore, which was 0xffffffffffffffff. The function is _Init_thread_lock() and it is calling EnterCriticalSection(&g_tss_mutex); Worker thread ntdll.dll thread throw this exception. call stack was.

ntdll.dll!00007ffb0b1272a6()
ntdll.dll!00007ffb0b13b5f6()
ntdll.dll!00007ffb0b13b440()
_init_thread_lock() 
_Init_thread_header(int *pOnce)
getAA()
BB::BB()
`dynamic initializer for 'bb'()

The call stack of the main thread was

_should_initialize_environment()
pre_c_initialization()
ucrtbased.dll!00007ffe9c1a4ab9
__scrt_common_main_seh()
__scrt_common_main()
WinMainCrtStartup()
....

Loader Threads of Windows 10 thread pool try to initialize thread_local bb. But if Loader threads call constructor of bb before the main thread of a process does not call _scrt_initialize_thread_safe_statics_platform_specific() to gerentee static magic. https://docs.microsoft.com/en-us/cpp/build/reference/zc-threadsafeinit-thread-safe-local-static-initialization?view=vs-2019

I met this situation. What can I do to handle this problem.

1
I couldn't understand the sentence "But if loader threads ..." -- can you try to clarify? - tenfour
Sorry, I don't understand this question. Could you try to rephrase it? - Asteroids With Wings
@tenfour LoaderThreads mean threads made by ntdll for loading library pararell. - Chanhyun Park
@AsteroidsWithWings sorry. To support thread safety of initializing static local variable, _scrt_initialize_thread_safe_statics_platform_specific() is called. So any initialization of static local variable (aa in getAA()) should happen after that function call. However, to initialize ntdll's worker threads, tls_init() function is called before that function is called. At this time, __bb is being initialized, as a result constructor of aa in``getAA()` is called. - Chanhyun Park
@walnut sorry about my mistake. I rewrote the code. __aa should be __bb, and __bb should be aa in getAA(). - Chanhyun Park

1 Answers

0
votes

Even if Windows Thread Pool would not spawn its own threads, there could always be guests in your process. Antivirus software, management software, shell extensions, accessibility software... or even malware.

The key problem here is that thread_local constructed as early as threads start.

You can help by making their constructor constexpr with only static initialization, and perform any dynamic stuff later.

Or wrap your thread_local objects with std::optional, and construct them when they are needed yourself. Or create your own wrapper for that.

I understand this all are workarounds, and the solution would be that MSVC would delay construction of thread local variable. But I'm satisfied with workarounds. Send feedback to MS if you are not.

Ultimately, by delaying thread_local object construction you also optimize: you don't construct the object for threads in which your code will not ever run.