• Welcome to Valhalla Legends Archive.
 

C++ Queue Dispatcher Implementation

Started by Mephisto, December 25, 2004, 01:23 AM

Previous topic - Next topic

Mephisto

The way my queue is setup is that a thread is created that is specialized for dispatching queued messages.  It sets up a while loop which runs until the applications ends, and then it checks if there are queued messages, and if there are it checks to see if the required delay has gone by.  The problem with this implementation is that it causes iterations that take 100% CPU usage (while(true) ; for example) until there are no more queued messages.  Once there are no queued messages pending it suspends the thread, and when AddQueue(...) is called, it resumes the thread if it detects that it's been suspended by checking whether the queue size is empty or not, because if it's empty we know it's been suspended, but if it's not it's going through messages, so we can add another one on without resuming it.

Here's the code for the thread.  My question is how could I change this around so that it doesn't take 100% CPU usage?  Because even with the time slicer, Sleep(0), it still causes some undesired effects and records 100% CPU usage in the Task Manager.VOID CALLBACK QueueThread()
{
while (global->shutdownBot == FALSE) {
if (!global->data.BnetMsgQueue.empty()) {
if (GetTickCount() > global->sendQueuedMessage) {
BnetPacketBuffer *bnetBuf = new BnetPacketBuffer(SID_CHATCOMMAND);
bnetBuf->pInsert(global->data.BnetMsgQueue.front().c_str());
global->bnetConnect->SendPacket(bnetBuf);
global->sendQueuedMessage = GetTickCount() + RequiredDelay(sizeof(global->data.BnetMsgQueue.front()), FALSE);
global->data.BnetMsgQueue.pop();
}
} else if (global->data.BnetMsgQueue.empty()) {
SuspendThread(global->threadedQueueHandle);
}
Sleep(0);
}
}

Note: global->sendQueuedMessage is the delay amount

MyndFyre

Instead of using a separate thread with a polling loop, why don't you implement a timer, and when you get a WM_TIMER message, create a new thread with that callback?
QuoteEvery generation of humans believed it had all the answers it needed, except for a few mysteries they assumed would be solved at any moment. And they all believed their ancestors were simplistic and deluded. What are the odds that you are the first generation of humans who will understand reality?

After 3 years, it's on the horizon.  The new JinxBot, and BN#, the managed Battle.net Client library.

Quote from: chyea on January 16, 2009, 05:05 PM
You've just located global warming.

Mephisto

Since I will be using MsgWaitForMultipleObjectsEx(...) for this implementation does anyone know how to handle messages if the function returns because one has been queued that is set by the flags?  In my case I set QS_TIMER, and if a WM_TIMER message is queued and it returns, how do I handle it?  Would I see if the return result is WM_TIMER?  Do I call GetMessage(...) and expect that the message it polls will be WM_TIMER if the wait function didn't return because of a signaled event?

Adron

In your original implementation, you should just Sleep until it's time to send the next message. If messages can be added to the queue to be sent at an earlier time than the messages already there, you would have to wake up the thread when that happens.

Mephisto

I did try to sleep on the delay, the problem with that is if someone clears the queue you're still sleeping and there's no way to interupt the sleep call, which makes clearqueue partially useless to the end-user.

Adron

It's not really useless even in that situation - if a certain delay is needed to send the next message, you won't completely get away from a delay even if the queue is cleared because the delay depends on the messages that have already been sent. Clearing the queue means the thread finds out on the next message to be sent that it's done.

Also, as I said, if the queue can have messages removed like that you need to tell this thread about it. You also have to synchronize this thread with the one that might remove things from the queue or else you have a crash waiting to happen.

To produce a Sleep that can be interrupted by a message from another thread, use any of the WaitForMultipleObjects (or single object, if that's enough) functions.

Mephisto

When you pass TRUE to the RequiredDelay function it sets the required delay to 0 and the queue is cleard elsewhere.

As for using a wait function, I've thought of using that, but I don't know how I could create an event to wait on an std::queue object.

Arta

It wouldn't wait on the queue. It would wait on some object that you'd create with CreateEvent(). You can then signal the object and wake up the thread with SetEvent(), which will cause the wait to return. When you call the wait function, you set the timeout to INFINITE if there are no messages in the queue, or to RequiredDelay if there are. You would have to have some logic in there to figure out if you should send a message or not when the wait function returns, because there is the possibility that you're waiting to send an existing message when a new message is added to the queue (which wakes you up early).

Note: You can do all of this with your main wait function by having it wait on an extra event. Then you wouldn't need another thread :)

Adron

Quote from: Mephisto on December 25, 2004, 12:00 PM
When you pass TRUE to the RequiredDelay function it sets the required delay to 0 and the queue is cleard elsewhere.

If the queue is cleared by someone other than the thread that takes messages out of it to process, it needs to be synchronized.

Mephisto