haiku/src/kits/app/MessageQueue.cpp
haydentech 30fd51473f Minor header-related changes
git-svn-id: file:///srv/svn/repos/haiku/trunk/current@3246 a95241bf-73f2-0310-859d-f6bbb57e9c96
2003-05-14 17:21:46 +00:00

435 lines
13 KiB
C++

//
// $Id: MessageQueue.cpp,v 1.3 2003/05/14 17:21:46 haydentech Exp $
//
// This file contains the implementation of the BMessageQueue class
// for the OpenBeOS project.
//
#include <MessageQueue.h>
#include <Autolock.h>
#include <Message.h>
#ifdef USE_OPENBEOS_NAMESPACE
namespace OpenBeOS {
#endif
/*
* Method: BMessageQueue::BMessageQueue()
* Descr: This method is the only constructor for a BMessageQueue. Once the
* constructor completes, the BMessageQueue is created with no BMessages
* in it.
*
*/
BMessageQueue::BMessageQueue() :
fTheQueue(NULL), fQueueTail(NULL), fMessageCount(0)
{
}
/*
* Method: BMessageQueue::~BMessageQueue()
* Descr: This is the desctructor for the BMessageQueue. It iterates over
* any messages left on the queue and deletes them.
*
* The implementation is careful not to release the lock when the
* BMessageQueue is deconstructed. If the lock is released, it is
* possible another thread will start an AddMessage() operation before
* the BLocker is deleted. The safe thing to do is not to unlock the
* BLocker from the destructor once it is acquired. That way, any thread
* waiting to do a AddMessage() will fail to acquire the lock since the
* BLocker will be deleted before they can acquire it.
*
*/
BMessageQueue::~BMessageQueue()
{
if (fLocker.Lock()) {
BMessage *theMessage = fTheQueue;
while (theMessage != NULL) {
BMessage *messageToDelete = theMessage;
theMessage = theMessage->link;
delete messageToDelete;
}
}
}
/*
* Method: BMessageQueue::AddMessage()
* Descr: This method adds a BMessage to the queue. It makes a couple of
* assumptions:
* - The BMessage was allocated on the heap with new. Since the
* destructor delete's BMessages left on the queue, this must be
* true. The same assumption is made with Be's implementation.
* - The BMessage is not already on this or any other BMessageQueue.
* If it is, the queue it is already on will be corrupted. Be's
* implementation makes this assumption also and does corrupt
* BMessageQueues where this is violated.
*
*/
void
BMessageQueue::AddMessage(BMessage *message)
{
if (message == NULL) {
return;
}
// The Be implementation does not seem to check that the lock acquisition
// was successful. This will specifically cause problems when the
// message queue is deleted. On delete, any thread waiting for the lock
// will be notified that the lock failed. Be's implementation, because
// they do not check proceeds with the operation, potentially corrupting
// memory. This implementation is different, but I can't imagine that
// Be's implementation is worth emulating.
//
BAutolock theAutoLocker(fLocker);
if (theAutoLocker.IsLocked()) {
// The message passed in will be the last message on the queue so its
// link member should be set to null.
message->link = NULL;
// We now have one more BMessage on the queue.
fMessageCount++;
// If there are no BMessages on the queue.
if (fQueueTail == NULL) {
// Then this message is both the start and the end of the queue.
fTheQueue = message;
fQueueTail = message;
} else {
// If there are already messages on the queue, then the put this
// BMessage at the end. The last BMessage prior to this AddMessage()
// is fQueueTail. The BMessage at fQueueTail needs to point to the
// new last message, the one being added.
fQueueTail->link = message;
// Now update the fQueueTail to point to this new last message.
fQueueTail = message;
}
}
}
/*
* Method: BMessageQueue::RemoveMessage()
* Descr: This method searches the queue for a particular BMessage. If
* it is found, it is removed from the queue.
*
*/
void
BMessageQueue::RemoveMessage(BMessage *message)
{
if (message == NULL) {
return;
}
BAutolock theAutoLocker(fLocker);
// The Be implementation does not seem to check that the lock acquisition
// was successful. This will specifically cause problems when the
// message queue is deleted. On delete, any thread waiting for the lock
// will be notified that the lock failed. Be's implementation, because
// they do not check proceeds with the operation, potentially corrupting
// memory. This implementation is different, but I can't imagine that
// Be's implementation is worth emulating.
//
if (theAutoLocker.IsLocked()) {
// If the message to be removed is at the front of the queue.
if (fTheQueue == message) {
// We need to special case the handling of removing the first element.
// First, the new front element will be the next one.
fTheQueue = fTheQueue->link;
// Must decrement the count of elements since the front one is being
// removed.
fMessageCount--;
// If the new front element is NULL, then that means that the queue
// is now empty. That means that fQueueTail must be set to NULL.
if (fTheQueue == NULL) {
fQueueTail = NULL;
}
// We have found the message and removed it in this case. We can
// bail out now. The autolocker will take care of releasing the
// lock for us.
return;
}
// The message to remove is not the first one, so we need to scan the
// queue. Get a message iterator and set it to the first element.
BMessage *messageIter = fTheQueue;
// While we are not at the end of the list.
while (messageIter != NULL) {
// If the next message after this (ie second, then third etc) is
// the one we are looking for.
if (messageIter->link == message) {
// At this point, this is what we have:
// messageIter - the BMessage in the queue just before the
// match
// messageIter->link - the BMessage which matches message
// message - the same as messageIter->link
// message->link - the element after the match
//
// The next step is to link the BMessage just before the match
// to the one just after the match. This removes the match from
// the queue.
messageIter->link = message->link;
// One less element on the queue.
fMessageCount--;
// If there is no BMessage after the match is the
if (message->link == NULL) {
// That means that we just removed the last element from the
// queue. The new last element then must be messageIter.
fQueueTail = messageIter;
}
// We can return now because we have a match and removed it.
return;
}
// No match yet, go to the next element in the list.
messageIter = messageIter->link;
}
}
}
/*
* Method: BMessageQueue::CountMessages()
* Descr: This method just returns the number of BMessages on the queue.
*/
int32
BMessageQueue::CountMessages(void) const
{
return fMessageCount;
}
/*
* Method: BMessageQueue::IsEmpty()
* Descr: This method just returns true if there are no BMessages on the queue.
*/
bool
BMessageQueue::IsEmpty(void) const
{
return (fMessageCount == 0);
}
/*
* Method: BMessageQueue::FindMessage()
* Descr: This method searches the queue for the index'th BMessage. The first
* BMessage is at index 0, the second at index 1 etc. The BMessage
* is returned if it is found. If no BMessage exists at that index
* (ie the queue is not that long or the index is invalid) NULL is
* returned.
*
* This method does not lock the BMessageQueue so there is risk that
* the queue could change in the course of the search. Be's
* implementation must do the same, unless they do some funky casting.
* The method is declared const which means it cannot modify the data
* members. Because it cannot modify the data members, it cannot
* acquire a lock. So unless they are casting away the const-ness
* of the this pointer, this member in Be's implementation does no
* locking either.
*/
BMessage *
BMessageQueue::FindMessage(int32 index) const
{
// If the index is negative or larger than the number of messages on the
// queue.
if ((index < 0) || (index >= fMessageCount)) {
// No match is possible, bail out now.
return NULL;
}
// Get a message iterator and initialize it to the start of the queue.
BMessage *messageIter = fTheQueue;
// While this is not the end of the queue.
while (messageIter != NULL) {
// If the index reaches zero, then we have found a match.
if (index == 0) {
// Because this is a match, break out of the while loop so we can
// return the message pointed to messageIter.
break;
}
// No match yet, decrement the index. We will have a match once index
// reaches zero.
index--;
// Increment the messageIter to the next BMessage on the queue.
messageIter = messageIter->link;
}
// If no match was found, messageIter will be NULL since that is the only
// way out of the loop. If a match was found, the messageIter will point
// to that match.
return messageIter;
}
/*
* Method: BMessageQueue::FindMessage()
* Descr: This method searches the queue for the index'th BMessage that has a
* particular what code. The first BMessage with that what value is at
* index 0, the second at index 1 etc. The BMessage is returned if it
* is found. If no matching BMessage exists at that index NULL is
* returned.
*
* This method does not lock the BMessageQueue so there is risk that
* the queue could change in the course of the search. Be's
* implementation must do the same, unless they do some funky casting.
* The method is declared const which means it cannot modify the data
* members. Because it cannot modify the data members, it cannot
* acquire a lock. So unless they are casting away the const-ness
* of the this pointer, this member in Be's implementation does no
* locking either.
*/
BMessage *
BMessageQueue::FindMessage(uint32 what,
int32 index) const
{
// If the index is negative or larger than the number of messages on the
// queue.
if ((index < 0) || (index >= fMessageCount)) {
// No match is possible, bail out now.
return NULL;
}
// Get a message iterator and initialize it to the start of the queue.
BMessage *messageIter = fTheQueue;
// While this is not the end of the queue.
while (messageIter != NULL) {
// If the messageIter points to a BMessage with the what code we are
// looking for.
if (messageIter->what == what) {
// If the index reaches zero, then we have found a match.
if (index == 0) {
// Because this is a match, break out of the while loop so we can
// return the message pointed to messageIter.
break;
}
// No match yet, decrement the index. We will have a match once index
// reaches zero.
index--;
}
// Increment the messageIter to the next BMessage on the queue.
messageIter = messageIter->link;
}
// If no match was found, messageIter will be NULL since that is the only
// way out of the loop. If a match was found, the messageIter will point
// to that match.
return messageIter;
}
/*
* Method: BMessageQueue::Lock()
* Descr: This member just locks the BMessageQueue so no other thread can acquire
* the lock nor make changes to the queue through members like
* AddMessage(), RemoveMessage(), NextMessage() or ~BMessageQueue().
*/
bool
BMessageQueue::Lock(void)
{
return fLocker.Lock();
}
/*
* Method: BMessageQueue::Unlock()
* Descr: This member releases the lock which was acquired by Lock().
*/
void
BMessageQueue::Unlock(void)
{
fLocker.Unlock();
}
/*
* Method: BMessageQueue::IsLocked()
* Descr: This member returns whether or not the queue is locked
*/
bool
BMessageQueue::IsLocked(void)
{
return fLocker.IsLocked();
}
/*
* Method: BMessageQueue::NextMessage()
* Descr: This member removes the first BMessage on the queue and returns
* it to the caller. If the queue is empty, NULL is returned.
*/
BMessage *
BMessageQueue::NextMessage(void)
{
// By default, we will assume that no BMessage is on the queue.
BMessage *result = NULL;
BAutolock theAutoLocker(fLocker);
// The Be implementation does not seem to check that the lock acquisition
// was successful. This will specifically cause problems when the
// message queue is deleted. On delete, any thread waiting for the lock
// will be notified that the lock failed. Be's implementation, because
// they do not check proceeds with the operation, potentially corrupting
// memory. This implementation is different, but I can't imagine that
// Be's implementation is worth emulating.
//
if (theAutoLocker.IsLocked()) {
// Store the first BMessage in the queue in result.
result = fTheQueue;
// If the queue is not empty.
if (fTheQueue != NULL) {
// Decrement the message count since we are removing an element.
fMessageCount--;
// The new front of the list is moved forward thereby removing the
// first element from the queue.
fTheQueue = fTheQueue->link;
// If the queue is empty after removing the front element.
if (fTheQueue == NULL) {
// We need to set the tail of the queue to NULL since the queue
// is now empty.
fQueueTail = NULL;
}
}
}
return result;
}
void
BMessageQueue::_ReservedMessageQueue1(void)
{
}
void
BMessageQueue::_ReservedMessageQueue2(void)
{
}
void
BMessageQueue::_ReservedMessageQueue3(void)
{
}
#ifdef USE_OPENBEOS_NAMESPACE
}
#endif