<commit_before>/**
 * @file
 *
 * Define a class that abstracts Linux threads.
 */

/******************************************************************************
 * Copyright (c) 2009-2013, AllSeen Alliance. All rights reserved.
 *
 *    Permission to use, copy, modify, and/or distribute this software for any
 *    purpose with or without fee is hereby granted, provided that the above
 *    copyright notice and this permission notice appear in all copies.
 *
 *    THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *    WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *    MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *    ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *    ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 ******************************************************************************/
#include <qcc/platform.h>

#include <algorithm>
#include <assert.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <map>

#include <qcc/Debug.h>
#include <qcc/String.h>
#include <qcc/Mutex.h>
#include <qcc/Thread.h>

#include <Status.h>

using namespace std;

/** @internal */
#define QCC_MODULE "THREAD"

namespace qcc {

static uint32_t started = 0;
static uint32_t running = 0;
static uint32_t joined = 0;

/** Global thread list */
Mutex* Thread::threadListLock = NULL;
map<ThreadHandle, Thread*>* Thread::threadList = NULL;

static int threadListCounter = 0;

ThreadListInitializer::ThreadListInitializer()
{
    if (0 == threadListCounter++) {
        Thread::threadListLock = new Mutex();
        Thread::threadList = new map<ThreadHandle, Thread*>();
    }
}

ThreadListInitializer::~ThreadListInitializer()
{
    if (0 == --threadListCounter) {
        delete Thread::threadList;
        delete Thread::threadListLock;
    }
}

QStatus Sleep(uint32_t ms) {
    usleep(1000 * ms);
    return ER_OK;
}

Thread* Thread::GetThread()
{
    Thread* ret = NULL;

    /* Find thread on Thread::threadList */
    threadListLock->Lock();
    map<ThreadHandle, Thread*>::const_iterator iter = threadList->find(pthread_self());
    if (iter != threadList->end()) {
        ret = iter->second;
    }
    threadListLock->Unlock();

    /* If the current thread isn't on the list, then create an external (wrapper) thread */
    if (NULL == ret) {
        ret = new Thread("external", NULL, true);
    }

    return ret;
}

const char* Thread::GetThreadName()
{
    Thread* thread = NULL;

    /* Find thread on Thread::threadList */
    threadListLock->Lock();
    map<ThreadHandle, Thread*>::const_iterator iter = threadList->find(pthread_self());
    if (iter != threadList->end()) {
        thread = iter->second;
    }
    threadListLock->Unlock();

    /* If the current thread isn't on the list, then don't create an external (wrapper) thread */
    if (thread == NULL) {
        return "external";
    }

    return thread->GetName();
}

void Thread::CleanExternalThreads()
{
    threadListLock->Lock();
    map<ThreadHandle, Thread*>::iterator it = threadList->begin();
    while (it != threadList->end()) {
        if (it->second->isExternal) {
            delete it->second;
            threadList->erase(it++);
        } else {
            ++it;
        }
    }
    threadListLock->Unlock();
}

Thread::Thread(qcc::String name, Thread::ThreadFunction func, bool isExternal) :
    stopEvent(),
    state(isExternal ? RUNNING : INITIAL),
    isStopping(false),
    function(isExternal ? NULL : func),
    handle(isExternal ? pthread_self() : 0),
    exitValue(NULL),
    listener(NULL),
    isExternal(isExternal),
    platformContext(NULL),
    alertCode(0),
    auxListeners(),
    auxListenersLock(),
    waitCount(0),
    waitLock(),
    hasBeenJoined(false)
{
    /* qcc::String is not thread safe.  Don't use it here. */
    funcName[0] = '\0';
    strncpy(funcName, name.c_str(), sizeof(funcName));
    funcName[sizeof(funcName) - 1] = '\0';

    /* If this is an external thread, add it to the thread list here since Run will not be called */
    if (isExternal) {
        assert(func == NULL);
        threadListLock->Lock();
        (*threadList)[handle] = this;
        threadListLock->Unlock();
    }
    QCC_DbgHLPrintf(("Thread::Thread() created %s - %x -- started:%d running:%d joined:%d", funcName, handle, started, running, joined));
}

Thread::~Thread(void)
{
    QCC_DbgHLPrintf(("Thread::~Thread() destroying %s - %x", funcName, handle));

    if (!isExternal) {
        Stop();
        Join();
    }

    /* Keep object alive until waitCount goes to zero */
    while (waitCount) {
        qcc::Sleep(2);
    }

    QCC_DbgHLPrintf(("Thread::~Thread() destroyed %s - %x -- started:%d running:%d joined:%d", funcName, handle, started, running, joined));
}

ThreadInternalReturn Thread::RunInternal(void* threadArg)
{
    Thread* thread(reinterpret_cast<Thread*>(threadArg));
    sigset_t newmask;

    sigemptyset(&newmask);
    sigaddset(&newmask, SIGUSR1);

    assert(thread != NULL);

    if (thread->state != STARTED) {
        return NULL;
    }

    /* Plug race condition between Start and Run. (pthread_create may not write handle before run is called) */
    thread->handle = pthread_self();

    ++started;

    QCC_DbgPrintf(("Thread::RunInternal: %s (pid=%x)", thread->funcName, (unsigned long) thread->handle));

    /* Add this Thread to list of running threads */
    threadListLock->Lock();
    (*threadList)[thread->handle] = thread;
    thread->state = RUNNING;
    pthread_sigmask(SIG_UNBLOCK, &newmask, NULL);
    threadListLock->Unlock();

    /* Start the thread if it hasn't been stopped */
    if (!thread->isStopping) {
        QCC_DbgPrintf(("Starting thread: %s", thread->funcName));
        ++running;
        thread->exitValue = thread->Run(thread->arg);
        --running;
        QCC_DbgPrintf(("Thread function exited: %s --> %p", thread->funcName, thread->exitValue));
    }

    thread->state = STOPPING;
    thread->stopEvent.ResetEvent();

    /*
     * Call thread exit callback if specified. Note that ThreadExit may dellocate the thread so the
     * members of thread may not be accessed after this call
     */
    void* retVal = thread->exitValue;
    ThreadHandle handle = thread->handle;

    /* Call aux listeners before main listener since main listner may delete the thread */
    thread->auxListenersLock.Lock();
    ThreadListeners::iterator it = thread->auxListeners.begin();
    while (it != thread->auxListeners.end()) {
        ThreadListener* listener = *it;
        listener->ThreadExit(thread);
        it = thread->auxListeners.upper_bound(listener);
    }
    thread->auxListenersLock.Unlock();

    if (thread->listener) {
        thread->listener->ThreadExit(thread);
    }

    /* This also means no QCC_DbgPrintf as they try to get context on the current thread */

    /* Remove this Thread from list of running threads */
    threadListLock->Lock();
    threadList->erase(handle);
    threadListLock->Unlock();

    return reinterpret_cast<ThreadInternalReturn>(retVal);
}

static const uint32_t stacksize = 256 * 1024;

QStatus Thread::Start(void* arg, ThreadListener* listener)
{
    QStatus status = ER_OK;

    /* Check that thread can be started */
    if (isExternal) {
        status = ER_EXTERNAL_THREAD;
    } else if (isStopping) {
        status = ER_THREAD_STOPPING;
    } else if (IsRunning()) {
        status = ER_THREAD_RUNNING;
    }

    if (status != ER_OK) {
        QCC_LogError(status, ("Thread::Start [%s]", funcName));
    } else {
        int ret;

        /* Clear/initialize the join context */
        hasBeenJoined = false;
        waitCount = 0;

        /*  Reset the stop event so the thread doesn't start out alerted. */
        stopEvent.ResetEvent();
        /* Create OS thread */
        this->arg = arg;
        this->listener = listener;

        state = STARTED;
        pthread_attr_t attr;
        ret = pthread_attr_init(&attr);
        if (ret != 0) {
            status = ER_OS_ERROR;
            QCC_LogError(status, ("Initializing thread attr: %s", strerror(ret)));
        }
        ret = pthread_attr_setstacksize(&attr, stacksize);
        if (ret != 0) {
            status = ER_OS_ERROR;
            QCC_LogError(status, ("Setting stack size: %s", strerror(ret)));
        }
        ret = pthread_create(&handle, &attr, RunInternal, this);
        QCC_DbgTrace(("Thread::Start() [%s] pid = %x", funcName, handle));
        if (ret != 0) {
            state = DEAD;
            isStopping = false;
            status = ER_OS_ERROR;
            QCC_LogError(status, ("Creating thread %s: %s", funcName, strerror(ret)));
        }
    }
    return status;
}

QStatus Thread::Stop(void)
{
    /* Cannot stop external threads */
    if (isExternal) {
        QCC_LogError(ER_EXTERNAL_THREAD, ("Cannot stop an external thread"));
        return ER_EXTERNAL_THREAD;
    } else if ((state == DEAD) || (state == INITIAL)) {
        QCC_DbgPrintf(("Thread::Stop() thread is dead [%s]", funcName));
        return ER_OK;
    } else {
        QCC_DbgTrace(("Thread::Stop() %x [%s]", handle, funcName));
        isStopping = true;
        return stopEvent.SetEvent();
    }
}

QStatus Thread::Alert()
{
    if (state == DEAD) {
        return ER_DEAD_THREAD;
    }
    QCC_DbgTrace(("Thread::Alert() [%s:%srunning]", funcName, IsRunning() ? " " : " not "));
    return stopEvent.SetEvent();
}

QStatus Thread::Alert(uint32_t alertCode)
{
    this->alertCode = alertCode;
    if (state == DEAD) {
        return ER_DEAD_THREAD;
    }
    QCC_DbgTrace(("Thread::Alert(%u) [%s:%srunning]", alertCode, funcName, IsRunning() ? " " : " not "));
    return stopEvent.SetEvent();
}

void Thread::AddAuxListener(ThreadListener* listener)
{
    auxListenersLock.Lock();
    auxListeners.insert(listener);
    auxListenersLock.Unlock();
}

void Thread::RemoveAuxListener(ThreadListener* listener)
{
    auxListenersLock.Lock();
    ThreadListeners::iterator it = auxListeners.find(listener);
    if (it != auxListeners.end()) {
        auxListeners.erase(it);
    }
    auxListenersLock.Unlock();
}

QStatus Thread::Join(void)
{
    QStatus status = ER_OK;

    QCC_DbgTrace(("Thread::Join() [%s - %x :%srunning]", funcName, handle, IsRunning() ? " " : " not "));

    QCC_DbgPrintf(("[%s - %x] Joining thread [%s - %x]",
                   GetThread()->funcName, GetThread()->handle,
                   funcName, handle));

    /*
     * Nothing to join if the thread is dead
     */
    if (state == DEAD) {
        QCC_DbgPrintf(("Thread::Join() thread is dead [%s]", funcName));
        isStopping = false;
        return ER_OK;
    }
    /*
     * There is a race condition where the underlying OS thread has not yet started to run. We need
     * to wait until the thread is actually running before we can free it.
     */
    while (state == STARTED) {
        usleep(1000 * 5);
    }

    /* Threads that join themselves must detach without blocking */
    if (handle == pthread_self()) {
        int32_t waiters = IncrementAndFetch(&waitCount);
        hbjMutex.Lock();
        if ((waiters == 1) && !hasBeenJoined) {
            hasBeenJoined = true;
            hbjMutex.Unlock();
            int ret = pthread_detach(handle);
            handle = 0;
            if (ret == 0) {
                ++joined;
            } else {
                status = ER_OS_ERROR;
                QCC_LogError(status, ("Detaching thread: %d - %s", ret, strerror(ret)));
            }
        } else {
            hbjMutex.Unlock();

        }
        DecrementAndFetch(&waitCount);
        isStopping = false;
    } else {
        /*
         * Unfortunately, POSIX pthread_join can only be called once for a given thread. This is quite
         * inconvenient in a system of multiple threads that need to synchronize with each other.
         * This ugly looking code allows multiple threads to Join a thread. All but one thread block
         * in a Mutex. The first thread to obtain the mutex is the one that is allowed to call pthread_join.
         * All other threads wait for that join to complete. Then they are released.
         */
        int ret = 0;
        int32_t waiters = IncrementAndFetch(&waitCount);
        waitLock.Lock();
        hbjMutex.Lock();
        if ((waiters == 1) && !hasBeenJoined) {
            hasBeenJoined = true;
            hbjMutex.Unlock();
            ret = pthread_join(handle, NULL);
            handle = 0;
            ++joined;
        } else {
            hbjMutex.Unlock();

        }
        waitLock.Unlock();
        DecrementAndFetch(&waitCount);

        if (ret != 0) {
            status = ER_OS_ERROR;
            QCC_LogError(status, ("Joining thread: %d - %s", ret, strerror(ret)));
        }
        isStopping = false;
    }
    state = DEAD;
    QCC_DbgPrintf(("Joined thread %s", funcName));
    return status;
}

ThreadReturn STDCALL Thread::Run(void* arg)
{
    QCC_DbgTrace(("Thread::Run() [%s:%srunning]", funcName, IsRunning() ? " " : " not "));
    assert(NULL != function);
    assert(!isExternal);
    return (*function)(arg);
}

} /* namespace */
<commit_msg>ASACORE-502: Allow Join() on Thread never Start()ed<commit_after>/**
 * @file
 *
 * Define a class that abstracts Linux threads.
 */

