Click here to Skip to main content
15,893,161 members
Please Sign up or sign in to vote.
4.00/5 (1 vote)
See more:
Hi,

I have code in a COM Dll plug-in that trucks out MIDI sysex to a midi device.
Because of the large size of the sysex dump and the time it takes to execute I created a thread to do the work. Historically I was using _beginthread to get the thread going but recently found it necessary to know when the thread was finished so I switched over to _beginthreadex as it can return a handle to the started thread that WaitForSingleObject can use.

The main code stuffed 4 32 word arrays with data that the thread will ultimately brute force change the bytes in a global array that is more or less a sysex dump "template" that the thread will modify with the bytes in the arrays.

The Actual midi transmission of the data is what takes the most time. About a 1/2 second for each call to myMidiOut->MidiOutLongMessage. At such time I am at the mercy of the host application "Sonar" as it sends the sysex midi string out.

When I switched from _beginthread (which worked), to _beginthreadex fun began.

The thread is started like this:

C++
HANDLE hThread;
hThread = (HANDLE)_beginthreadex( NULL, 0,
CBCRMaster::SetKnobLed, m_pMidiOut, 0, NULL);  

hThread =(HANDLE) _beginthread(CBCRMaster::SetKnobLed,0,m_pMidiOut);

	switch (WaitForSingleObject(hThread, INFINITE))
	{
	     case WAIT_OBJECT_0 :
	     {
        	break;
	     }
	     case WAIT_TIMEOUT :
	     {
		TerminateThread(hThread, 0);
		break;
	     }
	        default :
	     {

		TerminateThread(hThread, 0);
		break;
	    }
        }


The Thread Code: The data arrays are stuffed by the main code. WorkLoad is the amount of data stuffed in the arrays.

C++
unsigned int WINAPI CBCRMaster::SetKnobLed(void * m_pMidiOut)
{

ISonarMidiOut * myMidiOut;
myMidiOut = (ISonarMidiOut*)m_pMidiOut;

while(WorkLoad > -1)
		
{

//.... Run through Sysex_SetEncoder[WorkLoad] and modify the bytes as needed...
//..... code omitted for brevity..............................................

// Now truck the Sysex dump to the midi device
							myMidiOut->MidiOutLongMsg((IControlSurface*)SurfaceAddress[0],sizeof(Sysex_SetEncoder),Sysex_SetEncoder);

WorkLoad--;  

}

return 0;
}



Now the problem:

If the switch (WaitForSingleObject(hThread, INFINITE)) code is in place, when the thread gets to the myMidiOut->MidiOutLongMessage code it never then will execute another line of code, the debugger just acts as if the thread went into the weeds.
The Sysex dump is never sent to the receiving device either.

If I comment out the entire switch (WaitForSingleObject(hThread, INFINITE)) block and just keep on trucking, the Thread executes as it should but I never get to know when it is finished.

However... If I leave the (WaitForSingleObject(hThread, INFINITE)) block in to execute, but comment out the myMidiOut->MidiOutLongMessage line then the thread runs as it should and furthermore the WaitForSingleObject event fires with WAIT_OBJECT_0.

I have tied increasing the stack from 0 to 30 with no avail and I should reiterate that the whole shootin' match worked with _beginthread.

Thanks.

:Ron
Posted
Comments
Chuck O'Toole 15-Jun-12 19:31pm    
I hope this is just a paste problem but your sample code has *both* beginthread and beginthreadex executed. I don't think you really mean to do both.
[no name] 16-Jun-12 5:08am    
I have a number of problems with your question. It is not clear. You said you switched from _beginthread() which worked. Why? _beginthread() returns a waitable handle. Why are you using TerminateThread() and not endthread(ex)()?

I had a similar problem, earlier in the year.

You have to just call the WaitForSingleObject without the switch, in a do loop, and inside the loop, take a peek at the message pump and wait for WAIT_OBJECT_0 or some other message.

This is just a visual concept of what I trying to convey, and will not paste and work for you. I used the MultpleObject and for CreateThread, but the concept is sort of similar.

But when the thread is done, it will exit gracefully and let you know about it.

DWORD dwRet;
do
{
	dwRet = ::MsgWaitForMultipleObjects(1, &hThread, FALSE, 
			INFINITE, QS_ALLINPUT);

	if (dwRet != WAIT_OBJECT_0)
	{
		MSG msg;
		while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}
} while ((dwRet != WAIT_OBJECT_0) && (dwRet != WAIT_FAILED));
 
Share this answer
 
Thank you.

I tried what you have suggested, modifying it to play with my code as shown but it sticks at the dwRet = ::WaitForSingleObject(hThread, INFINITE); line and never drops down to the if(dwRet != WAIT_OBJECT) line.


C++
DWORD dwRet;
do
{
	dwRet = ::WaitForSingleObject(hThread, INFINITE);

	if (dwRet != WAIT_OBJECT_0)
	{
	        MSG msg;
		while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}
} while ((dwRet != WAIT_OBJECT_0) && (dwRet != WAIT_FAILED));
 
Share this answer
 
Comments
jkirkerx 15-Jun-12 18:14pm    
Try the original version, the multipleObject, and use 1 for a single thread.

I think that's why I used the multipleObject, because I had the same issue.

Let me know what happens, there's a bit more code that goes along with it.
jkirkerx 15-Jun-12 18:32pm    
If it doesn't work, then you'll need help from one of the top level c++ experts in CJ. If fact, I'm surprised one didn't post a comment yet to my solution.

Don't forget I used CreateThread, and not beginThreadEx
Yes, that worked insomuch as now the code will fall through but the thread still goes "into the weeds" at myMidiOut-> and therefore never signals it's end.
If I comment this line out, all works as expected.

It's as if in the environment I am running in which is a COM Dll plug-in into a larger application, that somehow or another WaitForANYTYPEofObject is not permitted.

The myMidiOut is an object that belongs to the APP in plugged into.

Thanks for all your help on this.

I'm going to go mutter to myself for a while..........
 
Share this answer
 
Comments
jkirkerx 15-Jun-12 19:53pm    
Threads can get tricky,

break it down into modules, create the thread enviroment, create an empty thread, then run the thread, and make sure it's clean. Then start adding your code back in. If your calling a function inside a external dll, then put that in the thread.
I have determined (with doubt) that I can't call a thread that in it, calls a method in the Main application I am a pluged-in to, and WaitForSingleObject or WaitForMultipleObjects to see if that thread has completed. The method called in the main Application never returns at all if I am waiting for my thread to complete. It is as if I have violated a rule somewhere. If I do nothing more than call the thread with the method call myMidiOut->MidiOutLongMsg() and wait on it the MidiOutLongMsg never returns.

To overcome the problem I moved the calling code that so desparatly needs to know when the thread has quit, into the thread itself where I can control the flow locally.

Thanks for all your help,
I have learned some more stuff today.

:Ron
 
Share this answer
 
Comments
jkirkerx 17-Jun-12 23:30pm    
Well, sometimes our original plans fail. And you have to rethink the whole thing, and move code around to the right place.

Nothing wrong with running the thing in 1 giant thread, and just waiting for your exit code. In the mean time, while your thread is running, so can run some sort of progress indicator.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900