root/src/servers/app/MessageLooper.cpp
/*
 * Copyright 2005-2016, Haiku.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Axel Dörfler, axeld@pinc-software.de
 */


#include "MessageLooper.h"

#include <malloc.h>
#include <stdio.h>
#include <string.h>

#include <Autolock.h>


MessageLooper::MessageLooper(const char* name)
        :
        BLocker(name),
        fName(strdup(name)),
        fThread(-1),
        fQuitting(false),
        fDeathSemaphore(-1)
{
}


MessageLooper::~MessageLooper()
{
        free((void*)fName);
}


status_t
MessageLooper::Run()
{
        BAutolock locker(this);

        fQuitting = false;

        char name[B_OS_NAME_LENGTH];
        _GetLooperName(name, sizeof(name));

        // Spawn our message-monitoring thread
        fThread = spawn_thread(_message_thread, name, B_DISPLAY_PRIORITY, this);
        if (fThread < B_OK) {
                fQuitting = true;
                return fThread;
        }

        if (resume_thread(fThread) != B_OK) {
                fQuitting = true;
                kill_thread(fThread);
                fThread = -1;
                return B_BAD_THREAD_ID;
        }

        return B_OK;
}


void
MessageLooper::Quit()
{
        fQuitting = true;
        _PrepareQuit();

        if (fThread < B_OK) {
                // thread has not been started yet
                delete this;
                return;
        }

        if (fThread == find_thread(NULL)) {
                // called from our message looper
                delete this;
                exit_thread(0);
        } else {
                // called from a different thread
                PostMessage(kMsgQuitLooper);
        }
}


/*!
        \brief Send a message to the looper without any attachments
        \param code ID code of the message to post
*/
status_t
MessageLooper::PostMessage(int32 code, bigtime_t timeout)
{
        BPrivate::LinkSender link(MessagePort());
        link.StartMessage(code);
        return link.Flush(timeout);
}


/*static*/
status_t
MessageLooper::WaitForQuit(sem_id semaphore, bigtime_t timeout)
{
        status_t status;
        do {
                status = acquire_sem_etc(semaphore, 1, B_RELATIVE_TIMEOUT, timeout);
        } while (status == B_INTERRUPTED);

        if (status == B_TIMED_OUT)
                return status;

        return B_OK;
}


void
MessageLooper::_PrepareQuit()
{
        // to be implemented by subclasses
}


void
MessageLooper::_GetLooperName(char* name, size_t length)
{
        if (fName != NULL)
                strlcpy(name, fName, length);
        else
                strlcpy(name, "unnamed looper", length);
}


void
MessageLooper::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link)
{
}


void
MessageLooper::_MessageLooper()
{
        BPrivate::LinkReceiver& receiver = fLink.Receiver();

        while (true) {
                int32 code;
                status_t status = receiver.GetNextMessage(code);
                if (status < B_OK) {
                        // that shouldn't happen, it's our port
                        char name[256];
                        _GetLooperName(name, 256);
                        printf("MessageLooper \"%s\": Someone deleted our message port %"
                                B_PRId32 ", %s!\n", name, receiver.Port(), strerror(status));
                        break;
                }

                Lock();

                if (code == kMsgQuitLooper)
                        Quit();
                else
                        _DispatchMessage(code, receiver);

                Unlock();
        }
}


/*!
        \brief Message-dispatching loop starter
*/
/*static*/
int32
MessageLooper::_message_thread(void* _looper)
{
        MessageLooper* looper = (MessageLooper*)_looper;

        looper->_MessageLooper();
        return 0;
}