Here be dragons?

by Michael S. Kaplan, published on 2008/01/02 10:01 -05:00, original URI: http://blogs.msdn.com/b/michkap/archive/2008/01/02/6950506.aspx


Dan's question is one that has been on some people's minds ffrom time to time for as long as I can remember:

Based on code I’ve seen, I’d say that it’s okay to start threads (that call CRT functions) with CreateThread if you are dynamically linking to the CRT. Only when you are statically linking to the CRT do you need to use _beginthreadex. Am I right?

The only documentation I’ve found to support this says:

If you are going to call C run-time routines from a program built with Libcmt.lib, you must start your threads with the _beginthread or _beginthreadex function. Do not use the Win32 functions ExitThread and CreateThread. …

and

_beginthread and _beginthreadex are similar to the CreateThread function in the Win32 API but has these differences: … They initialize certain C run-time library variables. This is important only if you use the C run-time library in your threads. …

That first quote does say that if statically linking to the CRT, you must  use _beginthread[ex], but nowhere seems to mention that if you are dynamically linking, it is okay to use CreateThread instead. (I assume the CRT DLL does its initialization in a THREAD_ATTACH notification)

A whole bunch of experts weighed in with advice to just use CreateThread ane not worry about these docs that are not too clear about the underlying problems they were warning about.

and in some ways this is understandable, since the rule about giving the there be dragons there! warning is that you have to describe the dragon well enough that people will believe you (if not, then eventually they will remember that dragons are fictional and that there is really no reason to worry....

Luickily there are some of the wise longtime dragonslayers out there who we can get the real answers that the documentation hints at without really giving the full story, like Alan Chan from the Visual C++ development team:

Actually, you can use CreateThread (win32) or _beginthread[ex](CRT) for the dynamically linked CRT case.  _beginthread() is essentially a wrapper around CreateThread + do some initialization with per-thread data initalization (mostly for locales).  However, when dynamically linking, the CRT hooks the Dll_THREAD_ATTACH and do per-thread data initialization there instead.

I was curious about the "mostly for locales" bit, so I asked him for some information, and he filled in some the details:

I said mostly because the “big thing” we store in our PTD (per-thread data) is locale.  However, locales is not the only that we store in PTD.  On top of my head, I know there are some per-thread buffers that are stored in the ptd.

Actually, after sending this email and seeing so much stuff in the PTD, it got me thinking why I only seen locale related problems relating to this.  I went back and re-checked the code again.  The PTD is allocated on first use by _getptd() if it has not been allocated.  The only reason why there are locale problems when not using _beginthread vs _createthread is how PTD is initialized.  In _beginthread[ex](), the CRT can pass information between the thread spawning the new thread and the thread being created.  In the case of _beginthread(), we can copy the ptd->ptlocinfo and initialize it.  However, in the CreateThread case, during DLL_THREAD_ATTACH or first use of PTD, we can’t copy the locinfo from the thread that spawned us.  Hence, the CRT copies from a global pointer.  This might cause problems when using _ENABLE_PER_THREAD_LOCALE.

Indeed, there are specific potential problems that come up if you use that flag with the dynamic C Runtime, which I'll try to cover another day to show the kinds if problem you can hit in this situation....

And of course there is remionder that there may well be dragons lurking around these parts from time to time, even if the documentation is not properly scaring us away from the danger zone. :-)

 

This post brought to you byand(U+2eef and U+2ef0, aka CJK RADICAL J-SIMPLIFIED DRAGON and CJK RADICAL C-SIMPLIFIED DRAGON)


Mike Dimmick on 2 Jan 2008 12:10 PM:

Don't confuse them with exceptions. Always use _beginthreadex if you're using the C runtime. Theoretically, someone could put something in their own DLL_THREAD_ATTACH handling which relies on the CRT being correctly initialised for the thread, and there could be circumstances where you could manage to get that DLL initialised before the CRT. Don't take the risk.

The only exceptional case is if you're running on Windows CE, where _beginthreadex is not even provided. This is because the C runtime is an integral part of the system (implemented in coredll.dll, which also replaces user32, gdi32, kernel32, advapi32 and a few others) and therefore CreateThread already does the Right Thing to initialise the CRT for the new thread.

Michael S. Kaplan on 2 Jan 2008 1:53 PM:

Even in the DLL case? Anyone doing that will pretty likely find themselves broken, a little or a lot, given the current CW with folks here.

Anonymous on 2 Jan 2008 2:31 PM:

A very minor thing - the first CJK radical in the "sponsored by" section is U+2EFF (a typo of U+2EEF) and is mapped to a reserved character - so it doesn't show up for anybody.


go to newer or older post, or back to index or month or day