CAsyncSocket in a DLL, frequent crashes on close in DetachHandle

I have encountered a problem with closing CAsyncSocket. Crash is reported in sockcore.cpp, and it's an assert in CAsyncSocket::DetachHandle (my source files seem a bit out of sink with the pdb files, so I'm not too clear on where exactly).

Here are some details:

I'm writing a DLL that's loaded by a service. The DLL is using CAsyncSocket to communicate with the server. The DLL exports a few interfaces. I know that funcStart will be called first, and funcStop as the last one. Therefore, I do Create() in funcStart, and do ShutDown(2) and Close() in funcStop.

I also created a "listening" thread that is basically an infinite while loop, which checks if there is anything to receive (and processes it, if anything is there), then sleeps for a while, allowing other processing. The thread is created in DllMain on DLL_PROCESS_ATTACH and terminated on DLL_PROCESS_DETACH, i.e. this is not an UI (CWinThread-derived) thread.

The socket is a global variable (protected by its own mutex), that is used by the "listening" thread between calls to funcStart and funcStop.

The problem/crash happens on Close(), 1 out of 2 times approximately. I have no other issues, i.e. Send() and Receive() work fine.

From what I read on discussion groups, the problem could be in the fact that I'm using a socket in different threads. Now, if I created it in the main, and am destroying it in the main, I do not see how anything with handle tables could be messed up (if that's the issue here at all).

Help welcome and greatly appreciated! Thanks!


aquarius003Asked:
Who is Participating?
 
DanRollinsCommented:
As I indicated in your other thread (http:/MFC/Q_21783532.html#16261925 in which you gave me a grade of "B"), it is necessary to call AfxSocketInit() in each thread.  

You will also find great success if you do all of the socket activities in the one thread... create, open, read, write, close, destroy.  MFC socket handling is *not* threadsafe and the thread-unsafe parts of it are behind the scenes and hard to avoid -- unless you do *everything* in the thread.

Some related info in here:

    FIX: An unhandled exception occurs when you use MFC sockets in secondary threads
    in an MFC Visual C++ 6.0 application
    http://support.microsoft.com/kb/q193101/

-- Dan(y)
0
 
aquarius003Author Commented:
The cause seems to be in CAsyncSocket::AsyncSelect(), since the socket window is NULL:

ASSERT(pState->m_hSocketWindow != NULL);
0
 
aquarius003Author Commented:
Hi Dan,

Sorry for the B, but that didn't really solve any of my issues. I just thought it was a most useful of all answers, and wanted to close the thread because I abandoned the strategy I was pursuing then.

I tried UI threads, and gave up (that's what the article you're mentioning was related to), switching to CAsyncSocket, as you can see from my current issue. :)

Among other things, I tried your advice and called AfxSocketInit from both the main and 'listener' thread. I also tried killing off the listening thread before closing the socket, doing ShutDown(2) on it before closing it.... nothing changed my predicament. Closing socket still freezes/crashes the calling service.

Tomorrow morning (it's 1am in my village, and time for bed) I'm going to take a look at the MS link you included, I haven't came across it before. I was actually just thinking that I can do away with that puny miserable listening thread as well, set up a CWnd timer and check on the Receive() from the handler a few times a second. And see what kind of issue I'll encounter next with this (nope, I don't think my tempest in a bottle is over yet).

And btw, I do not seem to have any thread-safety issues -  I did secure the use of the socket by wrapping it in a mutex-secured sentinel. Why is the freaking sink window NULL in the thread that created the socket in the first place?? Oh, well.

Thanks for the reply (and sorry about the B :)!!

Sandra
0
Free Tool: IP Lookup

Get more info about an IP address or domain name, such as organization, abuse contacts and geolocation.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

 
mahesh1402Commented:
try making call to 'AsyncSelect(0)' in your derived classes destructor OR

Try overriding CAsyncSocket::Close as follows:

void CMySocket::Close()
{
    AsyncSelect(0);
    CAsyncSocket::Close();
}


MAHESH
0
 
aquarius003Author Commented:
Oh, forgot one thing: I did try the 'do everything from one thread' approach with UI thread and CSocket, and as everyone who's still awake and reading at this point in my story knows, it didnt' work. I guess it's along the lines with my CWnd timer idea, which is the only way I can see at the moment (and I've been known to suffer from selective blindness) to get all work done within one thread.

To be honest, sounds like not too smart thing to do. Any other smart-sounding idea will be given serious consideration.

Good nite!
0
 
aquarius003Author Commented:
Hi Mahesh,

I did try AsyncSelect thingie as well. I even think I got the idea from something very similar you mention in your post (probably posted by you as well :). Nope, didn't work. Thanks, though!

Sandra
0
 
mahesh1402Commented:
Well Where you are calling CAsyncSocket::Close()  ? in ExitInstance() ?? if so try calling it earlier CMainFrame::OnClose might be a good place.

MAHESH
0
 
aquarius003Author Commented:
Hi Mahesh, you remind me of myself, I always rush ahead without considering (or in your case,  reading) things fullly. :)