/******************************************************************************
 * Copyright (c) 2009-2013, AllSeen Alliance. All rights reserved.
 *
 *    Permission to use, copy, modify, and/or distribute this software for any
 *    purpose with or without fee is hereby granted, provided that the above
 *    copyright notice and this permission notice appear in all copies.
 *
 *    THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *    WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *    MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *    ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *    ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 ******************************************************************************/
#include <qcc/platform.h>

#include <algorithm>
#include <assert.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <map>

#include <qcc/Debug.h>
#include <qcc/String.h>
#include <qcc/Mutex.h>
#include <qcc/Thread.h>

#include <Status.h>

using namespace std;

/** @internal */
#define QCC_MODULE "THREAD"

namespace qcc {

static uint32_t started = 0;
static uint32_t running = 0;
static uint32_t joined = 0;

/** Global thread list */
Mutex* Thread::threadListLock = NULL;
map<ThreadHandle, Thread*>* Thread::threadList = NULL;

static int threadListCounter = 0;

ThreadListInitializer::ThreadListInitializer()
{
    if (0 == threadListCounter++) {
        Thread::threadListLock = new Mutex();
        Thread::threadList = new map<ThreadHandle, Thread*>();
    }
}

ThreadListInitializer::~ThreadListInitializer()
{
    if (0 == --threadListCounter) {
        delete Thread::threadList;
        delete Thread::threadListLock;
    }
}

QStatus Sleep(uint32_t ms) {
    usleep(1000 * ms);
    return ER_OK;
}

Thread* Thread::GetThread()
{
    Thread* ret = NULL;

    /* Find thread on Thread::threadList */
    threadListLock->Lock();
    map<ThreadHandle, Thread*>::const_iterator iter = threadList->find(pthread_self());
    if (iter != threadList->end()) {
        ret = iter->second;
    }
    threadListLock->Unlock();

    /* If the current thread isn't on the list, then create an external (wrapper) thread */
    if (NULL == ret) {
        ret = new Thread("external", NULL, true);
    }

    return ret;
}

const char* Thread::GetThreadName()
{
    Thread* thread = NULL;

    /* Find thread on Thread::threadList */
    threadListLock->Lock();
    map<ThreadHandle, Thread*>::const_iterator iter = threadList->find(pthread_self());
    if (iter != threadList->end()) {
        thread = iter->second;
    }
    threadListLock->Unlock();

    /* If the current thread isn't on the list, then don't create an external (wrapper) thread */
    if (thread == NULL) {
        return "external";
    }

    return thread->GetName();
}

void Thread::CleanExternalThreads()
{
    threadListLock->Lock();
    map<ThreadHandle, Thread*>::iterator it = threadList->begin();
    while (it != threadList->end()) {
        if (it->second->isExternal) {
            delete it->second;
            threadList->erase(it++);
        } else {
            ++it;
        }
    }
    threadListLock->Unlock();
}

Thread::Thread(qcc::String name, Thread::ThreadFunction func, bool isExternal) :
    stopEvent(),
    state(isExternal ? RUNNING : INITIAL),
    isStopping(false),
    function(isExternal ? NULL : func),
    handle(isExternal ? pthread_self() : 0),
    exitValue(NULL),
    listener(NULL),
    isExternal(isExternal),
    platformContext(NULL),
    alertCode(0),
    auxListeners(),
    auxListenersLock(),
    waitCount(0),
    waitLock(),
    hasBeenJoined(false)
{
    /* qcc::String is not thread safe.  Don't use it here. */
    funcName[0] = '\0';
    strncpy(funcName, name.c_str(), sizeof(funcName));
    funcName[sizeof(funcName) - 1] = '\0';

    /* If this is an external thread, add it to the thread list here since Run will not be called */
    if (isExternal) {
        assert(func == NULL);
        threadListLock->Lock();
        (*threadList)[handle] = this;
        threadListLock->Unlock();
    }
    QCC_DbgHLPrintf(("Thread::Thread() created %s - %x -- started:%d running:%d joined:%d", funcName, handle, started, running, joined));
}

Thread::~Thread(void)
{
    QCC_DbgHLPrintf(("Thread::~Thread() destroying %s - %x", funcName, handle));

    if (!isExternal) {
        Stop();
        Join();
    }

    /* Keep object alive until waitCount goes to zero */
    while (waitCount) {
        qcc::Sleep(2);
    }

    QCC_DbgHLPrintf(("Thread::~Thread() destroyed %s - %x -- started:%d running:%d joined:%d", funcName, handle, started, running, joined));
}

ThreadInternalReturn Thread::RunInternal(void* threadArg)
{
    Thread* thread(reinterpret_cast<Thread*>(threadArg));
    sigset_t newmask;

    sigemptyset(&newmask);
    sigaddset(&newmask, SIGUSR1);

    assert(thread != NULL);

    /* Plug race condition between Start and Run. (pthread_create may not write handle before run is called) */
    thread->handle = pthread_self();

    if (thread->state != STARTED) {
        return NULL;
    }

    ++started;

    QCC_DbgPrintf(("Thread::RunInternal: %s (pid=%x)", thread->funcName, (unsigned long) thread->handle));

    /* Add this Thread to list of running threads */
    threadListLock->Lock();
    (*threadList)[thread->handle] = thread;
    thread->state = RUNNING;
    pthread_sigmask(SIG_UNBLOCK, &newmask, NULL);
    threadListLock->Unlock();

    /* Start the thread if it hasn't been stopped */
    if (!thread->isStopping) {
        QCC_DbgPrintf(("Starting thread: %s", thread->funcName));
        ++running;
        thread->exitValue = thread->Run(thread->arg);
        --running;
        QCC_DbgPrintf(("Thread function exited: %s --> %p", thread->funcName, thread->exitValue));
    }

    thread->state = STOPPING;
    thread->stopEvent.ResetEvent();

    /*
     * Call thread exit callback if specified. Note that ThreadExit may dellocate the thread so the
     * members of thread may not be accessed after this call
     */
    void* retVal = thread->exitValue;
    ThreadHandle handle = thread->handle;

    /* Call aux listeners before main listener since main listner may delete the thread */
    thread->auxListenersLock.Lock();
    ThreadListeners::iterator it = thread->auxListeners.begin();
    while (it != thread->auxListeners.end()) {
        ThreadListener* listener = *it;
        listener->ThreadExit(thread);
        it = thread->auxListeners.upper_bound(listener);
    }
    thread->auxListenersLock.Unlock();

    if (thread->listener) {
        thread->listener->ThreadExit(thread);
    }

    /* This also means no QCC_DbgPrintf as they try to get context on the current thread */

    /* Remove this Thread from list of running threads */
    threadListLock->Lock();
    threadList->erase(handle);
    threadListLock->Unlock();

    return reinterpret_cast<ThreadInternalReturn>(retVal);
}

static const uint32_t stacksize = 256 * 1024;

QStatus Thread::Start(void* arg, ThreadListener* listener)
{
    QStatus status = ER_OK;

    /* Check that thread can be started */
    if (isExternal) {
        status = ER_EXTERNAL_THREAD;
    } else if (isStopping) {
        status = ER_THREAD_STOPPING;
    } else if (IsRunning()) {
        status = ER_THREAD_RUNNING;
    }

    if (status != ER_OK) {
        QCC_LogError(status, ("Thread::Start [%s]", funcName));
    } else {
        int ret;

        /* Clear/initialize the join context */
        hasBeenJoined = false;
        waitCount = 0;

        /*  Reset the stop event so the thread doesn't start out alerted. */
        stopEvent.ResetEvent();
        /* Create OS thread */
        this->arg = arg;
        this->listener = listener;

        state = STARTED;
        pthread_attr_t attr;
        ret = pthread_attr_init(&attr);
        if (ret != 0) {
            status = ER_OS_ERROR;
            QCC_LogError(status, ("Initializing thread attr: %s", strerror(ret)));
        }
        ret = pthread_attr_setstacksize(&attr, stacksize);
        if (ret != 0) {
            status = ER_OS_ERROR;
            QCC_LogError(status, ("Setting stack size: %s", strerror(ret)));
        }
        ret = pthread_create(&handle, &attr, RunInternal, this);
        QCC_DbgTrace(("Thread::Start() [%s] pid = %x", funcName, handle));
        if (ret != 0) {
            state = DEAD;
            isStopping = false;
            status = ER_OS_ERROR;
            QCC_LogError(status, ("Creating thread %s: %s", funcName, strerror(ret)));
        }
    }
    return status;
}

QStatus Thread::Stop(void)
{
    /* Cannot stop external threads */
    if (isExternal) {
        QCC_LogError(ER_EXTERNAL_THREAD, ("Cannot stop an external thread"));
        return ER_EXTERNAL_THREAD;
    } else if ((state == DEAD) || (state == INITIAL)) {
        QCC_DbgPrintf(("Thread::Stop() thread is dead [%s]", funcName));
        return ER_OK;
    } else {
        QCC_DbgTrace(("Thread::Stop() %x [%s]", handle, funcName));
        isStopping = true;
        return stopEvent.SetEvent();
    }
}

QStatus Thread::Alert()
{
    if (state == DEAD) {
        return ER_DEAD_THREAD;
    }
    QCC_DbgTrace(("Thread::Alert() [%s:%srunning]", funcName, IsRunning() ? " " : " not "));
    return stopEvent.SetEvent();
}

QStatus Thread::Alert(uint32_t alertCode)
{
    this->alertCode = alertCode;
    if (state == DEAD) {
        return ER_DEAD_THREAD;
    }
    QCC_DbgTrace(("Thread::Alert(%u) [%s:%srunning]", alertCode, funcName, IsRunning() ? " " : " not "));
    return stopEvent.SetEvent();
}

void Thread::AddAuxListener(ThreadListener* listener)
{
    auxListenersLock.Lock();
    auxListeners.insert(listener);
    auxListenersLock.Unlock();
}

void Thread::RemoveAuxListener(ThreadListener* listener)
{
    auxListenersLock.Lock();
    ThreadListeners::iterator it = auxListeners.find(listener);
    if (it != auxListeners.end()) {
        auxListeners.erase(it);
    }
    auxListenersLock.Unlock();
}

QStatus Thread::Join(void)
{
    QStatus status = ER_OK;

    QCC_DbgTrace(("Thread::Join() [%s - %x :%srunning]", funcName, handle, IsRunning() ? " " : " not "));

    QCC_DbgPrintf(("[%s - %x] Joining thread [%s - %x]",
                   GetThread()->funcName, GetThread()->handle,
                   funcName, handle));

    /*
     * Nothing to join if the thread is dead
     */
    if (state == DEAD) {
        QCC_DbgPrintf(("Thread::Join() thread is dead [%s]", funcName));
        isStopping = false;
        return ER_OK;
    }
    /*
     * There is a race condition where the underlying OS thread has not yet started to run. We need
     * to wait until the thread is actually running before we can free it.
     */
    while (state == STARTED) {
        usleep(1000 * 5);
    }

    /* Threads that join themselves must detach without blocking */
    if (handle == pthread_self()) {
        int32_t waiters = IncrementAndFetch(&waitCount);
        hbjMutex.Lock();
        if ((waiters == 1) && !hasBeenJoined) {
            hasBeenJoined = true;
            hbjMutex.Unlock();
            int ret = 0;
            if (state != INITIAL) {
                assert(handle);
                ret = pthread_detach(handle);
            }
            if (ret == 0) {
                ++joined;
            } else {
                status = ER_OS_ERROR;
                QCC_LogError(status, ("Detaching thread: %d - %s", ret, strerror(ret)));
            }
            handle = 0;
        } else {
            hbjMutex.Unlock();

        }
        DecrementAndFetch(&waitCount);
        isStopping = false;
    } else {
        /*
         * Unfortunately, POSIX pthread_join can only be called once for a given thread. This is quite
         * inconvenient in a system of multiple threads that need to synchronize with each other.
         * This ugly looking code allows multiple threads to Join a thread. All but one thread block
         * in a Mutex. The first thread to obtain the mutex is the one that is allowed to call pthread_join.
         * All other threads wait for that join to complete. Then they are released.
         */
        int ret = 0;
        int32_t waiters = IncrementAndFetch(&waitCount);
        waitLock.Lock();
        hbjMutex.Lock();
        if ((waiters == 1) && !hasBeenJoined) {
            hasBeenJoined = true;
            hbjMutex.Unlock();
            if (state != INITIAL) {
                assert(handle);
                ret = pthread_join(handle, NULL);
            }
            handle = 0;
            ++joined;
        } else {
            hbjMutex.Unlock();

        }
        waitLock.Unlock();
        DecrementAndFetch(&waitCount);

        if (ret != 0) {
            status = ER_OS_ERROR;
            QCC_LogError(status, ("Joining thread: %d - %s", ret, strerror(ret)));
        }
        isStopping = false;
    }
    state = DEAD;
    QCC_DbgPrintf(("Joined thread %s", funcName));
    return status;
}

ThreadReturn STDCALL Thread::Run(void* arg)
{
    QCC_DbgTrace(("Thread::Run() [%s:%srunning]", funcName, IsRunning() ? " " : " not "));
    assert(NULL != function);
    assert(!isExternal);
    return (*function)(arg);
}

} /* namespace */
<|endoftext|>


