root/src/kits/app/MessageQueue.cpp
/*
 * Copyright 2001-2014 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>


BMessageQueue::BMessageQueue()
        :
        fHead(NULL),
        fTail(NULL),
        fMessageCount(0),
        fLock("BMessageQueue Lock")
{
}


BMessageQueue::~BMessageQueue()
{
        if (!Lock())
                return;

        BMessage* message = fHead;
        while (message != NULL) {
                BMessage* next = message->fQueueLink;

                delete message;
                message = next;
        }
}


void
BMessageQueue::AddMessage(BMessage* message)
{
        if (message == NULL)
                return;

        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;
        }
}


void
BMessageQueue::RemoveMessage(BMessage* message)
{
        if (message == NULL)
                return;

        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;
                        else
                                last->fQueueLink = entry->fQueueLink;

                        if (entry == fTail)
                                fTail = last;

                        fMessageCount--;
                        return;
                }
                last = entry;
        }
}


int32
BMessageQueue::CountMessages() const
{
    return fMessageCount;
}


bool
BMessageQueue::IsEmpty() const
{
    return fMessageCount == 0;
}


BMessage*
BMessageQueue::FindMessage(int32 index) const
{
        BAutolock _(fLock);
        if (!IsLocked())
                return NULL;

        if (index < 0 || index >= fMessageCount)
                return NULL;

        for (BMessage* message = fHead; message != NULL; message = message->fQueueLink) {
                // If the index reaches zero, then we have found a match.
                if (index == 0)
                        return message;

                index--;
        }

    return NULL;
}


BMessage*
BMessageQueue::FindMessage(uint32 what, int32 index) const
{
        BAutolock _(fLock);
        if (!IsLocked())
                return NULL;

        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)
                                return message;

                        index--;
                }
        }

    return NULL;
}


bool
BMessageQueue::Lock()
{
    return fLock.Lock();
}


void
BMessageQueue::Unlock()
{
        fLock.Unlock();
}


bool
BMessageQueue::IsLocked() const
{
        return fLock.IsLocked();
}


BMessage*
BMessageQueue::NextMessage()
{
        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 head;
}


bool
BMessageQueue::IsNextMessage(const BMessage* message) const
{
        BAutolock _(fLock);
        return fHead == message;
}


// 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::_ReservedMessageQueue1() {}
void BMessageQueue::_ReservedMessageQueue2() {}
void BMessageQueue::_ReservedMessageQueue3() {}