Is it easy to create? Any sample are
appreciated.
Lisa.
>Hi,
>I want to create a DLL inside which is one thread.
Threads don't live inside DLLs. Instead, they live inside processes. Can a
function within a DLL create a new thread inside the process the DLL is
loaded into? Sure.
>and exported methods are working in the threads
Any thread can call any function it has access to. Whether those functions
can safely be called from multiple threads depends on the design of the
function.
>Is it easy to create? Any sample are
>appreciated.
It's not clear what you want to do.
--
Doug Harrison
Microsoft MVP - Visual C++
Joseph M. Newcomer [MVP]
email: newc...@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
>Just note that unless you have indicated you don't want thread callbacks, you can't create
>a thread in DllMain because it attempts a recursive call of DllMain and deadlocks. (See
>DisableThreadLibraryCalls).
DllMain invocations are serialized, and the system doesn't call that
function recursively. Thus, DisableThreadLibraryCalls doesn't help avoid
recursive DllMain calls, because they simply don't occur. Instead,
DisableThreadLibraryCalls can be used to optimize thread startup.
You can create threads in DllMain. The main way you can deadlock is by
creating a thread, say, in DLL_PROCESS_ATTACH, and then waiting on the newly
created thread to do something. But due to OS-level serialization of the
DllMain calls, the new thread can't run until the current thread exits
DllMain, and if it's waiting on the new thread, you're deadlocked. Richter
expands on this in "Programming Applications for Microsoft Windows, 4th ed."
and specifically notes that DisableThreadLibraryCalls doesn't help in this
situation.
In general, it's possible to create threads in DllMain, but this KB article
explains why MFC apps compiled with VC4 and later shouldn't do it:
http://support.microsoft.com/?kbid=142243
Briefly, MFC threads created in DllMain under MFC 4.0 and later can deadlock
for the reason given above.
There are a number of things you must not do in DllMain, and in general,
anybody contemplating adding things to a function they're unfamiliar with
should look it up in the documentation. For DllMain, it's not just stuff you
insert directly into the function; it also includes global objects defined
in a DLL, because the initialization code is run in the context of DllMain.
In general, it's best to stick to relatively simple things in DllMain, and
I'd agree it's best not to start threads from it. But DllMain doesn't itself
forbid it, and it can work if you're very careful, modulo higher level
restrictions such as those pertaining to MFC threads in MFC 4.0 and later.
What confuses me about the history is the conflict of information between the KB article,
my own experience, and Richter's explanation. Richter says the problem is because the OS
serializes the access, but the KB article says it is new with MFC 4 (and was not present
in MFC 2.2), and exactly how the version of MFC can affect how the operating system
implements serialization escapes me. Futhermore, Richter keeps referring to a mutex in the
kernel, which would have meant that it could not possibly have worked under 2.2., but in
fact I was successfully running a real app written in 2.2, which worked perfectly until I
recompiled it in 4.0. However, I've just spent a fair amount of time trying to determine
what is really happening, and I can't find any explicit synchronization action in the MFC
library that would cause the problem; a breakpoint at the _rawDllMain entry for MFC does
not show any attempt to synchronize, and the second call on it fails.
Although it is worth pointing out that I *did* discover that you can CREATE a thread in a
DLL, as long as it is created suspended! So if thread creation was the problem (you want
to create a whole bunch of threads at once and get that overhead out of the way), it
appears that you are free to create non-running threads in DllMain, and then ResumeThread
the threads once you are out of it. (Try it yourself! It works! Note that AfxBeginThread
creates the thread suspended, and this works perfectly and returns; the hang occurs when
ResumeThread is executed)
I've just read Richter's explanation (p.709), and while he talks about serialization, the
scenario he discusses, which is multiple threads attempting to call DllMain, is either
irrelevant here, or the core of the problem, depending on your viewpoint. For example, the
real problem is that thread A calls DllMain. In DllMain, thread B is created, and it must
call DllMain. But as explained in Q142243, this thread B is serialized, and because it
represents a recursive call, it deadlocks. Thread A can't return from DllMain until its
CreateThread call returns; the CreateThread call can't return until all the DLLs which are
loaded have their DllMain/DLL_THREAD_ATTACH event handled; in attempting to call the
DllMain of the current DLL (I know of no better definition of a recursive call!), the
serialization lock is hit. Thus, thread B can't proceed until thread A releases the lock,
and thread A can't release the lock until thread B completes its notifications so
CreateThread can return, and, bang, classic deadlock. Richter carefully explains this on
p.710. As I say, I discovered this myself shortly after the release of MFC 4.2. My program
had run successfully for several years, which makes me wonder about the
I had been told at one point when I explained this problem to someone that
DisableThreadLibraryCalls would fix this; since I never actually create threads in DllMain
any longer, it never occurred to me to test this. So my advice in that regard was
incorrect. As I indicated above, my own experiment fails. But I can't make sense out of
the KB article or my own experience in correlating this to MFC instead of the OS.
Certainly we knew that MFC 2.x was not thread-safe, but I expected to see a recursive call
that then hung on a synchronization primitive in the MFC runtime. I see behavior
consistent with Richter's analysis that it is in the kernel, but I don't have any more
time today to look at this.
joe
Joseph M. Newcomer [MVP]
That section really is in two parts. The multiple simultaneous thread
creation example, which he talks about first, merely illustrates the
serialization of DllMain. It's not related to the code example he goes on to
present, in which a thread executing in DllMain creates a new thread.
>For example, the
>real problem is that thread A calls DllMain. In DllMain, thread B is created, and it must
>call DllMain. But as explained in Q142243, this thread B is serialized, and because it
>represents a recursive call, it deadlocks. Thread A can't return from DllMain until its
>CreateThread call returns; the CreateThread call can't return until all the DLLs which are
>loaded have their DllMain/DLL_THREAD_ATTACH event handled; in attempting to call the
>DllMain of the current DLL (I know of no better definition of a recursive call!), the
>serialization lock is hit. Thus, thread B can't proceed until thread A releases the lock,
>and thread A can't release the lock until thread B completes its notifications so
>CreateThread can return, and, bang, classic deadlock. Richter carefully explains this on
>p.710. As I say, I discovered this myself shortly after the release of MFC 4.2. My program
>had run successfully for several years, which makes me wonder about the
Well, I wouldn't call that a recursive call, as it's made by a different
thread. This is how I understand things WRT Richter's example and Q142243:
Thread A:
Grab OS loader lock
Execute DllMain
B = CreateThread (*)
WaitForSingleObject(B, INFINITE); (*) // stalls here
Release OS loader lock
Thread B:
Grab OS loader lock // stalls here
Execute DllMain
Release OS loader lock
So CreateThread returns right away, and the deadlock occurs because B is
waiting forever to grab the OS loader lock (so it can call DllMain), which
is currently held by A, which is waiting forever on B. As Richter says, "The
problem is the call to WaitForSingleObject."
The situation in MFC is the same, except the starred lines under "Thread A"
above are in AfxBeginThread. I haven't looked at it today, but IIRC, the "B"
in the WaitForSingleObject above is a CEvent object in the MFC source. So to
see the MFC analogue, replace the starred lines with AfxBeginThread.
Joseph M. Newcomer [MVP]
>Indeed. But a recursive call does not have to be from a single thread to meet the
>criterion; DllMain invokes an action which requires DllMain be called. That's recursive.
>Oh yes, incidentally, it happens from another thread.
It's an asynchronous call made by a different thread. To think of this as
recursion is to stretch the notion past its breaking point in my mind.
Moreover, it doesn't help me think about the problem.
Here again is the example I sketched at the end of my last message (I
explained how the starred lines relate to the Richter example and MFC's
AfxBeginThread in my last message, and that "B" in the WaitForSingleObject
could be any sync object, such as an event):
>>Thread A:
>> Grab OS loader lock
>> Execute DllMain
>> B = CreateThread (*)
>> WaitForSingleObject(B, INFINITE); (*) // stalls here
>> Release OS loader lock
>>
>>Thread B:
>> Grab OS loader lock // stalls here
>> Execute DllMain
>> Release OS loader lock
When I look at this, I see Thread A and Thread B independently and
asynchronously doing their respective things. I see a straightforward thread
synchronization problem. I can remove the "Execute DllMain" lines and not
change the problem at all.
Note that Thread A's CreateThread/WaitForSingleObject sequence is essential
to even begin to think of B's attempt to call DllMain as recursive WRT A. If
we were to eliminate the OS loader locking, so that B could progress, then I
guess B satisfying A's wait condition would have to be considered the
"return statement" for the "recursion", because once A is allowed to
continue, we have two threads which merely happen to be executing in the
same function. This is all very weird and it doesn't help me visualize the
situation.
So, please forget about recursion for a moment. Do you see now why your
thread creation worked in MFC 2 but not 4? Do you see now that the
CreateThread above does return right away and isn't the problem? Do you see
now that the WaitForSingleObject call is necessary to cause deadlock?
Joseph M. Newcomer [MVP]
>Indeed, I had another hour to burn today, and I studied it more deeply. The problem is
>definitely a WaitForSingleObject inside the AfxBeginThread code. But the problem is not
>the WaitForSingleObject in the application code; mine doesn't have one and the app still
>hangs. But it is trapped by the ::WaitForSingleObject on the thread->hEvent handle.
Right, which is why I said a couple of messages ago to look inside
AfxBeginThread (and naturally whatever it calls) for the CreateThread and
WaitForSingleObject calls (the "starred lines" in my example). It doesn't
much matter if you make these calls in your own code or MFC does it for you.
>Using straight CreateThread, even not suspended, does work.
Right. You can also use _beginthreadex.