Here is a part of my original post:

"The DLL exports a few interfaces. I know that funcStart will be called first, and funcStop as the last one. Therefore, I do Create() in funcStart, and do ShutDown(2) and Close() in funcStop."

So, you can see that it's called way before CWinApp is destructed (there ain't no CMainFrame here, it's a DLL).

0
 
mahesh1402Commented:
You are getting assertion on which line of sockcore.cpp ?
0
 
mahesh1402Commented:
>>The socket is a global variable

Its necessary to make socket object global ?  Can you move it inside thread ??

http://www.experts-exchange.com/Programming/Programming_Languages/MFC/Q_10132623.html <=====


MAHESH
0
 
aquarius003Author Commented:
Assert happens in CAsyncSocket::AsyncSelect(), at ASSERT(pState->m_hSocketWindow != NULL).

I'll try Attach()/Detach() thingie when the socket it passed between the threads.

I don't see the point in making it local to the thread, since the thread is doing the listening part only. Sending is done from the main thread: whenever one of the exported functions is called, I have to send a specific message, and  I'd end up having to pass it out to the main one anyhow. I don't think I could use thread notification for this, since I don't think I can pass parameters with it.

I shall try and let you know!
0
 
mahesh1402Commented:
and you are sending messages using PostThreadMessage ?

MAHESH
0
 
mahesh1402Commented:
>>Assert happens in CAsyncSocket::AsyncSelect(),

you're calling CAsyncSocket::Create method in the context of the same thread that calls CAsyncSocket::AsyncSelect ?

MAHESH
0
 
DanRollinsCommented:
>> The thread is created in DllMain on DLL_PROCESS_ATTACH
 
You should not do complex tasks such a creating threads and/or sockets in your DLL_PROCESS_ATTACH handler.  The normal procedure is to have the program that accesses your DLL functions to first call an Intitialize() or Setup() or Open() fn of some type.

    DllMain
    http://msdn.microsoft.com/libraryen-us/dllproc/base/dllmain.asp
0
 
aquarius003Author Commented:
Dan, what's wrong with starting/killing a thread in DllMain???

Okay, I hope this was my last issue with the darn DLL. I was fiddling with it all day, and even when I got rid of threads, the thing just asserted randomly on the closing time. I have no idea why. The thing is, you can't really monitor the sink window for a change (at what point it gets set to NULL) - it won't let you step into whichever function it is that's called internally to retrieve state structure (_afxGetStateOrSomething).

Anyhow....

I switched to WinSock, and it's working just fine. No issues any more. I just wasted two full days on CAsyncSocket issues. Using MFC sockets gave me absolutely no advantage, and I ended up with a whole bunch of headaches. On the positive side, I actually learned a lot, even though I detested the unpleasantness of the process.

Thanks for all you advices. I shall split the points between two of you. I really do appreciate your efforts with this convoluted issue. In a way, I was in a way led out of the forest with your "you shouldn't do that" kind of advice. :)

Ta-ta!
0
 
DanRollinsCommented:
>> Dan, what's wrong with starting/killing a thread in DllMain???

From
    DllMain
    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wcecoreos5/html/wce50lrfdllmain.asp
 >> For example, calling User, Shell, COM, and Windows Sockets functions (or functions
       that call these functions) can cause access violation errors, because their DLLs call
       LoadLibrary to load other system components.

That warning is actually for WinCE developers, but it is scary enough to make *me* (at least) want to avoid it in all programs.  Other MSDN pages warn against calling CoCreateInstance in DllMain and the specific warning is to avoid doing anything that might end up loading additional libaries.  Similar warnings indicate that care must be taken when unloading.  See the raft of "be careful here!" type warnings at the end of:
     http://msdn.microsoft.com/library/en-us/dllproc/base/dllmain.asp
0
 
aquarius003Author Commented:

Good pointers, thanks.

If I read them correctly, the problems happen only if you cause any of the above during the startup of the thread (i.e. while still in the DllMain).

I believe I'm in clear, since the thread I start on DLL_PROCESS_ATTACH doesn't do socket initialization or has anything to do with creating it. WSAStartup and socket creation is done from the main thread, when one of the exported function is called , way after the thread has been created. Before the time that socket is initialized and connected, the listening thread is spinning empty cycles.

If I am wrong, and my setup _is_ potentially unstable, the only remaining option I see would be to switch to timed events, to support listening. I'm not too keen on this, but it will be considered.

Thank you very much again!!
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.