<commit_before>//==============================================================================
// CellML annotation view metadata details widget
//==============================================================================

#include "borderedwidget.h"
#include "cellmlannotationviewmetadatadetailswidget.h"
#include "cellmlannotationviewmetadataviewdetailswidget.h"
#include "cellmlannotationviewplugin.h"
#include "cellmlannotationviewwidget.h"

//==============================================================================

#include "ui_cellmlannotationviewmetadatadetailswidget.h"

//==============================================================================

#include <QLabel>

//==============================================================================

namespace OpenCOR {
namespace CellMLAnnotationView {

//==============================================================================

CellmlAnnotationViewMetadataDetailsWidget::CellmlAnnotationViewMetadataDetailsWidget(CellmlAnnotationViewWidget *pParent) :
    Widget(pParent),
    mParent(pParent),
    mGui(new Ui::CellmlAnnotationViewMetadataDetailsWidget)
{
    // Set up the GUI

    mGui->setupUi(this);

    // Create our unsupported metadata message widget

    mUnsupportedMetadataMsg = new QLabel(pParent);

    QFont unsupportedMetadataMsgFont = mUnsupportedMetadataMsg->font();

    unsupportedMetadataMsgFont.setPointSize(1.5*unsupportedMetadataMsgFont.pointSize());

    mUnsupportedMetadataMsg->setAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
    mUnsupportedMetadataMsg->setFont(unsupportedMetadataMsgFont);
    mUnsupportedMetadataMsg->setSizePolicy(QSizePolicy::Expanding,
                                           QSizePolicy::Expanding);
    mUnsupportedMetadataMsg->setWordWrap(true);

    mBorderedUnsupportedMetadataMsg = new Core::BorderedWidget(mUnsupportedMetadataMsg,
                                                               false, true, true, false);

    mBorderedUnsupportedMetadataMsg->setVisible(false);
    // Note: we don't initially want to see it, so...

    // Create our details widget

    mMetadataViewDetails = new CellmlAnnotationViewMetadataViewDetailsWidget(pParent);

    mBorderedMetadataViewDetails = new Core::BorderedWidget(mMetadataViewDetails,
                                                            false, true, false, false);

    // Add our bordered widgets to our layout

    mGui->layout->addWidget(mBorderedUnsupportedMetadataMsg);
    mGui->layout->addWidget(mBorderedMetadataViewDetails);

    // Some further initialisations which are done as part of retranslating the
    // GUI (so that they can be updated when changing languages)

    retranslateUi();
}

//==============================================================================

CellmlAnnotationViewMetadataDetailsWidget::~CellmlAnnotationViewMetadataDetailsWidget()
{
    // Delete the GUI

    delete mGui;
}

//==============================================================================

void CellmlAnnotationViewMetadataDetailsWidget::retranslateUi()
{
    // Retranslate our GUI

    mGui->retranslateUi(this);

    mMetadataViewDetails->retranslateUi();

    // Update our unsupported metadata message

    mUnsupportedMetadataMsg->setText(tr("Sorry, but the %1 view does not support this type of metadata...").arg(mParent->pluginParent()->viewName()));
}

//==============================================================================

void CellmlAnnotationViewMetadataDetailsWidget::updateGui(const CellMLSupport::CellmlFileRdfTriples &pRdfTriples)
{
    // Show/hide our unsupported metadata message depending on whether the type
    // of the RDF triples is known or not

    CellMLSupport::CellmlFileRdfTriple::Type rdfTriplesType = pRdfTriples.type();

    mBorderedUnsupportedMetadataMsg->setVisible(rdfTriplesType == CellMLSupport::CellmlFileRdfTriple::Unknown);
    mBorderedMetadataViewDetails->setVisible(rdfTriplesType == CellMLSupport::CellmlFileRdfTriple::Unknown);

    // Update our Metadata view details GUI

    if (mBorderedMetadataViewDetails->isVisible())
        mMetadataViewDetails->updateGui(pRdfTriples);
}

//==============================================================================

}   // namespace CellMLAnnotationView
}   // namespace OpenCOR

