views:

375

answers:

4

Let's say that I have the following code that's run in one thread of a multi-threaded COM application:

// Thread 1
struct foo {
    int (*DoSomething)(void ** parm);
};

HMODULE fooHandle = LoadLibrary("pathToFoo");
typedef int (*loadUpFooFP)(foo ** fooPtrPtr);
loadUpFooFP loadUpFoo;
loadUpFoo = (loadUpFooFP)GetProcAddress(fooHandle, "fooLoader");
foo * myFoo;
loadUpFoo(&myFoo);

This all works great and I can then call

myFoo->DoSomething(&parmPtr);

This works, too! Now, another thread comes along and loads up its foo:

// Thread 2
foo * myFooInThread2;
loadUpFoo(&myFooInThread2);

And this, too, works great. In thread 2 I can call DoSomething:

// Thread 2
myFooInThread2->DoSomething(&anotherParmPtr);

Now, when thread 1 eventually goes away, I have a problem. I notice that debugging in Visual Studio that the address of DoSomething can no longer be evaluated. After the first thread dies, when I call:

myFooInThread2->DoSomething(&anotherParmPtr);

I get an access violation. The myFooInThread2 pointer is still valid, but the function pointer was not. This function pointer was set by a call into loadUpFoo which in turn was in a dll loaded by LoadLibrary.

My question is: where do I start looking for the reason this is failing? Is it some problem with the way the external DLL (that I load with LoadLibrary) is setting the function pointer in my foo struct? Or is it something to do with differing threads using the same library? Or, could it somehow be related to my usage of COM in this application (would calling CoUninitialize in the first thread somehow free this memory or library)?

I can provide more details on the COM setup if that looks like it could be responsible. Thanks!

edit: Thanks for the suggestions so far. The foo struct is opaque - I don't really know much about its implementation. The foo struct is declared in a header I import. There are no reference counting methods that I explicitly call and there are no other interactions with the library that's loaded with LoadLibrary. I'm pretty sure the foo struct is not memory mapped to some COM class, but like I said it's opaque and I can't say for sure.

The foo pointers' lifetimes are properly managed (not deleted).

The foo structure is an encryption library, so I'm not at liberty to divulge any more. At this point, I'm confident that there is nothing inherently wrong with my usage of LoadLibrary across different threads and within a COM application (and I suppose that the function pointer memory cleanup is being caused by something in the library itself outside of my control).

A: 

Thats because the fooHandle and loadUpFoo shouldn't be specific to any thread but the main thread. You should load those directly in the WinMain() instead of entering the Thread 1 to do that, or by declaring those two as static but I'm not sure about this last one.

Havenard
So there is something about LoadLibrary that dictates (suggests?) that I should be doing this in the main thread? BTW - There is no WinMain in this case...
Zach
There must be a "main" somewhere. I'm not sure if LoadLibrary is thread specific. If it is not, then the static thing will do.
Havenard
There's nothing in LoadLibrary that dictates it should be done on the main thread.
Franci Penov
Maybe becouse nobody thought that an insane programmer would try to use something from a thread in another thread without synchronizating them. Why don't you just do it right and stop complaining?
Havenard
Your reaction to a simple fact statement is quite unprofessional. You should consider reevaluating your attitude.
Franci Penov
-1, it's in fact a bad idea in many environments to block the main thread on I/O. And `LoadLibrary()` is quite likely to require significant I/O
MSalters
He is calling the threads from somewhere. He can simply LoadLibrary() there. Thats it. Fixed. Its nonsense to LoadLibrary inside a thread and use it inside another concurrent thread, you can't even get sure if the first one had really done something before this secound one get called. His whole question has a serious thread safety problem just by start. Thats what I'm trying to show here.
Havenard
+2  A: 

There's nothing COM related in the code you've shown. LoadLibrary is not thread-specific, so once you have the handle to the lib, you can reuse it from all your threads. Same applies to the pointer to the fooLoader method.

However, there certainly could be something COM-specific inside fooLoader. Also, what's not clear here is what is the lifetime control of the foo instances.

From the fact that you mention COM and the funky behavior you see, I have the sneaky suspicion that foo is actually a memory map of a COM object's vtable and fooLoader is DllGetClassObject or another factory method that creates COM objects.. :-)

In any case, the most probable explanation for the foo instance becoming invalid would be that it is ref-counted and DoSomething calls AddRef()/Release() causing it to self-destroy.

To pinpoint exactly what's going on, you'll have to provide a bit more information on what fooLoader does and why you think your code is COM related.

Franci Penov
It's true that there is nothing COM related in the code I provided and I apologize for not being clearer.All the code you see (in both threads) is executed in COM classes, which is why I mentioned COM. I've noticed quirky behavior in the past that had to do with COM and how I used CoInitialize/CoUninitialze, stemming from my lack of experience with COM.Suppose that I am wrong and that the opaque library that I load with LoadLibrary is using COM. If that's the case, would calling CoUninitialize in the thread that loaded the library somehow cause the memory that was mapped to become invalid?
Zach
No, CoUninitialize will not cause the memory to become unmapped, as long as there's outstanding LoadMemory without the corresponding Free call. However, if COM is unintialized on that thread and the fooLoader has used COM to fill out the foo instance, the pointer it has could be a proxy pointer and the proxy would not work properly.
Franci Penov
A: 

The value of DoSomething is determined by the library you load. You should be able to determine where it's pointing. Look at the debug output in Visual Studio. It will tell you not just when, but also where DLLs are loaded. Does your function really point into te DLL you think it should point to?

MSalters
+1  A: 

By any chance, is Thread 1 calling FreeLibrary (or ExitThreadAndFreeLibrary) when it shuts down? If so, you're trying to call code that's no longer mapped into the process. Your object still looks good, because the instance data still exists, but the code for its methods would be gone.

If this is the problem, you can change Thread 1 to not free the library.

Or, you could have the second thread also call LoadLibrary. LoadLibrary and FreeLibrary use reference counting, so if you load a DLL 3 times, it won't unload until you free it 3 times. The reference counting is per process, so you can load the same library from different threads. The additional loads are very low cost.

Adrian McCarthy
This really seems like what is going on, at least from a functional perspective. But FreeLibrary is never being called by me in this case that I can see (I set a breakpoint on FreeLibrary in the only place that it occurs in my code).
Zach