* almost rewrote BMessageQueue; simplified code, removed over-extensive documentation,

cleanup.
* made BMessageQueue::IsLocked() const - the non-const version is still provided
  for binary compatibility.
* Both BMessageQueue::FindMessage() versions are now thread safe, the queue's BLocker
  is now mutable to allow for this.
* renamed BMessage::link to fQueueLink as the "Message4" implementation uses it.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@14955 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler
2005-11-16 13:01:59 +00:00
parent 02ed46b0de
commit 1ba67cc8c6
5 changed files with 234 additions and 468 deletions
+1 -1
View File
@@ -399,7 +399,7 @@ static BBlockCache *sMsgCache;
void da_swap_var_sized(dyn_array *da);
void da_swap_fixed_sized(dyn_array *da);
BMessage *link;
BMessage *fQueueLink;
int32 fTarget;
BMessage *fOriginal;
uint32 fChangeCount;
+45 -54
View File
@@ -1,65 +1,56 @@
//
// $Id: MessageQueue.h 1686 2002-10-26 18:59:16Z beveloper $
//
// This is the BMessageQueue interface for OpenBeOS. It has been created
// to be source and binary compatible with the BeOS version of
// BMessageQueue.
//
#ifndef _OPENBEOS_MESSAGEQUEUE_H
#define _OPENBEOS_MESSAGEQUEUE_H
/*
* Copyright 2001-2005, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _MESSAGE_QUEUE_H
#define _MESSAGE_QUEUE_H
#include <Locker.h>
#include <Message.h> /* For convenience */
#include <Message.h>
/* For convenience */
#ifdef USE_OPENBEOS_NAMESPACE
namespace OpenBeOS {
#endif
class BMessageQueue {
public:
BMessageQueue();
virtual ~BMessageQueue();
void AddMessage(BMessage *message);
void RemoveMessage(BMessage *message);
int32 CountMessages(void) const;
bool IsEmpty(void) const;
BMessage *FindMessage(int32 index) const;
BMessage *FindMessage(uint32 what, int32 index=0) const;
bool Lock(void);
void Unlock(void);
bool IsLocked(void);
public:
BMessageQueue();
virtual ~BMessageQueue();
BMessage *NextMessage(void);
void AddMessage(BMessage* message);
void RemoveMessage(BMessage* message);
private:
int32 CountMessages() const;
bool IsEmpty() const;
// Reserved space in the vtable for future changes to BMessageQueue
virtual void _ReservedMessageQueue1(void);
virtual void _ReservedMessageQueue2(void);
virtual void _ReservedMessageQueue3(void);
BMessageQueue(const BMessageQueue &);
BMessageQueue &operator=(const BMessageQueue &);
BMessage *fTheQueue;
BMessage *fQueueTail;
int32 fMessageCount;
BLocker fLocker;
// Reserved space for future changes to BMessageQueue
uint32 fReservedSpace[3];
BMessage* FindMessage(int32 index) const;
BMessage* FindMessage(uint32 what, int32 index = 0) const;
bool Lock();
void Unlock();
bool IsLocked() const;
BMessage *NextMessage();
private:
// Reserved space in the vtable for future changes to BMessageQueue
virtual void _ReservedMessageQueue1();
virtual void _ReservedMessageQueue2();
virtual void _ReservedMessageQueue3();
BMessageQueue(const BMessageQueue &);
BMessageQueue &operator=(const BMessageQueue &);
bool IsLocked();
// this needs to be exported for R5 compatibility and should
// be dropped as soon as possible
private:
BMessage* fHead;
BMessage* fTail;
int32 fMessageCount;
mutable BLocker fLock;
uint32 _reserved[3];
};
#ifdef USE_OPENBEOS_NAMESPACE
}
#endif
#endif // _OPENBEOS_MESSAGEQUEUE_H
#endif // _MESSAGE_QUEUE_H
+2 -2
View File
@@ -476,7 +476,7 @@ BMessage& BMessage::operator=(const BMessage& msg)
{
what = msg.what;
link = msg.link;
fQueueLink = msg.fQueueLink;
fTarget = msg.fTarget;
fOriginal = msg.fOriginal;
fChangeCount = msg.fChangeCount;
@@ -506,7 +506,7 @@ void BMessage::init_data()
{
what = 0;
link = NULL;
fQueueLink = NULL;
fTarget = B_NULL_TOKEN;
fOriginal = NULL;
fChangeCount = 0;
+2 -2
View File
@@ -472,7 +472,7 @@ BMessage::operator=(const BMessage &msg)
{
what = msg.what;
link = msg.link;
fQueueLink = msg.fQueueLink;
fTarget = msg.fTarget;
fOriginal = msg.fOriginal;
fChangeCount = msg.fChangeCount;
@@ -504,7 +504,7 @@ BMessage::init_data()
{
what = 0;
link = NULL;
fQueueLink = NULL;
fTarget = B_NULL_TOKEN;
fOriginal = NULL;
fChangeCount = 0;
+184 -409
View File
@@ -1,501 +1,276 @@
//------------------------------------------------------------------------------
// Copyright (c) 2001-2002, OpenBeOS
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
// File Name: MessageQueue.cpp
// Author(s): unknown
//
// Description: Queue for holding BMessages
//
//------------------------------------------------------------------------------
/*
* Copyright 2001-2005, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Unknown? Eric?
* Axel Dörfler, axeld@pinc-software.de
*/
/** Queue for holding BMessages */
#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),
:
fHead(NULL),
fTail(NULL),
fMessageCount(0),
fLocker("BMessageQueue_fLocker")
fLock("BMessageQueue Lock")
{
}
/*
* 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.
*
*/
/*!
\brief 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;
#ifndef USING_MESSAGE4
theMessage = theMessage->link;
#else
theMessage = theMessage->fQueueLink;
#endif
delete messageToDelete;
}
if (!Lock())
return;
BMessage* message = fHead;
while (message != NULL) {
BMessage *next = message->fQueueLink;
delete message;
message = next;
}
}
/*
* 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.
*
*/
/*!
\brief 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.
- The BMessage is not already on this or any other BMessageQueue.
If it is, the queue it is already on will be corrupted.
*/
void
BMessageQueue::AddMessage(BMessage *message)
BMessageQueue::AddMessage(BMessage* message)
{
if (message == NULL) {
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.
#ifndef USING_MESSAGE4
message->link = NULL;
#else
message->fQueueLink = NULL;
#endif
// 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.
#ifndef USING_MESSAGE4
fQueueTail->link = message;
#else
fQueueTail->fQueueLink = message;
#endif
// Now update the fQueueTail to point to this new last message.
fQueueTail = message;
}
BAutolock _(fLock);
if (!IsLocked())
return;
// The message passed in will be the last message on the queue so its
// link member should be set to null.
message->fQueueLink = NULL;
fMessageCount++;
if (fTail == NULL) {
// there are no messages in the queue yet
fHead = fTail = message;
} else {
// just add it after the tail
fTail->fQueueLink = message;
fTail = message;
}
}
/*
* Method: BMessageQueue::RemoveMessage()
* Descr: This method searches the queue for a particular BMessage. If
* it is found, it is removed from the queue.
*
*/
/*!
\brief This method searches the queue for a particular BMessage.
If it is found, it is removed from the queue.
*/
void
BMessageQueue::RemoveMessage(BMessage *message)
BMessageQueue::RemoveMessage(BMessage* message)
{
if (message == NULL) {
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.
#ifndef USING_MESSAGE4
fTheQueue = fTheQueue->link;
#else
fTheQueue = fTheQueue->fQueueLink;
#endif
// Must decrement the count of elements since the front one is being
// removed.
BAutolock _(fLock);
if (!IsLocked())
return;
BMessage* last = NULL;
for (BMessage* entry = fHead; entry != NULL; entry = entry->fQueueLink) {
if (entry == message) {
// remove this one
if (entry == fHead)
fHead = entry->fQueueLink;
if (entry == fTail)
fTail = last;
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.
#ifndef USING_MESSAGE4
if (messageIter->link == message) {
#else
if (messageIter->fQueueLink == message) {
#endif
// 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.
#ifndef USING_MESSAGE4
messageIter->link = message->link;
#else
messageIter->fQueueLink = message->fQueueLink;
#endif
// One less element on the queue.
fMessageCount--;
// If there is no BMessage after the match is the
#ifndef USING_MESSAGE4
if (message->link == NULL) {
#else
if (message->fQueueLink == NULL) {
#endif
// 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.
#ifndef USING_MESSAGE4
messageIter = messageIter->link;
#else
messageIter = messageIter->fQueueLink;
#endif
}
last = entry;
}
}
/*
* Method: BMessageQueue::CountMessages()
* Descr: This method just returns the number of BMessages on the queue.
*/
/*!
\brief This method just returns the number of BMessages on the queue.
*/
int32
BMessageQueue::CountMessages(void) const
BMessageQueue::CountMessages() const
{
return fMessageCount;
}
/*
* Method: BMessageQueue::IsEmpty()
* Descr: This method just returns true if there are no BMessages on the queue.
*/
/*!
\brief This method just returns true if there are no BMessages on the queue.
*/
bool
BMessageQueue::IsEmpty(void) const
BMessageQueue::IsEmpty() const
{
return (fMessageCount == 0);
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.
*/
/*!
\brief 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.
*/
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.
BAutolock _(fLock);
if (!IsLocked())
return NULL;
if (index < 0 || index >= fMessageCount)
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) {
for (BMessage* message = fHead; message != NULL; message = message->fQueueLink) {
// 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.
if (index == 0)
return message;
index--;
// Increment the messageIter to the next BMessage on the queue.
#ifndef USING_MESSAGE4
messageIter = messageIter->link;
#else
messageIter = messageIter->fQueueLink;
#endif
}
// 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;
return NULL;
}
/*
* 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.
*/
/*!
\brief Searches the queue for the index'th BMessage that has a
particular what code.
*/
BMessage *
BMessageQueue::FindMessage(uint32 what,
int32 index) const
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.
BAutolock _(fLock);
if (!IsLocked())
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 (index < 0 || index >= fMessageCount)
return NULL;
for (BMessage* message = fHead; message != NULL; message = message->fQueueLink) {
if (message->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.
if (index == 0)
return message;
index--;
}
// Increment the messageIter to the next BMessage on the queue.
#ifndef USING_MESSAGE4
messageIter = messageIter->link;
#else
messageIter = messageIter->fQueueLink;
#endif
}
// 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;
return NULL;
}
/*
* 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().
*/
/*!
\brief Locks the BMessageQueue so no other thread can change
or search the queue.
*/
bool
BMessageQueue::Lock(void)
BMessageQueue::Lock()
{
return fLocker.Lock();
return fLock.Lock();
}
/*
* Method: BMessageQueue::Unlock()
* Descr: This member releases the lock which was acquired by Lock().
*/
/*!
\brief Releases the lock which was acquired by Lock().
*/
void
BMessageQueue::Unlock(void)
BMessageQueue::Unlock()
{
fLocker.Unlock();
fLock.Unlock();
}
/*
* Method: BMessageQueue::IsLocked()
* Descr: This member returns whether or not the queue is locked
*/
bool
BMessageQueue::IsLocked(void)
/*!
\brief Returns whether or not the queue is locked
*/
bool
BMessageQueue::IsLocked() const
{
return fLocker.IsLocked();
return fLock.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.
*/
/*!
\brief 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)
BMessageQueue::NextMessage()
{
// 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.
#ifndef USING_MESSAGE4
fTheQueue = fTheQueue->link;
#else
fTheQueue = fTheQueue->fQueueLink;
#endif
// 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;
}
}
BAutolock _(fLock);
if (!IsLocked())
return NULL;
// remove the head of the queue, if any, and return it
BMessage* head = fHead;
if (head == NULL)
return NULL;
fMessageCount--;
fHead = head->fQueueLink;
if (fHead == NULL) {
// If the queue is empty after removing the front element,
// we need to set the tail of the queue to NULL since the queue
// is now empty.
fTail = NULL;
}
return result;
return head;
}
void
BMessageQueue::_ReservedMessageQueue1(void)
/*!
\brief This method is only here for R5 binary compatibility!
It should be dropped as soon as possible (it misses the const qualifier).
*/
bool
BMessageQueue::IsLocked()
{
return fLock.IsLocked();
}
void
BMessageQueue::_ReservedMessageQueue2(void)
{
}
void BMessageQueue::_ReservedMessageQueue1() {}
void BMessageQueue::_ReservedMessageQueue2() {}
void BMessageQueue::_ReservedMessageQueue3() {}
void
BMessageQueue::_ReservedMessageQueue3(void)
{
}
#ifdef USE_OPENBEOS_NAMESPACE
}
#endif