//==============================================================================
// End of file
//==============================================================================
<commit_msg>Resuming our work on the creation/editing of metadata (#78).<commit_after>//==============================================================================
// CellML annotation view metadata details widget
//==============================================================================

#include "borderedwidget.h"
#include "cellmlannotationviewmetadatadetailswidget.h"
#include "cellmlannotationviewmetadataviewdetailswidget.h"
#include "cellmlannotationviewplugin.h"
#include "cellmlannotationviewwidget.h"

//==============================================================================

#include "ui_cellmlannotationviewmetadatadetailswidget.h"

//==============================================================================

#include <QLabel>

//==============================================================================

namespace OpenCOR {
namespace CellMLAnnotationView {

//==============================================================================

CellmlAnnotationViewMetadataDetailsWidget::CellmlAnnotationViewMetadataDetailsWidget(CellmlAnnotationViewWidget *pParent) :
    Widget(pParent),
    mParent(pParent),
    mGui(new Ui::CellmlAnnotationViewMetadataDetailsWidget)
{
    // Set up the GUI

    mGui->setupUi(this);

    // Create our unsupported metadata message widget

    mUnsupportedMetadataMsg = new QLabel(pParent);

    QFont unsupportedMetadataMsgFont = mUnsupportedMetadataMsg->font();

    unsupportedMetadataMsgFont.setPointSize(1.5*unsupportedMetadataMsgFont.pointSize());

    mUnsupportedMetadataMsg->setAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
    mUnsupportedMetadataMsg->setFont(unsupportedMetadataMsgFont);
    mUnsupportedMetadataMsg->setSizePolicy(QSizePolicy::Expanding,
                                           QSizePolicy::Expanding);
    mUnsupportedMetadataMsg->setWordWrap(true);

    mBorderedUnsupportedMetadataMsg = new Core::BorderedWidget(mUnsupportedMetadataMsg,
                                                               false, true, true, false);

    mBorderedUnsupportedMetadataMsg->setVisible(false);
    // Note: we don't initially want to see it, so...

    // Create our details widget

    mMetadataViewDetails = new CellmlAnnotationViewMetadataViewDetailsWidget(pParent);

    mBorderedMetadataViewDetails = new Core::BorderedWidget(mMetadataViewDetails,
                                                            false, true, false, false);

    // Add our bordered widgets to our layout

    mGui->layout->addWidget(mBorderedUnsupportedMetadataMsg);
    mGui->layout->addWidget(mBorderedMetadataViewDetails);

    // Some further initialisations which are done as part of retranslating the
    // GUI (so that they can be updated when changing languages)

    retranslateUi();
}

//==============================================================================

CellmlAnnotationViewMetadataDetailsWidget::~CellmlAnnotationViewMetadataDetailsWidget()
{
    // Delete the GUI

    delete mGui;
}

//==============================================================================

void CellmlAnnotationViewMetadataDetailsWidget::retranslateUi()
{
    // Retranslate our GUI

    mGui->retranslateUi(this);

    mMetadataViewDetails->retranslateUi();

    // Update our unsupported metadata message

    mUnsupportedMetadataMsg->setText(tr("Sorry, but the %1 view does not support this type of metadata...").arg(mParent->pluginParent()->viewName()));
}

//==============================================================================

void CellmlAnnotationViewMetadataDetailsWidget::updateGui(const CellMLSupport::CellmlFileRdfTriples &pRdfTriples)
{
    // Show/hide our unsupported metadata message depending on whether the type
    // of the RDF triples is known or not

    mBorderedUnsupportedMetadataMsg->setVisible(pRdfTriples.type() == CellMLSupport::CellmlFileRdfTriple::Unknown);

    // Update our Metadata view details GUI

    mMetadataViewDetails->updateGui(pRdfTriples);
}

//==============================================================================

}   // namespace CellMLAnnotationView
}   // namespace OpenCOR

//==============================================================================
// End of file
//==============================================================================
<|endoftext|>


<commit_before>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author:  Vassil Vassilev <vasil.georgiev.vasilev@cern.ch>
//------------------------------------------------------------------------------

#include "cling/Interpreter/DynamicLibraryManager.h"
#include "cling/Interpreter/InvocationOptions.h"

#include "llvm/Support/DynamicLibrary.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"

#include <stdlib.h>

#ifdef WIN32
#include <Windows.h>
#else
#include <limits.h> /* PATH_MAX */
#include <dlfcn.h>
#endif

namespace cling {
  DynamicLibraryManager::DynamicLibraryManager(const InvocationOptions& Opts)
    : m_Opts(Opts) { }

  DynamicLibraryManager::~DynamicLibraryManager() {}

  static bool isSharedLib(llvm::StringRef LibName, bool& exists) {
    using namespace llvm::sys::fs;
    file_magic Magic;
    llvm::error_code Error = identify_magic(LibName, Magic);
    exists = (Error == llvm::errc::success);
    return exists &&
#ifdef __APPLE__
        (Magic == file_magic::macho_fixed_virtual_memory_shared_lib
         || Magic == file_magic::macho_dynamically_linked_shared_lib
         || Magic == file_magic::macho_dynamically_linked_shared_lib_stub)
#elif defined(LLVM_ON_UNIX)
        Magic == file_magic::elf_shared_object
#elif defined(LLVM_ON_WIN32)
# error "Windows DLLs  not yet implemented!"
        //Magic == file_magic::pecoff_executable?
#else
# error "Unsupported platform."
#endif
      ;
  }

  static void
  findSharedLibrary(llvm::StringRef fileStem,
                    const llvm::SmallVectorImpl<std::string>& Paths,
                    llvm::SmallString<512>& FoundDyLib,
                    bool& exists, bool& isDyLib) {
    for (llvm::SmallVectorImpl<std::string>::const_iterator
        IPath = Paths.begin(), EPath = Paths.end(); IPath != EPath; ++IPath) {
      llvm::SmallString<512> ThisPath(*IPath);
      llvm::sys::path::append(ThisPath, fileStem);
      isDyLib = isSharedLib(ThisPath.str(), exists);
      if (isDyLib)
        ThisPath.swap(FoundDyLib);
      if (exists)
        return;
    }
  }

#if defined(LLVM_ON_UNIX)
  static void GetSystemLibraryPaths(llvm::SmallVectorImpl<std::string>& Paths) {
    char* env_var = getenv("LD_LIBRARY_PATH");
#if __APPLE__
    if (!env_var)
      env_var = getenv("DYLD_LIBRARY_PATH");
    if (!env_var)
      env_var = getenv("DYLD_FALLBACK_LIBRARY_PATH");
#endif
    if (env_var != 0) {
      static const char PathSeparator = ':';
      const char* at = env_var;
      const char* delim = strchr(at, PathSeparator);
      while (delim != 0) {
        std::string tmp(at, size_t(delim-at));
        if (llvm::sys::fs::is_directory(tmp.c_str()))
            Paths.push_back(tmp);
        at = delim + 1;
        delim = strchr(at, PathSeparator);
      }

      if (*at != 0)
        if (llvm::sys::fs::is_directory(llvm::StringRef(at)))
          Paths.push_back(at);
    }

    Paths.push_back("/usr/local/lib/");
    Paths.push_back("/usr/X11R6/lib/");
    Paths.push_back("/usr/lib/");
    Paths.push_back("/lib/");
  }
#elif defined(LLVM_ON_WIN32)
  static void GetSystemLibraryPaths(llvm::SmallVectorImpl<std::string>& Paths) {
    char buff[MAX_PATH];
    // Generic form of C:\Windows\System32
    HRESULT res =  SHGetFolderPathA(NULL,
                                    CSIDL_FLAG_CREATE | CSIDL_SYSTEM,
                                    NULL,
                                    SHGFP_TYPE_CURRENT,
                                    buff);
    if (res != S_OK) {
      assert(0 && "Failed to get system directory");
      return;
    }
    Paths.push_back(buff);

    // Reset buff.
    buff[0] = 0;
    // Generic form of C:\Windows
    res =  SHGetFolderPathA(NULL,
                            CSIDL_FLAG_CREATE | CSIDL_WINDOWS,
                            NULL,
                            SHGFP_TYPE_CURRENT,
                            buff);
    if (res != S_OK) {
      assert(0 && "Failed to get windows directory");
      return;
    }
    Paths.push_back(buff);
  }
#else
# error "Unsupported platform."
#endif

  DynamicLibraryManager::LoadLibResult
  DynamicLibraryManager::tryLinker(const std::string& filename, bool permanent,
                                   bool isAbsolute, bool& exists,
                                   bool& isDyLib) {
    using namespace llvm::sys;
    exists = false;
    isDyLib = false;

    llvm::SmallString<512> FoundDyLib;

    if (isAbsolute) {
      isDyLib = isSharedLib(filename, exists);
      if (isDyLib)
        FoundDyLib = filename;
    } else {
      llvm::SmallVector<std::string, 16>
        SearchPaths(m_Opts.LibSearchPath.begin(), m_Opts.LibSearchPath.end());
      GetSystemLibraryPaths(SearchPaths);

      findSharedLibrary(filename, SearchPaths, FoundDyLib, exists, isDyLib);

      if (!exists) {
        // Add DyLib extension:
        llvm::SmallString<512> filenameWithExt(filename);
#if defined(LLVM_ON_UNIX)
#ifdef __APPLE__
        llvm::SmallString<512>::iterator IStemEnd = filenameWithExt.end() - 1;
#endif
        static const char* DyLibExt = ".so";
#elif defined(LLVM_ON_WIN32)
        static const char* DyLibExt = ".dll";
#else
# error "Unsupported platform."
#endif
        filenameWithExt += DyLibExt;
        findSharedLibrary(filenameWithExt, SearchPaths, FoundDyLib, exists,
                          isDyLib);
#ifdef __APPLE__
        if (!exists) {
           filenameWithExt.erase(IStemEnd + 1, filenameWithExt.end());
           filenameWithExt += ".dylib";
           findSharedLibrary(filenameWithExt, SearchPaths, FoundDyLib, exists,
                             isDyLib);
        }
#endif
      }
    }

    if (!isDyLib)
      return kLoadLibError;

    assert(!FoundDyLib.empty() && "The shared lib exists but can't find it!");

    // get canonical path name and check if already loaded
#ifdef WIN32
    llvm::SmallString<_MAX_PATH> FullPath(_MAX_PATH);
    char *res = _fullpath((char *)FullPath.data(), FoundDyLib.c_str(), _MAX_PATH);
#else
    llvm::SmallString<PATH_MAX+1> FullPath(PATH_MAX+1);
    char *res = realpath(FoundDyLib.c_str(), (char *)FullPath.data());
#endif
    if (res == 0) {
      llvm::errs() << "cling::Interpreter::tryLinker(): error getting real (canonical) path\n";
      return kLoadLibError;
    }
    FullPath.set_size(strlen(res));
    if (m_loadedLibraries.find(FullPath) != m_loadedLibraries.end())
      return kLoadLibExists;

    // TODO: !permanent case
#ifdef WIN32
# error "Windows DLL opening still needs to be implemented!"
    void* dyLibHandle = needs to be implemented!;
    std::string errMsg;
#else
    const void* dyLibHandle = dlopen(FullPath.c_str(), RTLD_LAZY|RTLD_GLOBAL);
    std::string errMsg;
    if (const char* DyLibError = dlerror()) {
      errMsg = DyLibError;
    }
#endif
    if (!dyLibHandle) {
      llvm::errs() << "cling::Interpreter::tryLinker(): " << errMsg << '\n';
      return kLoadLibError;
    }
    std::pair<DyLibs::iterator, bool> insRes
      = m_DyLibs.insert(std::pair<DyLibHandle, std::string>(dyLibHandle,
                                                            FullPath.str()));
    if (!insRes.second)
      return kLoadLibExists;
    m_loadedLibraries.insert(FullPath);
    return kLoadLibSuccess;
  }

  DynamicLibraryManager::LoadLibResult
  DynamicLibraryManager::loadLibrary(const std::string& filename,
                                     bool permanent, bool* tryCode) {
    llvm::SmallString<128> Absolute((llvm::StringRef(filename)));
    llvm::sys::fs::make_absolute(Absolute);
    bool isAbsolute = filename == Absolute.c_str();
    bool exists = false;
    bool isDyLib = false;
    LoadLibResult res = tryLinker(filename, permanent, isAbsolute, exists,
                                  isDyLib);
    if (tryCode) {
      *tryCode = !isDyLib;
      if (isAbsolute)
        *tryCode &= exists;
    }
    if (exists)
      return res;

    if (!isAbsolute && filename.compare(0, 3, "lib")) {
      // try with "lib" prefix:
      res = tryLinker("lib" + filename, permanent, false, exists, isDyLib);
      if (tryCode) {
        *tryCode = !isDyLib;
        if (isAbsolute)
          *tryCode &= exists;
      }
      if (res != kLoadLibError)
        return res;
    }
    return kLoadLibError;
  }

  bool
  DynamicLibraryManager::isDynamicLibraryLoaded(llvm::StringRef fullPath) const {
    // get canonical path name and check if already loaded
#ifdef WIN32
    char buf[_MAX_PATH];
    char *res = _fullpath(buf, fullPath.str().c_str(), _MAX_PATH);
#else
    char buf[PATH_MAX+1];
    char *res = realpath(fullPath.str().c_str(), buf);
#endif
    if (res == 0) {
      llvm::errs() << "cling::Interpreter::isDynamicLibraryLoaded(): error getting real (canonical) path\n";
      return false;
    }
    if (m_loadedLibraries.find(buf) != m_loadedLibraries.end()) return true;
    return false;
  }

  void DynamicLibraryManager::ExposeHiddenSharedLibrarySymbols(void* handle) {
    llvm::sys::DynamicLibrary::addPermanentLibrary(const_cast<void*>(handle));
  }
} // end namespace cling
<commit_msg>Use set_size so make sure the llvm::SmallString is not empty<commit_after>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author:  Vassil Vassilev <vasil.georgiev.vasilev@cern.ch>
//------------------------------------------------------------------------------

#include "cling/Interpreter/DynamicLibraryManager.h"
#include "cling/Interpreter/InvocationOptions.h"

#include "llvm/Support/DynamicLibrary.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"

#include <stdlib.h>

#ifdef WIN32
#include <Windows.h>
#else
#include <limits.h> /* PATH_MAX */
#include <dlfcn.h>
#endif

namespace cling {
  DynamicLibraryManager::DynamicLibraryManager(const InvocationOptions& Opts)
    : m_Opts(Opts) { }

  DynamicLibraryManager::~DynamicLibraryManager() {}

  static bool isSharedLib(llvm::StringRef LibName, bool& exists) {
    using namespace llvm::sys::fs;
    file_magic Magic;
    llvm::error_code Error = identify_magic(LibName, Magic);
    exists = (Error == llvm::errc::success);
    return exists &&
#ifdef __APPLE__
        (Magic == file_magic::macho_fixed_virtual_memory_shared_lib
         || Magic == file_magic::macho_dynamically_linked_shared_lib
         || Magic == file_magic::macho_dynamically_linked_shared_lib_stub)
#elif defined(LLVM_ON_UNIX)
        Magic == file_magic::elf_shared_object
#elif defined(LLVM_ON_WIN32)
# error "Windows DLLs  not yet implemented!"
        //Magic == file_magic::pecoff_executable?
#else
# error "Unsupported platform."
#endif
      ;
  }

  static void
  findSharedLibrary(llvm::StringRef fileStem,
                    const llvm::SmallVectorImpl<std::string>& Paths,
                    llvm::SmallString<512>& FoundDyLib,
                    bool& exists, bool& isDyLib) {
    for (llvm::SmallVectorImpl<std::string>::const_iterator
        IPath = Paths.begin(), EPath = Paths.end(); IPath != EPath; ++IPath) {
      llvm::SmallString<512> ThisPath(*IPath);
      llvm::sys::path::append(ThisPath, fileStem);
      isDyLib = isSharedLib(ThisPath.str(), exists);
      if (isDyLib)
        ThisPath.swap(FoundDyLib);
      if (exists)
        return;
    }
  }

#if defined(LLVM_ON_UNIX)
  static void GetSystemLibraryPaths(llvm::SmallVectorImpl<std::string>& Paths) {
    char* env_var = getenv("LD_LIBRARY_PATH");
#if __APPLE__
    if (!env_var)
      env_var = getenv("DYLD_LIBRARY_PATH");
    if (!env_var)
      env_var = getenv("DYLD_FALLBACK_LIBRARY_PATH");
#endif
    if (env_var != 0) {
      static const char PathSeparator = ':';
      const char* at = env_var;
      const char* delim = strchr(at, PathSeparator);
      while (delim != 0) {
        std::string tmp(at, size_t(delim-at));
        if (llvm::sys::fs::is_directory(tmp.c_str()))
            Paths.push_back(tmp);
        at = delim + 1;
        delim = strchr(at, PathSeparator);
      }

      if (*at != 0)
        if (llvm::sys::fs::is_directory(llvm::StringRef(at)))
          Paths.push_back(at);
    }

    Paths.push_back("/usr/local/lib/");
    Paths.push_back("/usr/X11R6/lib/");
    Paths.push_back("/usr/lib/");
    Paths.push_back("/lib/");
  }
#elif defined(LLVM_ON_WIN32)
  static void GetSystemLibraryPaths(llvm::SmallVectorImpl<std::string>& Paths) {
    char buff[MAX_PATH];
    // Generic form of C:\Windows\System32
    HRESULT res =  SHGetFolderPathA(NULL,
                                    CSIDL_FLAG_CREATE | CSIDL_SYSTEM,
                                    NULL,
                                    SHGFP_TYPE_CURRENT,
                                    buff);
    if (res != S_OK) {
      assert(0 && "Failed to get system directory");
      return;
    }
    Paths.push_back(buff);

    // Reset buff.
    buff[0] = 0;
    // Generic form of C:\Windows
    res =  SHGetFolderPathA(NULL,
                            CSIDL_FLAG_CREATE | CSIDL_WINDOWS,
                            NULL,
                            SHGFP_TYPE_CURRENT,
                            buff);
    if (res != S_OK) {
      assert(0 && "Failed to get windows directory");
      return;
    }
    Paths.push_back(buff);
  }
#else
# error "Unsupported platform."
#endif

  DynamicLibraryManager::LoadLibResult
  DynamicLibraryManager::tryLinker(const std::string& filename, bool permanent,
                                   bool isAbsolute, bool& exists,
                                   bool& isDyLib) {
    using namespace llvm::sys;
    exists = false;
    isDyLib = false;

    llvm::SmallString<512> FoundDyLib;

    if (isAbsolute) {
      isDyLib = isSharedLib(filename, exists);
      if (isDyLib)
        FoundDyLib = filename;
    } else {
      llvm::SmallVector<std::string, 16>
        SearchPaths(m_Opts.LibSearchPath.begin(), m_Opts.LibSearchPath.end());
      GetSystemLibraryPaths(SearchPaths);

      findSharedLibrary(filename, SearchPaths, FoundDyLib, exists, isDyLib);

      if (!exists) {
        // Add DyLib extension:
        llvm::SmallString<512> filenameWithExt(filename);
#if defined(LLVM_ON_UNIX)
#ifdef __APPLE__
        llvm::SmallString<512>::iterator IStemEnd = filenameWithExt.end() - 1;
#endif
        static const char* DyLibExt = ".so";
#elif defined(LLVM_ON_WIN32)
        static const char* DyLibExt = ".dll";
#else
# error "Unsupported platform."
#endif
        filenameWithExt += DyLibExt;
        findSharedLibrary(filenameWithExt, SearchPaths, FoundDyLib, exists,
                          isDyLib);
#ifdef __APPLE__
        if (!exists) {
           filenameWithExt.erase(IStemEnd + 1, filenameWithExt.end());
           filenameWithExt += ".dylib";
           findSharedLibrary(filenameWithExt, SearchPaths, FoundDyLib, exists,
                             isDyLib);
        }
#endif
      }
    }

    if (!isDyLib)
      return kLoadLibError;

    assert(!FoundDyLib.empty() && "The shared lib exists but can't find it!");

    // get canonical path name and check if already loaded
#ifdef WIN32
    llvm::SmallString<_MAX_PATH> FullPath;
    FullPath.set_size(_MAX_PATH);
    char *res = _fullpath((char *)FullPath.data(), FoundDyLib.c_str(), _MAX_PATH);
#else
    llvm::SmallString<PATH_MAX+1> FullPath;
    FullPath.set_size(PATH_MAX+1);
    char *res = realpath(FoundDyLib.c_str(), (char *)FullPath.data());
#endif
    if (res == 0) {
      llvm::errs() << "cling::Interpreter::tryLinker(): error getting real (canonical) path\n";
      return kLoadLibError;
    }
    FullPath.set_size(strlen(res));
    if (m_loadedLibraries.find(FullPath) != m_loadedLibraries.end())
      return kLoadLibExists;

    // TODO: !permanent case
#ifdef WIN32
# error "Windows DLL opening still needs to be implemented!"
    void* dyLibHandle = needs to be implemented!;
    std::string errMsg;
#else
    const void* dyLibHandle = dlopen(FullPath.c_str(), RTLD_LAZY|RTLD_GLOBAL);
    std::string errMsg;
    if (const char* DyLibError = dlerror()) {
      errMsg = DyLibError;
    }
#endif
    if (!dyLibHandle) {
      llvm::errs() << "cling::Interpreter::tryLinker(): " << errMsg << '\n';
      return kLoadLibError;
    }
    std::pair<DyLibs::iterator, bool> insRes
      = m_DyLibs.insert(std::pair<DyLibHandle, std::string>(dyLibHandle,
                                                            FullPath.str()));
    if (!insRes.second)
      return kLoadLibExists;
    m_loadedLibraries.insert(FullPath);
    return kLoadLibSuccess;
  }

  DynamicLibraryManager::LoadLibResult
  DynamicLibraryManager::loadLibrary(const std::string& filename,
                                     bool permanent, bool* tryCode) {
    llvm::SmallString<128> Absolute((llvm::StringRef(filename)));
    llvm::sys::fs::make_absolute(Absolute);
    bool isAbsolute = filename == Absolute.c_str();
    bool exists = false;
    bool isDyLib = false;
    LoadLibResult res = tryLinker(filename, permanent, isAbsolute, exists,
                                  isDyLib);
    if (tryCode) {
      *tryCode = !isDyLib;
      if (isAbsolute)
        *tryCode &= exists;
    }
    if (exists)
      return res;

    if (!isAbsolute && filename.compare(0, 3, "lib")) {
      // try with "lib" prefix:
      res = tryLinker("lib" + filename, permanent, false, exists, isDyLib);
      if (tryCode) {
        *tryCode = !isDyLib;
        if (isAbsolute)
          *tryCode &= exists;
      }
      if (res != kLoadLibError)
        return res;
    }
    return kLoadLibError;
  }

  bool
  DynamicLibraryManager::isDynamicLibraryLoaded(llvm::StringRef fullPath) const {
    // get canonical path name and check if already loaded
#ifdef WIN32
    char buf[_MAX_PATH];
    char *res = _fullpath(buf, fullPath.str().c_str(), _MAX_PATH);
#else
    char buf[PATH_MAX+1];
    char *res = realpath(fullPath.str().c_str(), buf);
#endif
    if (res == 0) {
      llvm::errs() << "cling::Interpreter::isDynamicLibraryLoaded(): error getting real (canonical) path\n";
      return false;
    }
    if (m_loadedLibraries.find(buf) != m_loadedLibraries.end()) return true;
    return false;
  }

  void DynamicLibraryManager::ExposeHiddenSharedLibrarySymbols(void* handle) {
    llvm::sys::DynamicLibrary::addPermanentLibrary(const_cast<void*>(handle));
  }
} // end namespace cling
<|endoftext|>


<commit_before>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// author:  Vassil Vassilev <vasil.georgiev.vasilev@cern.ch>
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------

#include "cling/Interpreter/DynamicLibraryManager.h"
#include "cling/Interpreter/InterpreterCallbacks.h"
#include "cling/Interpreter/InvocationOptions.h"

#include "llvm/Support/DynamicLibrary.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"

#include <algorithm>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>

#ifdef WIN32
#include <Windows.h>
#include <shlobj.h>
#else
#include <limits.h> /* PATH_MAX */
#include <dlfcn.h>
#endif

namespace {
#if defined(LLVM_ON_UNIX)
  static void GetSystemLibraryPaths(llvm::SmallVectorImpl<std::string>& Paths) {
    char* env_var = getenv("LD_LIBRARY_PATH");
#if __APPLE__
    if (!env_var)
      env_var = getenv("DYLD_LIBRARY_PATH");
    if (!env_var)
      env_var = getenv("DYLD_FALLBACK_LIBRARY_PATH");
#endif
    if (env_var != 0) {
      static const char PathSeparator = ':';
      const char* at = env_var;
      const char* delim = strchr(at, PathSeparator);
      while (delim != 0) {
        std::string tmp(at, size_t(delim-at));
        if (llvm::sys::fs::is_directory(tmp.c_str()))
          Paths.push_back(tmp);
        at = delim + 1;
        delim = strchr(at, PathSeparator);
      }

      if (*at != 0)
        if (llvm::sys::fs::is_directory(llvm::StringRef(at)))
          Paths.push_back(at);
    }
#if defined(__APPLE__) || defined(__CYGWIN__)
    Paths.push_back("/usr/local/lib/");
    Paths.push_back("/usr/X11R6/lib/");
    Paths.push_back("/usr/lib/");
    Paths.push_back("/lib/");

    Paths.push_back("/lib/x86_64-linux-gnu/");
    Paths.push_back("/usr/local/lib64/");
    Paths.push_back("/usr/lib64/");
    Paths.push_back("/lib64/");
#else
    static bool initialized = false;
    static std::vector<std::string> SysPaths;
    if (!initialized) {
      // trick to get the system search path
      std::string cmd("LD_DEBUG=libs LD_PRELOAD=DOESNOTEXIST ls 2>&1");
      FILE *pf = popen(cmd.c_str (), "r");
      std::string result = "";
      std::string sys_path = "";
      char buffer[128];
      while (!feof(pf)) {
        if (fgets(buffer, 128, pf) != NULL)
          result += buffer;
      }
      pclose(pf);
      std::size_t from
        = result.find("search path=", result.find("(LD_LIBRARY_PATH)"));
      std::size_t to = result.find("(system search path)");
      if (from != std::string::npos && to != std::string::npos) {
        from += 12;
        sys_path = result.substr(from, to-from);
        sys_path.erase(std::remove_if(sys_path.begin(), sys_path.end(), isspace),
                       sys_path.end());
        sys_path += ':';
      }
      static const char PathSeparator = ':';
      const char* at = sys_path.c_str();
      const char* delim = strchr(at, PathSeparator);
      while (delim != 0) {
        std::string tmp(at, size_t(delim-at));
        if (llvm::sys::fs::is_directory(tmp.c_str()))
          SysPaths.push_back(tmp);
        at = delim + 1;
        delim = strchr(at, PathSeparator);
      }
      initialized = true;
    }

    for (std::vector<std::string>::const_iterator I = SysPaths.begin(),
           E = SysPaths.end(); I != E; ++I)
      Paths.push_back((*I).c_str());
#endif
  }
#elif defined(LLVM_ON_WIN32)
  static void GetSystemLibraryPaths(llvm::SmallVectorImpl<std::string>& Paths) {
    char buff[MAX_PATH];
    // Generic form of C:\Windows\System32
    HRESULT res =  SHGetFolderPathA(NULL,
                                    CSIDL_FLAG_CREATE | CSIDL_SYSTEM,
                                    NULL,
                                    SHGFP_TYPE_CURRENT,
                                    buff);
    if (res != S_OK) {
      assert(0 && "Failed to get system directory");
      return;
    }
    Paths.push_back(buff);

    // Reset buff.
    buff[0] = 0;
    // Generic form of C:\Windows
    res =  SHGetFolderPathA(NULL,
                            CSIDL_FLAG_CREATE | CSIDL_WINDOWS,
                            NULL,
                            SHGFP_TYPE_CURRENT,
                            buff);
    if (res != S_OK) {
      assert(0 && "Failed to get windows directory");
      return;
    }
    Paths.push_back(buff);
  }
#else
# error "Unsupported platform."
#endif

}

namespace cling {
  DynamicLibraryManager::DynamicLibraryManager(const InvocationOptions& Opts)
    : m_Opts(Opts), m_Callbacks(0) {
    GetSystemLibraryPaths(m_SystemSearchPaths);
    m_SystemSearchPaths.push_back(".");
  }

  DynamicLibraryManager::~DynamicLibraryManager() {}

  static bool isSharedLib(llvm::StringRef LibName, bool* exists = 0) {
    using namespace llvm::sys::fs;
    file_magic Magic;
    llvm::error_code Error = identify_magic(LibName, Magic);
    bool onDisk = (Error == llvm::errc::success);
    if (exists)
      *exists = onDisk;

    return onDisk &&
#ifdef __APPLE__
      (Magic == file_magic::macho_fixed_virtual_memory_shared_lib
       || Magic == file_magic::macho_dynamically_linked_shared_lib
       || Magic == file_magic::macho_dynamically_linked_shared_lib_stub)
#elif defined(LLVM_ON_UNIX)
#ifdef __CYGWIN__
      (Magic == file_magic::pecoff_executable)
#else
      (Magic == file_magic::elf_shared_object)
#endif
#elif defined(LLVM_ON_WIN32)
      (Magic == file_magic::pecoff_executable)
#else
# error "Unsupported platform."
#endif
      ;
  }

  std::string
  DynamicLibraryManager::lookupLibInPaths(llvm::StringRef libStem) const {
    llvm::SmallVector<std::string, 128>
      Paths(m_Opts.LibSearchPath.begin(), m_Opts.LibSearchPath.end());
    Paths.append(m_SystemSearchPaths.begin(), m_SystemSearchPaths.end());

    for (llvm::SmallVectorImpl<std::string>::const_iterator
           IPath = Paths.begin(), E = Paths.end();IPath != E; ++IPath) {
      llvm::SmallString<512> ThisPath(*IPath); // FIXME: move alloc outside loop
      llvm::sys::path::append(ThisPath, libStem);
      bool exists;
      if (isSharedLib(ThisPath.str(), &exists))
        return ThisPath.str();
      if (exists)
        return "";
    }
    return "";
  }

  std::string
  DynamicLibraryManager::lookupLibMaybeAddExt(llvm::StringRef libStem) const {
    using namespace llvm::sys;

    std::string foundDyLib = lookupLibInPaths(libStem);

    if (foundDyLib.empty()) {
      // Add DyLib extension:
      llvm::SmallString<512> filenameWithExt(libStem);
#if defined(LLVM_ON_UNIX)
#ifdef __APPLE__
      llvm::SmallString<512>::iterator IStemEnd = filenameWithExt.end() - 1;
#endif
      static const char* DyLibExt = ".so";
#elif defined(LLVM_ON_WIN32)
      static const char* DyLibExt = ".dll";
#else
# error "Unsupported platform."
#endif
      filenameWithExt += DyLibExt;
      foundDyLib = lookupLibInPaths(filenameWithExt);
#ifdef __APPLE__
      if (foundDyLib.empty()) {
        filenameWithExt.erase(IStemEnd + 1, filenameWithExt.end());
        filenameWithExt += ".dylib";
        foundDyLib = lookupLibInPaths(filenameWithExt);
      }
#endif
    }

    if (foundDyLib.empty())
      return "";

    // get canonical path name and check if already loaded
#if defined(LLVM_ON_WIN32)
    llvm::SmallString<_MAX_PATH> FullPath("");
    char *res = _fullpath((char *)FullPath.data(), foundDyLib.c_str(), _MAX_PATH);
#else
    llvm::SmallString<PATH_MAX+1> FullPath("");
    char *res = realpath(foundDyLib.c_str(), (char *)FullPath.data());
#endif
    if (res == 0) {
      llvm::errs() << "cling::DyLibMan::lookupLibMaybeAddExt(): error getting "
        "real (canonical) path of library " << foundDyLib << '\n';
      return foundDyLib;
    }
    FullPath.set_size(strlen(res));
    return FullPath.str();
  }

  std::string DynamicLibraryManager::normalizePath(llvm::StringRef path) {
    // Make the path canonical if the file exists.
    struct stat buffer;
    if (stat(path.data(), &buffer) != 0)
      return "";
#if defined(LLVM_ON_WIN32)
    char buf[_MAX_PATH];
    char *res = _fullpath(buf, path.data(), _MAX_PATH);
#else
    char buf[PATH_MAX+1];
    char *res = realpath(path.data(), buf);
#endif
    if (res == 0) {
      assert(0 && "Cannot normalize!?");
      return "";
    }
    return res;
  }

  std::string
  DynamicLibraryManager::lookupLibrary(llvm::StringRef libStem) const {
    llvm::SmallString<128> Absolute(libStem);
    llvm::sys::fs::make_absolute(Absolute);
    bool isAbsolute = libStem == Absolute;

    // If it is an absolute path, don't try iterate over the paths.
    if (isAbsolute) {
      if (isSharedLib(libStem))
        return normalizePath(libStem);
      else
        return "";
    }

    std::string foundName = lookupLibMaybeAddExt(libStem);
    if (foundName.empty() && !libStem.startswith("lib")) {
      // try with "lib" prefix:
      foundName = lookupLibMaybeAddExt("lib" + libStem.str());
    }

    if (isSharedLib(foundName))
      return normalizePath(foundName);
    return "";
  }

  DynamicLibraryManager::LoadLibResult
  DynamicLibraryManager::loadLibrary(const std::string& libStem,
                                     bool permanent) {
    std::string canonicalLoadedLib = lookupLibrary(libStem);
    if (canonicalLoadedLib.empty())
      return kLoadLibNotFound;

    if (m_LoadedLibraries.find(canonicalLoadedLib) != m_LoadedLibraries.end())
      return kLoadLibAlreadyLoaded;

    std::string errMsg;
    // TODO: !permanent case
#if defined(LLVM_ON_WIN32)
    HMODULE dyLibHandle = LoadLibraryEx(canonicalLoadedLib.c_str(), NULL,
                                        DONT_RESOLVE_DLL_REFERENCES);
    errMsg = "LoadLibraryEx: GetLastError() returned ";
    errMsg += GetLastError();
#else
    const void* dyLibHandle = dlopen(canonicalLoadedLib.c_str(),
                                     RTLD_LAZY|RTLD_GLOBAL);
    if (const char* DyLibError = dlerror()) {
      errMsg = DyLibError;
    }
#endif
    if (!dyLibHandle) {
      llvm::errs() << "cling::DyLibMan::loadLibrary(): " << errMsg << '\n';
      return kLoadLibLoadError;
    }
    else if (InterpreterCallbacks* C = getCallbacks())
      C->LibraryLoaded(dyLibHandle, canonicalLoadedLib);

    std::pair<DyLibs::iterator, bool> insRes
      = m_DyLibs.insert(std::pair<DyLibHandle, std::string>(dyLibHandle,
                                                            canonicalLoadedLib));
    if (!insRes.second)
      return kLoadLibAlreadyLoaded;
    m_LoadedLibraries.insert(canonicalLoadedLib);
    return kLoadLibSuccess;
  }

  void DynamicLibraryManager::unloadLibrary(llvm::StringRef libStem) {
    std::string canonicalLoadedLib = lookupLibrary(libStem);
    if (!isLibraryLoaded(canonicalLoadedLib))
      return;

    DyLibHandle dyLibHandle = 0;
    for (DyLibs::const_iterator I = m_DyLibs.begin(), E = m_DyLibs.end();
         I != E; ++I) {
      if (I->second == canonicalLoadedLib)
        dyLibHandle = I->first;
    }

    std::string errMsg;
    // TODO: !permanent case
#if defined(LLVM_ON_WIN32)
    FreeLibrary((HMODULE)dyLibHandle);
    errMsg = "UnoadLibraryEx: GetLastError() returned ";
    errMsg += GetLastError();
#else
    dlclose(const_cast<void*>(dyLibHandle));
    if (const char* DyLibError = dlerror()) {
      errMsg = DyLibError;
    }
#endif
    if (InterpreterCallbacks* C = getCallbacks())
      C->LibraryUnloaded(dyLibHandle, canonicalLoadedLib);

    m_DyLibs.erase(dyLibHandle);
    m_LoadedLibraries.erase(canonicalLoadedLib);
  }

  bool DynamicLibraryManager::isLibraryLoaded(llvm::StringRef fullPath) const {
    std::string canonPath = normalizePath(fullPath);
    if (m_LoadedLibraries.find(canonPath) != m_LoadedLibraries.end())
      return true;
    return false;
  }

  void DynamicLibraryManager::ExposeHiddenSharedLibrarySymbols(void* handle) {
    llvm::sys::DynamicLibrary::addPermanentLibrary(const_cast<void*>(handle));
  }
} // end namespace cling
<commit_msg>Use actual class name to make it easier to find the source of errors.<commit_after>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// author:  Vassil Vassilev <vasil.georgiev.vasilev@cern.ch>
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------

#include "cling/Interpreter/DynamicLibraryManager.h"
#include "cling/Interpreter/InterpreterCallbacks.h"
#include "cling/Interpreter/InvocationOptions.h"

#include "llvm/Support/DynamicLibrary.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"

#include <algorithm>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>

#ifdef WIN32
#include <Windows.h>
#include <shlobj.h>
#else
#include <limits.h> /* PATH_MAX */
#include <dlfcn.h>
#endif

namespace {
#if defined(LLVM_ON_UNIX)
  static void GetSystemLibraryPaths(llvm::SmallVectorImpl<std::string>& Paths) {
    char* env_var = getenv("LD_LIBRARY_PATH");
#if __APPLE__
    if (!env_var)
      env_var = getenv("DYLD_LIBRARY_PATH");
    if (!env_var)
      env_var = getenv("DYLD_FALLBACK_LIBRARY_PATH");
#endif
    if (env_var != 0) {
      static const char PathSeparator = ':';
      const char* at = env_var;
      const char* delim = strchr(at, PathSeparator);
      while (delim != 0) {
        std::string tmp(at, size_t(delim-at));
        if (llvm::sys::fs::is_directory(tmp.c_str()))
          Paths.push_back(tmp);
        at = delim + 1;
        delim = strchr(at, PathSeparator);
      }

      if (*at != 0)
        if (llvm::sys::fs::is_directory(llvm::StringRef(at)))
          Paths.push_back(at);
    }
#if defined(__APPLE__) || defined(__CYGWIN__)
    Paths.push_back("/usr/local/lib/");
    Paths.push_back("/usr/X11R6/lib/");
    Paths.push_back("/usr/lib/");
    Paths.push_back("/lib/");

    Paths.push_back("/lib/x86_64-linux-gnu/");
    Paths.push_back("/usr/local/lib64/");
    Paths.push_back("/usr/lib64/");
    Paths.push_back("/lib64/");
#else
    static bool initialized = false;
    static std::vector<std::string> SysPaths;
    if (!initialized) {
      // trick to get the system search path
      std::string cmd("LD_DEBUG=libs LD_PRELOAD=DOESNOTEXIST ls 2>&1");
      FILE *pf = popen(cmd.c_str (), "r");
      std::string result = "";
      std::string sys_path = "";
      char buffer[128];
      while (!feof(pf)) {
        if (fgets(buffer, 128, pf) != NULL)
          result += buffer;
      }
      pclose(pf);
      std::size_t from
        = result.find("search path=", result.find("(LD_LIBRARY_PATH)"));
      std::size_t to = result.find("(system search path)");
      if (from != std::string::npos && to != std::string::npos) {
        from += 12;
        sys_path = result.substr(from, to-from);
        sys_path.erase(std::remove_if(sys_path.begin(), sys_path.end(), isspace),
                       sys_path.end());
        sys_path += ':';
      }
      static const char PathSeparator = ':';
      const char* at = sys_path.c_str();
      const char* delim = strchr(at, PathSeparator);
      while (delim != 0) {
        std::string tmp(at, size_t(delim-at));
        if (llvm::sys::fs::is_directory(tmp.c_str()))
          SysPaths.push_back(tmp);
        at = delim + 1;
        delim = strchr(at, PathSeparator);
      }
      initialized = true;
    }

    for (std::vector<std::string>::const_iterator I = SysPaths.begin(),
           E = SysPaths.end(); I != E; ++I)
      Paths.push_back((*I).c_str());
#endif
  }
#elif defined(LLVM_ON_WIN32)
  static void GetSystemLibraryPaths(llvm::SmallVectorImpl<std::string>& Paths) {
    char buff[MAX_PATH];
    // Generic form of C:\Windows\System32
    HRESULT res =  SHGetFolderPathA(NULL,
                                    CSIDL_FLAG_CREATE | CSIDL_SYSTEM,
                                    NULL,
                                    SHGFP_TYPE_CURRENT,
                                    buff);
    if (res != S_OK) {
      assert(0 && "Failed to get system directory");
      return;
    }
    Paths.push_back(buff);

    // Reset buff.
    buff[0] = 0;
    // Generic form of C:\Windows
    res =  SHGetFolderPathA(NULL,
                            CSIDL_FLAG_CREATE | CSIDL_WINDOWS,
                            NULL,
                            SHGFP_TYPE_CURRENT,
                            buff);
    if (res != S_OK) {
      assert(0 && "Failed to get windows directory");
      return;
    }
    Paths.push_back(buff);
  }
#else
# error "Unsupported platform."
#endif

}

namespace cling {
  DynamicLibraryManager::DynamicLibraryManager(const InvocationOptions& Opts)
    : m_Opts(Opts), m_Callbacks(0) {
    GetSystemLibraryPaths(m_SystemSearchPaths);
    m_SystemSearchPaths.push_back(".");
  }

  DynamicLibraryManager::~DynamicLibraryManager() {}

  static bool isSharedLib(llvm::StringRef LibName, bool* exists = 0) {
    using namespace llvm::sys::fs;
    file_magic Magic;
    llvm::error_code Error = identify_magic(LibName, Magic);
    bool onDisk = (Error == llvm::errc::success);
    if (exists)
      *exists = onDisk;

    return onDisk &&
#ifdef __APPLE__
      (Magic == file_magic::macho_fixed_virtual_memory_shared_lib
       || Magic == file_magic::macho_dynamically_linked_shared_lib
       || Magic == file_magic::macho_dynamically_linked_shared_lib_stub)
#elif defined(LLVM_ON_UNIX)
#ifdef __CYGWIN__
      (Magic == file_magic::pecoff_executable)
#else
      (Magic == file_magic::elf_shared_object)
#endif
#elif defined(LLVM_ON_WIN32)
      (Magic == file_magic::pecoff_executable)
#else
# error "Unsupported platform."
#endif
      ;
  }

  std::string
  DynamicLibraryManager::lookupLibInPaths(llvm::StringRef libStem) const {
    llvm::SmallVector<std::string, 128>
      Paths(m_Opts.LibSearchPath.begin(), m_Opts.LibSearchPath.end());
    Paths.append(m_SystemSearchPaths.begin(), m_SystemSearchPaths.end());

    for (llvm::SmallVectorImpl<std::string>::const_iterator
           IPath = Paths.begin(), E = Paths.end();IPath != E; ++IPath) {
      llvm::SmallString<512> ThisPath(*IPath); // FIXME: move alloc outside loop
      llvm::sys::path::append(ThisPath, libStem);
      bool exists;
      if (isSharedLib(ThisPath.str(), &exists))
        return ThisPath.str();
      if (exists)
        return "";
    }
    return "";
  }

  std::string
  DynamicLibraryManager::lookupLibMaybeAddExt(llvm::StringRef libStem) const {
    using namespace llvm::sys;

    std::string foundDyLib = lookupLibInPaths(libStem);

    if (foundDyLib.empty()) {
      // Add DyLib extension:
      llvm::SmallString<512> filenameWithExt(libStem);
#if defined(LLVM_ON_UNIX)
#ifdef __APPLE__
      llvm::SmallString<512>::iterator IStemEnd = filenameWithExt.end() - 1;
#endif
      static const char* DyLibExt = ".so";
#elif defined(LLVM_ON_WIN32)
      static const char* DyLibExt = ".dll";
#else
# error "Unsupported platform."
#endif
      filenameWithExt += DyLibExt;
      foundDyLib = lookupLibInPaths(filenameWithExt);
#ifdef __APPLE__
      if (foundDyLib.empty()) {
        filenameWithExt.erase(IStemEnd + 1, filenameWithExt.end());
        filenameWithExt += ".dylib";
        foundDyLib = lookupLibInPaths(filenameWithExt);
      }
#endif
    }

    if (foundDyLib.empty())
      return "";

    // get canonical path name and check if already loaded
#if defined(LLVM_ON_WIN32)
    llvm::SmallString<_MAX_PATH> FullPath("");
    char *res = _fullpath((char *)FullPath.data(), foundDyLib.c_str(), _MAX_PATH);
#else
    llvm::SmallString<PATH_MAX+1> FullPath("");
    char *res = realpath(foundDyLib.c_str(), (char *)FullPath.data());
#endif
    if (res == 0) {
      llvm::errs() << "cling::DynamicLibraryManager::lookupLibMaybeAddExt(): "
        "error getting real (canonical) path of library " << foundDyLib << '\n';
      return foundDyLib;
    }
    FullPath.set_size(strlen(res));
    return FullPath.str();
  }

  std::string DynamicLibraryManager::normalizePath(llvm::StringRef path) {
    // Make the path canonical if the file exists.
    struct stat buffer;
    if (stat(path.data(), &buffer) != 0)
      return "";
#if defined(LLVM_ON_WIN32)
    char buf[_MAX_PATH];
    char *res = _fullpath(buf, path.data(), _MAX_PATH);
#else
    char buf[PATH_MAX+1];
    char *res = realpath(path.data(), buf);
#endif
    if (res == 0) {
      assert(0 && "Cannot normalize!?");
      return "";
    }
    return res;
  }

  std::string
  DynamicLibraryManager::lookupLibrary(llvm::StringRef libStem) const {
    llvm::SmallString<128> Absolute(libStem);
    llvm::sys::fs::make_absolute(Absolute);
    bool isAbsolute = libStem == Absolute;

    // If it is an absolute path, don't try iterate over the paths.
    if (isAbsolute) {
      if (isSharedLib(libStem))
        return normalizePath(libStem);
      else
        return "";
    }

    std::string foundName = lookupLibMaybeAddExt(libStem);
    if (foundName.empty() && !libStem.startswith("lib")) {
      // try with "lib" prefix:
      foundName = lookupLibMaybeAddExt("lib" + libStem.str());
    }

    if (isSharedLib(foundName))
      return normalizePath(foundName);
    return "";
  }

  DynamicLibraryManager::LoadLibResult
  DynamicLibraryManager::loadLibrary(const std::string& libStem,
                                     bool permanent) {
    std::string canonicalLoadedLib = lookupLibrary(libStem);
    if (canonicalLoadedLib.empty())
      return kLoadLibNotFound;

    if (m_LoadedLibraries.find(canonicalLoadedLib) != m_LoadedLibraries.end())
      return kLoadLibAlreadyLoaded;

    std::string errMsg;
    // TODO: !permanent case
#if defined(LLVM_ON_WIN32)
    HMODULE dyLibHandle = LoadLibraryEx(canonicalLoadedLib.c_str(), NULL,
                                        DONT_RESOLVE_DLL_REFERENCES);
    errMsg = "LoadLibraryEx: GetLastError() returned ";
    errMsg += GetLastError();
#else
    const void* dyLibHandle = dlopen(canonicalLoadedLib.c_str(),
                                     RTLD_LAZY|RTLD_GLOBAL);
    if (const char* DyLibError = dlerror()) {
      errMsg = DyLibError;
    }
#endif
    if (!dyLibHandle) {
      llvm::errs() << "cling::DynamicLibraryManager::loadLibrary(): " << errMsg
                   << '\n';
      return kLoadLibLoadError;
    }
    else if (InterpreterCallbacks* C = getCallbacks())
      C->LibraryLoaded(dyLibHandle, canonicalLoadedLib);

    std::pair<DyLibs::iterator, bool> insRes
      = m_DyLibs.insert(std::pair<DyLibHandle, std::string>(dyLibHandle,
                                                            canonicalLoadedLib));
    if (!insRes.second)
      return kLoadLibAlreadyLoaded;
    m_LoadedLibraries.insert(canonicalLoadedLib);
    return kLoadLibSuccess;
  }

  void DynamicLibraryManager::unloadLibrary(llvm::StringRef libStem) {
    std::string canonicalLoadedLib = lookupLibrary(libStem);
    if (!isLibraryLoaded(canonicalLoadedLib))
      return;

    DyLibHandle dyLibHandle = 0;
    for (DyLibs::const_iterator I = m_DyLibs.begin(), E = m_DyLibs.end();
         I != E; ++I) {
      if (I->second == canonicalLoadedLib)
        dyLibHandle = I->first;
    }

    std::string errMsg;
    // TODO: !permanent case
#if defined(LLVM_ON_WIN32)
    FreeLibrary((HMODULE)dyLibHandle);
    errMsg = "UnoadLibraryEx: GetLastError() returned ";
    errMsg += GetLastError();
#else
    dlclose(const_cast<void*>(dyLibHandle));
    if (const char* DyLibError = dlerror()) {
      errMsg = DyLibError;
    }
#endif
    if (InterpreterCallbacks* C = getCallbacks())
      C->LibraryUnloaded(dyLibHandle, canonicalLoadedLib);

    m_DyLibs.erase(dyLibHandle);
    m_LoadedLibraries.erase(canonicalLoadedLib);
  }

  bool DynamicLibraryManager::isLibraryLoaded(llvm::StringRef fullPath) const {
    std::string canonPath = normalizePath(fullPath);
    if (m_LoadedLibraries.find(canonPath) != m_LoadedLibraries.end())
      return true;
    return false;
  }

  void DynamicLibraryManager::ExposeHiddenSharedLibrarySymbols(void* handle) {
    llvm::sys::DynamicLibrary::addPermanentLibrary(const_cast<void*>(handle));
  }
} // end namespace cling
<|endoftext|>


<commit_before>//////////////////////////////////////////////////////////////////////////
#pragma once
#include "Graphics/BlendStateDesc.hpp"
#include "Graphics/RasterizerStateDesc.hpp"
#include "Graphics/DepthStencilStateDesc.hpp"
#include "Graphics/InputLayoutDesc.hpp"
#include "Graphics/SampleDesc.hpp"
#include "Graphics/iResourceBinding.hpp"
#include "Graphics/IndexStripCut.hpp"
#include "Graphics/TopologyType.hpp"
#include "IntrusivePtr.hpp"

namespace CPF
{
	namespace Graphics
	{
		struct iDevice;
		struct iShader;
		struct iPipeline;
		struct iResourceBinding;

		struct PipelineStateDesc
		{
			static constexpr int kMaxRenderTargets = 8;

			PipelineStateDesc();
			PipelineStateDesc(const PipelineStateDesc& rhs);

			IntrusivePtr<iResourceBinding> mpResourceBinding;
			// TODO: Shaders should not be instances, they should be opaque handles or a struct.
			IntrusivePtr<iShader> mpVertex;
			IntrusivePtr<iShader> mpPixel;
			IntrusivePtr<iShader> mpDomain;
			IntrusivePtr<iShader> mpHull;
			IntrusivePtr<iShader> mpGeometry;
			BlendStateDesc mBlendState;
			uint32_t mSampleMask;
			RasterizerStateDesc mRasterizerState;
			DepthStencilStateDesc mDepthStencil;
			cInputLayoutDesc mInputLayout;
			IndexStripCut mIndexStripCut;
			TopologyType mTopology;
			int32_t mRenderTargetCount;
			Format mRenderTargetFormats[kMaxRenderTargets];
			Format mDepthStencilFormat;
			SampleDesc mSampleState;

			// Items not in the pipeline at this time.
			// StreamOutput
			// node mask
			// cache pipeline state
			// flags
		};

		inline PipelineStateDesc::PipelineStateDesc()
			: mBlendState(Defaults<BlendStateDesc>())
			, mSampleMask(uint32_t(-1))
			, mRasterizerState(Defaults<RasterizerStateDesc>())
			, mIndexStripCut(IndexStripCut::eNone)
			, mTopology(TopologyType::eTriangle)
			, mRenderTargetCount(0)
			, mDepthStencilFormat(Format::eNone)
			, mSampleState{ 1, 0 }
		{
			for (int i = 0; i < kMaxRenderTargets; ++i)
				mRenderTargetFormats[i] = Format::eNone;
		}

		inline PipelineStateDesc::PipelineStateDesc(const PipelineStateDesc& rhs)
			: mpResourceBinding(rhs.mpResourceBinding)
			, mpVertex(rhs.mpVertex)
			, mpPixel(rhs.mpPixel)
			, mpDomain(rhs.mpDomain)
			, mpHull(rhs.mpHull)
			, mpGeometry(rhs.mpGeometry)
			, mBlendState(rhs.mBlendState)
			, mSampleMask(rhs.mSampleMask)
			, mRasterizerState(rhs.mRasterizerState)
			, mDepthStencil(rhs.mDepthStencil)
			, mInputLayout(rhs.mInputLayout)
			, mIndexStripCut(rhs.mIndexStripCut)
			, mTopology(rhs.mTopology)
			, mRenderTargetCount(rhs.mRenderTargetCount)
			, mDepthStencilFormat(rhs.mDepthStencilFormat)
			, mSampleState(rhs.mSampleState)
		{
			for (int i = 0; i < kMaxRenderTargets; ++i)
				mRenderTargetFormats[i] = rhs.mRenderTargetFormats[i];
		}
	}
}
<commit_msg>Quick fixit.<commit_after>//////////////////////////////////////////////////////////////////////////
#pragma once
#include "Graphics/BlendStateDesc.hpp"
#include "Graphics/RasterizerStateDesc.hpp"
#include "Graphics/DepthStencilStateDesc.hpp"
#include "Graphics/InputLayoutDesc.hpp"
#include "Graphics/SampleDesc.hpp"
#include "Graphics/iResourceBinding.hpp"
#include "Graphics/IndexStripCut.hpp"
#include "Graphics/TopologyType.hpp"
#include "IntrusivePtr.hpp"

namespace CPF
{
	namespace Graphics
	{
		struct iDevice;
		struct iShader;
		struct iPipeline;
		struct iResourceBinding;

		struct PipelineStateDesc
		{
			static constexpr int kMaxRenderTargets = 8;

			PipelineStateDesc();
			PipelineStateDesc(const PipelineStateDesc& rhs);

			IntrusivePtr<iResourceBinding> mpResourceBinding;
			// TODO: Shaders should not be instances, they should be opaque handles or a struct.
			IntrusivePtr<iShader> mpVertex;
			IntrusivePtr<iShader> mpPixel;
			IntrusivePtr<iShader> mpDomain;
			IntrusivePtr<iShader> mpHull;
			IntrusivePtr<iShader> mpGeometry;
			BlendStateDesc mBlendState;
			uint32_t mSampleMask;
			RasterizerStateDesc mRasterizerState;
			DepthStencilStateDesc mDepthStencil;
			InputLayoutDesc mInputLayout;
			IndexStripCut mIndexStripCut;
			TopologyType mTopology;
			int32_t mRenderTargetCount;
			Format mRenderTargetFormats[kMaxRenderTargets];
			Format mDepthStencilFormat;
			SampleDesc mSampleState;

			// Items not in the pipeline at this time.
			// StreamOutput
			// node mask
			// cache pipeline state
			// flags
		};

		inline PipelineStateDesc::PipelineStateDesc()
			: mBlendState(Defaults<BlendStateDesc>())
			, mSampleMask(uint32_t(-1))
			, mRasterizerState(Defaults<RasterizerStateDesc>())
			, mIndexStripCut(IndexStripCut::eNone)
			, mTopology(TopologyType::eTriangle)
			, mRenderTargetCount(0)
			, mDepthStencilFormat(Format::eNone)
			, mSampleState{ 1, 0 }
		{
			for (int i = 0; i < kMaxRenderTargets; ++i)
				mRenderTargetFormats[i] = Format::eNone;
		}

		inline PipelineStateDesc::PipelineStateDesc(const PipelineStateDesc& rhs)
			: mpResourceBinding(rhs.mpResourceBinding)
			, mpVertex(rhs.mpVertex)
			, mpPixel(rhs.mpPixel)
			, mpDomain(rhs.mpDomain)
			, mpHull(rhs.mpHull)
			, mpGeometry(rhs.mpGeometry)
			, mBlendState(rhs.mBlendState)
			, mSampleMask(rhs.mSampleMask)
			, mRasterizerState(rhs.mRasterizerState)
			, mDepthStencil(rhs.mDepthStencil)
			, mInputLayout(rhs.mInputLayout)
			, mIndexStripCut(rhs.mIndexStripCut)
			, mTopology(rhs.mTopology)
			, mRenderTargetCount(rhs.mRenderTargetCount)
			, mDepthStencilFormat(rhs.mDepthStencilFormat)
			, mSampleState(rhs.mSampleState)
		{
			for (int i = 0; i < kMaxRenderTargets; ++i)
				mRenderTargetFormats[i] = rhs.mRenderTargetFormats[i];
		}
	}
}
<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include <svdata.hxx>
#include <tools/time.hxx>
#include <vcl/scheduler.hxx>
#include <vcl/timer.hxx>
#include <algorithm>
#include <saltimer.hxx>

void Scheduler::ImplInvoke(sal_uInt64 nTime)
{
    mnUpdateTime = nTime;

    if (mpSchedulerData->mbDelete || mbInScheduler )
        return;

    // prepare Scheduler Object for deletion after handling
    SetDeletionFlags();

    // invoke it
    mbInScheduler = true;
    Invoke();
    mbInScheduler = false;
}

Scheduler* Scheduler::ImplGetHighestPrioTask( bool bTimer )
{
    ImplSVData*     pSVData     = ImplGetSVData();
    Scheduler *     pMostUrgent = NULL;

    std::for_each(pSVData->maSchedulers->begin(), pSVData->maSchedulers->end(),
        [&pSVData, bTimer, &pMostUrgent] (ImplSchedulerData *rScheduler)
    {
        if ( rScheduler->mpScheduler &&
            rScheduler->mpScheduler->ImplIsScheduleReady(pSVData->mnUpdateStack) &&
            rScheduler->mpScheduler->ReadyForSchedule( bTimer ) &&
            rScheduler->mpScheduler->IsActive() )
        {
            if (!pMostUrgent)
                pMostUrgent = rScheduler->mpScheduler;
            else
            {
                // Find the highest priority.
                // If the priority of the current task is higher (numerical value is lower) than
                // the priority of the most urgent, the current task gets the new most urgent.
                if ( rScheduler->mpScheduler->GetPriority() < pMostUrgent->GetPriority() )
                    pMostUrgent = rScheduler->mpScheduler;
            }
        }
    });

    return pMostUrgent;
}

void Scheduler::SetDeletionFlags()
{
    Stop();
}

void Scheduler::ImplDeInitScheduler(bool bAll /*=true*/)
{
    ImplSVData* pSVData = ImplGetSVData();

    if (pSVData->mpSalTimer)
    {
        pSVData->mpSalTimer->Stop();
    }

    pSVData->maSchedulers->remove_if( [] (ImplSchedulerData *rSchedulerData)
    {
        if(rSchedulerData->mpScheduler != NULL)
        {
            rSchedulerData->mpScheduler->ImplDispose();
        }
        else
            delete rSchedulerData;

        return true;
    });

    if(bAll)
    {
        delete pSVData->maSchedulers;
        pSVData->maSchedulers = NULL;
    }

    delete pSVData->mpSalTimer;
    pSVData->mpSalTimer  = NULL;
}

void Scheduler::CallbackTaskScheduling(bool ignore)
{
    // this function is for the saltimer callback
    (void)ignore;
    Scheduler::ProcessTaskScheduling( true );
}

void Scheduler::ProcessTaskScheduling( bool bTimer )
{
    // process all pending Tasks
    // if bTimer True, only handle timer
    Scheduler*  pScheduler = NULL;
    ImplSVData* pSVData = ImplGetSVData();
    sal_uInt64  nTime = tools::Time::GetSystemTicks();
    sal_uInt64  nMinPeriod = MAX_TIMER_PERIOD;

    pSVData->mnUpdateStack++;

    // tdf#91727 - NB. bTimer is ultimately not used
    if ((pScheduler = Scheduler::ImplGetHighestPrioTask(bTimer)) != NULL)
        pScheduler->ImplInvoke(nTime);

    pSVData->maSchedulers->remove_if( [&nMinPeriod, nTime, pSVData] (ImplSchedulerData *rSchedulerData)
    {
        if (rSchedulerData->mpScheduler)
            return rSchedulerData->mpScheduler->ImplHandleTaskScheduling(nMinPeriod, nTime);
        else
        {
            delete rSchedulerData;
            return true;
        }
    });

    // delete clock if no more timers available
    if ( pSVData->maSchedulers->empty() )
    {
        if ( pSVData->mpSalTimer )
            pSVData->mpSalTimer->Stop();
        pSVData->mnTimerPeriod = MAX_TIMER_PERIOD;
    }
    else
    {
        Timer::ImplStartTimer( pSVData, nMinPeriod );
    }

    pSVData->mnUpdateStack--;
}

void Scheduler::SetPriority( SchedulerPriority ePriority )
{
    mePriority = ePriority;
}

void Scheduler::Start()
{
    ImplSVData* pSVData = ImplGetSVData();
    // Mark timer active
    mbActive = true;

    if ( !mpSchedulerData )
    {
        mpSchedulerData = new ImplSchedulerData;
        mpSchedulerData->mpScheduler = this;
        // insert Scheduler
        mbInScheduler   = false;
        pSVData->maSchedulers->push_back(mpSchedulerData);
    }

    mpSchedulerData->mbDelete = false;
    mnUpdateTime              = tools::Time::GetSystemTicks();
    mnUpdateStack             = pSVData->mnUpdateStack;
}

void Scheduler::Stop()
{
    mbActive = false;

    if ( mpSchedulerData )
        mpSchedulerData->mbDelete = true;
}

Scheduler& Scheduler::operator=( const Scheduler& rScheduler )
{
    if ( IsActive() )
        Stop();

    mbActive   = false;
    mePriority = rScheduler.mePriority;

    if ( rScheduler.IsActive() )
        Start();

    return *this;
}

Scheduler::Scheduler(const sal_Char *pDebugName):
    mpSchedulerData(NULL),
    mpDebugName(pDebugName),
    mePriority(SchedulerPriority::HIGH),
    mbActive(false),
    mnUpdateTime(0)
{
}

Scheduler::Scheduler( const Scheduler& rScheduler ):
    mpSchedulerData(NULL),
    mpDebugName(rScheduler.mpDebugName),
    mePriority(SchedulerPriority::HIGH),
    mbActive(false),
    mnUpdateTime(0)
{
    if ( rScheduler.IsActive() )
        Start();
}

Scheduler::~Scheduler()
{
    if ( mpSchedulerData )
    {
        mpSchedulerData->mbDelete = true;
        mpSchedulerData->mpScheduler = NULL;
    }
}

bool Scheduler::ImplIsScheduleReady(sal_uInt32 nUpdateStack)
{
    return !mpSchedulerData->mbDelete && (mnUpdateStack <= nUpdateStack);
}

void Scheduler::ImplDispose()
{
    mpSchedulerData->mpScheduler = NULL;
    delete mpSchedulerData;
    mpSchedulerData = NULL;
}

void Scheduler::ImplInitScheduler()
{
    ImplSVData* pSVData = ImplGetSVData();

    if(pSVData->maSchedulers == NULL)
        pSVData->maSchedulers = new ImplScheduler_t;
}

bool  Scheduler::ImplHandleTaskScheduling(sal_uInt64 &nMinPeriod, sal_uInt64 nTime)
{
    // process all pending Tasks
    if( !mbInScheduler )
    {
        // Should Task be released from scheduling?
        if ( !mpSchedulerData->mbDelete )
        {
            mnUpdateStack = 0;
            nMinPeriod    = UpdateMinPeriod( nMinPeriod, nTime );
        }
        else
        {
            ImplDispose();
            return true;
        }
    }

    return false;
}
<commit_msg>vcl: loplugin:pointertobool<commit_after>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include <svdata.hxx>
#include <tools/time.hxx>
#include <vcl/scheduler.hxx>
#include <vcl/timer.hxx>
#include <algorithm>
#include <saltimer.hxx>

void Scheduler::ImplInvoke(sal_uInt64 nTime)
{
    mnUpdateTime = nTime;

    if (mpSchedulerData->mbDelete || mbInScheduler )
        return;

    // prepare Scheduler Object for deletion after handling
    SetDeletionFlags();

    // invoke it
    mbInScheduler = true;
    Invoke();
    mbInScheduler = false;
}

Scheduler* Scheduler::ImplGetHighestPrioTask( bool bTimer )
{
    ImplSVData*     pSVData     = ImplGetSVData();
    Scheduler *     pMostUrgent = NULL;

    std::for_each(pSVData->maSchedulers->begin(), pSVData->maSchedulers->end(),
        [&pSVData, bTimer, &pMostUrgent] (ImplSchedulerData *rScheduler)
    {
        if ( rScheduler->mpScheduler &&
            rScheduler->mpScheduler->ImplIsScheduleReady(pSVData->mnUpdateStack) &&
            rScheduler->mpScheduler->ReadyForSchedule( bTimer ) &&
            rScheduler->mpScheduler->IsActive() )
        {
            if (!pMostUrgent)
                pMostUrgent = rScheduler->mpScheduler;
            else
            {
                // Find the highest priority.
                // If the priority of the current task is higher (numerical value is lower) than
                // the priority of the most urgent, the current task gets the new most urgent.
                if ( rScheduler->mpScheduler->GetPriority() < pMostUrgent->GetPriority() )
                    pMostUrgent = rScheduler->mpScheduler;
            }
        }
    });

    return pMostUrgent;
}

void Scheduler::SetDeletionFlags()
{
    Stop();
}

void Scheduler::ImplDeInitScheduler(bool bAll /*=true*/)
{
    ImplSVData* pSVData = ImplGetSVData();

    if (pSVData->mpSalTimer)
    {
        pSVData->mpSalTimer->Stop();
    }

    pSVData->maSchedulers->remove_if( [] (ImplSchedulerData *rSchedulerData)
    {
        if(rSchedulerData->mpScheduler != NULL)
        {
            rSchedulerData->mpScheduler->ImplDispose();
        }
        else
            delete rSchedulerData;

        return true;
    });

    if(bAll)
    {
        delete pSVData->maSchedulers;
        pSVData->maSchedulers = NULL;
    }

    delete pSVData->mpSalTimer;
    pSVData->mpSalTimer  = NULL;
}

void Scheduler::CallbackTaskScheduling(bool ignore)
{
    // this function is for the saltimer callback
    (void)ignore;
    Scheduler::ProcessTaskScheduling( true );
}

void Scheduler::ProcessTaskScheduling( bool bTimer )
{
    // process all pending Tasks
    // if bTimer True, only handle timer
    Scheduler*  pScheduler = NULL;
    ImplSVData* pSVData = ImplGetSVData();
    sal_uInt64  nTime = tools::Time::GetSystemTicks();
    sal_uInt64  nMinPeriod = MAX_TIMER_PERIOD;

    pSVData->mnUpdateStack++;

    // tdf#91727 - NB. bTimer is ultimately not used
    if ((pScheduler = Scheduler::ImplGetHighestPrioTask(bTimer)) != NULL)
        pScheduler->ImplInvoke(nTime);

    pSVData->maSchedulers->remove_if( [&nMinPeriod, nTime, pSVData] (ImplSchedulerData *rSchedulerData)
    {
        if (rSchedulerData->mpScheduler != 0)
            return rSchedulerData->mpScheduler->ImplHandleTaskScheduling(nMinPeriod, nTime);
        else
        {
            delete rSchedulerData;
            return true;
        }
    });

    // delete clock if no more timers available
    if ( pSVData->maSchedulers->empty() )
    {
        if ( pSVData->mpSalTimer )
            pSVData->mpSalTimer->Stop();
        pSVData->mnTimerPeriod = MAX_TIMER_PERIOD;
    }
    else
    {
        Timer::ImplStartTimer( pSVData, nMinPeriod );
    }

    pSVData->mnUpdateStack--;
}

void Scheduler::SetPriority( SchedulerPriority ePriority )
{
    mePriority = ePriority;
}

void Scheduler::Start()
{
    ImplSVData* pSVData = ImplGetSVData();
    // Mark timer active
    mbActive = true;

    if ( !mpSchedulerData )
    {
        mpSchedulerData = new ImplSchedulerData;
        mpSchedulerData->mpScheduler = this;
        // insert Scheduler
        mbInScheduler   = false;
        pSVData->maSchedulers->push_back(mpSchedulerData);
    }

    mpSchedulerData->mbDelete = false;
    mnUpdateTime              = tools::Time::GetSystemTicks();
    mnUpdateStack             = pSVData->mnUpdateStack;
}

void Scheduler::Stop()
{
    mbActive = false;

    if ( mpSchedulerData )
        mpSchedulerData->mbDelete = true;
}

Scheduler& Scheduler::operator=( const Scheduler& rScheduler )
{
    if ( IsActive() )
        Stop();

    mbActive   = false;
    mePriority = rScheduler.mePriority;

    if ( rScheduler.IsActive() )
        Start();

    return *this;
}

Scheduler::Scheduler(const sal_Char *pDebugName):
    mpSchedulerData(NULL),
    mpDebugName(pDebugName),
    mePriority(SchedulerPriority::HIGH),
    mbActive(false),
    mnUpdateTime(0)
{
}

Scheduler::Scheduler( const Scheduler& rScheduler ):
    mpSchedulerData(NULL),
    mpDebugName(rScheduler.mpDebugName),
    mePriority(SchedulerPriority::HIGH),
    mbActive(false),
    mnUpdateTime(0)
{
    if ( rScheduler.IsActive() )
        Start();
}

Scheduler::~Scheduler()
{
    if ( mpSchedulerData )
    {
        mpSchedulerData->mbDelete = true;
        mpSchedulerData->mpScheduler = NULL;
    }
}

bool Scheduler::ImplIsScheduleReady(sal_uInt32 nUpdateStack)
{
    return !mpSchedulerData->mbDelete && (mnUpdateStack <= nUpdateStack);
}

void Scheduler::ImplDispose()
{
    mpSchedulerData->mpScheduler = NULL;
    delete mpSchedulerData;
    mpSchedulerData = NULL;
}

void Scheduler::ImplInitScheduler()
{
    ImplSVData* pSVData = ImplGetSVData();

    if(pSVData->maSchedulers == NULL)
        pSVData->maSchedulers = new ImplScheduler_t;
}

bool  Scheduler::ImplHandleTaskScheduling(sal_uInt64 &nMinPeriod, sal_uInt64 nTime)
{
    // process all pending Tasks
    if( !mbInScheduler )
    {
        // Should Task be released from scheduling?
        if ( !mpSchedulerData->mbDelete )
        {
            mnUpdateStack = 0;
            nMinPeriod    = UpdateMinPeriod( nMinPeriod, nTime );
        }
        else
        {
            ImplDispose();
            return true;
        }
    }

    return false;
}
<|endoftext|>