<commit_before>/*
* Copyright (c) 2012 Fredrik Mellbin
*
* This file is part of VapourSynth.
*
* VapourSynth is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* VapourSynth is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with VapourSynth; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include "vscore.h"
#ifdef VS_TARGET_CPU_X86
#include "x86utils.h"
#endif

VSThread::VSThread(VSThreadPool *owner) : owner(owner), stop(false) {

}

void VSThread::stopThread() {
    stop = true;
}

void VSThread::run() {
#ifdef VS_TARGET_OS_WINDOWS
    if (!vs_isMMXStateOk())
        qFatal("Bad MMX state detected after creating new thread");
    if (!vs_isFPUStateOk())
        qFatal("Bad FPU state detected after creating new thread");
    if (!vs_isSSEStateOk())
        qFatal("Bad SSE state detected after creating new thread");
#endif

    owner->lock.lock();
	owner->activeThreads.ref();

    while (true) {
        bool ranTask = false;

        for (int i = 0; i < owner->tasks.count(); i++) {
            PFrameContext rCtx = owner->tasks[i];

            if (rCtx->frameDone && rCtx->returnedFrame) {
                owner->tasks.removeAt(i--);
                owner->returnFrame(rCtx, rCtx->returnedFrame);
                ranTask = true;
                break;
            } else if (rCtx->frameDone && rCtx->hasError()) {
                owner->tasks.removeAt(i--);
                owner->returnFrame(rCtx, rCtx->getErrorMessage());
                ranTask = true;
                break;
            } else {

                PFrameContext pCtx = rCtx;

                if (rCtx->returnedFrame || rCtx->hasError())
                    rCtx = rCtx->upstreamContext;

                Q_ASSERT(rCtx);
                Q_ASSERT(pCtx);

                // if an error has already been flagged upstream simply drop this task so a filter won't get multiple arError calls for the same frame
                if (rCtx->hasError()) {
                    owner->tasks.removeAt(i--);
                    continue;
                }

                bool isSingleInstance = (rCtx->clip->filterMode == fmParallelRequests && pCtx->returnedFrame && rCtx->numFrameRequests == 1 && pCtx != rCtx) || rCtx->clip->filterMode == fmSerial;

                // this check is common for both filter modes, it makes sure that multiple calls won't be made in parallel to a single filter to produce the same frame
                // special casing so serial unordered doesn't need yet another list
                if (owner->runningTasks.contains(FrameKey(rCtx->clip, rCtx->clip->filterMode == fmUnordered ? -1 : rCtx->n)))
                    continue;

                if (isSingleInstance) {
                    // this is the complicated case, a new frame may not be started until all calls are completed for the current one
                    if (owner->framesInProgress.contains(rCtx->clip) && owner->framesInProgress[rCtx->clip] != rCtx->n)
                        continue;
                }

                // mark task as active
                owner->tasks.removeAt(i--);
                owner->runningTasks.insert(FrameKey(rCtx->clip, rCtx->clip->filterMode == fmUnordered ? -1 : rCtx->n), rCtx);

                if (isSingleInstance)
                    owner->framesInProgress.insert(rCtx->clip, rCtx->n);

                VSActivationReason ar = arInitial;

                if (pCtx->hasError()) {
                    ar = arError;
                    rCtx->setError(pCtx->getErrorMessage());
                } else if (pCtx != rCtx && pCtx->returnedFrame) {
                    if (--rCtx->numFrameRequests)
                        ar = arFrameReady;
                    else
                        ar = arAllFramesReady;

                    Q_ASSERT(rCtx->numFrameRequests >= 0);
                    rCtx->availableFrames.insert(NodeOutputKey(pCtx->clip, pCtx->n, pCtx->index), pCtx->returnedFrame);
                    rCtx->lastCompletedN = pCtx->n;
                    rCtx->lastCompletedNode = pCtx->node;
                }

                owner->lock.unlock();
                // run task

                PVideoFrame f = rCtx->clip->getFrameInternal(rCtx->n, ar, rCtx);
                ranTask = true;
                owner->lock.lock();

                if (f && rCtx->numFrameRequests > 0)
					qFatal("Frame returned but there are still pending frame requests, filter: %s", rCtx->clip->name.constData());

                owner->runningTasks.remove(FrameKey(rCtx->clip, rCtx->clip->filterMode == fmUnordered ? -1 : rCtx->n));

                if (f || ar == arError || rCtx->hasError()) {
                    // free all input frames quickly since the frame processing is done
                    rCtx->availableFrames.clear();

                    if (isSingleInstance) {
                        if (owner->framesInProgress[rCtx->clip] != rCtx->n && !rCtx->hasError())
                            qWarning("Releasing unobtained frame lock");

                        owner->framesInProgress.remove(rCtx->clip);
                    }

                    owner->allContexts.remove(NodeOutputKey(rCtx->clip, rCtx->n, rCtx->index));
                }

                if (rCtx->hasError()) {
                    PFrameContext n;

                    do {
                        n = rCtx->notificationChain;

                        if (n) {
                            rCtx->notificationChain.clear();
                            n->setError(rCtx->getErrorMessage());
                        }

                        if (rCtx->upstreamContext) {
                            owner->startInternal(rCtx);
                        }

                        if (rCtx->frameDone) {
                            owner->returnFrame(rCtx, rCtx->getErrorMessage());
                        }
                    } while ((rCtx = n));
                } else if (f) {
                    Q_ASSERT(rCtx->numFrameRequests == 0);
                    PFrameContext n;

                    do {
                        n = rCtx->notificationChain;

                        if (n)
                            rCtx->notificationChain.clear();

                        if (rCtx->upstreamContext) {
                            rCtx->returnedFrame = f;
                            owner->startInternal(rCtx);
                        }

                        if (rCtx->frameDone)
                            owner->returnFrame(rCtx, f);
                    } while ((rCtx = n));
                } else if (rCtx->numFrameRequests > 0 || rCtx->n < 0) {
                    // already scheduled or in the case of negative n it is simply a cache notify message
                } else {
					qFatal("No frame returned at the end of processing by %s", rCtx->clip->name.constData());
                }

                break;
            }
        }

        if ((!ranTask && !stop) || (owner->activeThreadCount() > owner->threadCount())) {
			owner->activeThreads.deref();
			owner->idleThreads++;
            owner->newWork.wait(&owner->lock);
			owner->idleThreads--;
			owner->activeThreads.ref();
        }

		if (stop) {
			owner->idleThreads--;
			owner->activeThreads.deref();
            owner->lock.unlock();
            return;
        }
    }
}

VSThreadPool::VSThreadPool(VSCore *core, int threads) : core(core), activeThreads(0), idleThreads(0) {
	maxThreads = threads > 0 ? threads : QThread::idealThreadCount();
	QMutexLocker m(&lock);
}

int VSThreadPool::activeThreadCount() const {
    return activeThreads;
}

int VSThreadPool::threadCount() const {
    return maxThreads;
}

void VSThreadPool::spawnThread() {
    VSThread *vst = new VSThread(this);
    allThreads.insert(vst);
    vst->start();
}

void VSThreadPool::setThreadCount(int threads) {
	maxThreads = threads;
}

void VSThreadPool::wakeThread() {
	if (activeThreads < maxThreads && idleThreads == 0)
		spawnThread();

    if (activeThreads < maxThreads)
        newWork.wakeOne();
}

void VSThreadPool::releaseThread() {
	activeThreads.deref();
}

void VSThreadPool::reserveThread() {
    activeThreads.ref();
}

void VSThreadPool::notifyCaches(CacheActivation reason) {
    QMutexLocker lock(&core->cacheLock);
    for (int i = 0; i < core->caches.count(); i++)
        tasks.insert(0, PFrameContext(new FrameContext(reason, 0, core->caches[i], PFrameContext())));
}

void VSThreadPool::start(const PFrameContext &context) {
    Q_ASSERT(context);
    QMutexLocker m(&lock);
	startInternal(context);
}

void VSThreadPool::returnFrame(const PFrameContext &rCtx, const PVideoFrame &f) {
    Q_ASSERT(rCtx->frameDone);
    // we need to unlock here so the callback may request more frames without causing a deadlock
    // AND so that slow callbacks will only block operations in this thread, not all the others
    lock.unlock();
    VSFrameRef *ref = new VSFrameRef(f);
    callbackLock.lock();
    rCtx->frameDone(rCtx->userData, ref, rCtx->n, rCtx->node, NULL);
    callbackLock.unlock();
    lock.lock();
}

void VSThreadPool::returnFrame(const PFrameContext &rCtx, const QByteArray &errMsg) {
    Q_ASSERT(rCtx->frameDone);
    // we need to unlock here so the callback may request more frames without causing a deadlock
    // AND so that slow callbacks will only block operations in this thread, not all the others
    lock.unlock();
    callbackLock.lock();
    rCtx->frameDone(rCtx->userData, NULL, rCtx->n, rCtx->node, errMsg.constData());
    callbackLock.unlock();
    lock.lock();
}

void VSThreadPool::startInternal(const PFrameContext &context) {
    //technically this could be done by walking up the context chain and add a new notification to the correct one
    //unfortunately this would probably be quite slow for deep scripts so just hope the cache catches it

    if (context->n < 0)
		qFatal("Negative frame request by: %s", context->clip->getName().constData());

    // check to see if it's time to reevaluate cache sizes
    if (core->memory->isOverLimit()) {
        ticks = 0;
        notifyCaches(cNeedMemory);
    }

    // a normal tick for caches to adjust their sizes based on recent history
    if (!context->upstreamContext && ticks.fetchAndAddAcquire(1) == 99) {
        ticks = 0;
        notifyCaches(cCacheTick);
    }

    // add it immediately if the task is to return a completed frame and put it at the front of the line since it has no dependencies
    if (context->returnedFrame || context->hasError()) {
        tasks.prepend(context);
        wakeThread();
        return;
    } else {
        if (context->upstreamContext)
            context->upstreamContext->numFrameRequests++;

        ////////////////////////
        // see if the task is a duplicate
        foreach(const PFrameContext &ctx, tasks) {
            if (context->clip == ctx->clip && context->n == ctx->n && context->index == ctx->index) {
                if (ctx->returnedFrame) {
                    // special case where the requested frame is encountered "by accident"
                    context->returnedFrame = ctx->returnedFrame;
                    tasks.append(context);
                    wakeThread();
                    return;
                } else {
                    PFrameContext rCtx = ctx;

                    if (rCtx->returnedFrame)
                        rCtx = rCtx->upstreamContext;

                    if (context->clip == rCtx->clip && context->n == rCtx->n && context->index == ctx->index) {
                        PFrameContext t = rCtx;

                        while (t && t->notificationChain)
                            t = t->notificationChain;

                        t->notificationChain = context;
                        return;
                    }
                }
            }
        }

        NodeOutputKey p(context->clip, context->n, context->index);

        if (allContexts.contains(p)) {
            PFrameContext ctx = allContexts[p];
            Q_ASSERT(ctx);
            Q_ASSERT(context->clip == ctx->clip && context->n == ctx->n);

            if (ctx->returnedFrame) {
                // special case where the requested frame is encountered "by accident"
                context->returnedFrame = ctx->returnedFrame;
                tasks.append(context);
                wakeThread();
                return;
            } else {
                while (ctx->notificationChain)
                    ctx = ctx->notificationChain;
                ctx->notificationChain = context;
                return;
            }
        } else {
            allContexts[p] = context;
        }

        tasks.append(context);
        wakeThread();
        return;
    }

}

bool VSThreadPool::isWorkerThread() {
	QMutexLocker m(&lock);
	return allThreads.contains((VSThread *)QThread::currentThread());
}

void VSThreadPool::waitForDone() {
	// todo
}

VSThreadPool::~VSThreadPool() {
	QMutexLocker m(&lock);

	// fixme, hangs on free
    while (allThreads.count()) {
        VSThread *t = *allThreads.begin();
        t->stopThread();
        newWork.wakeAll();
        m.unlock();
        t->wait();
        m.relock();
        allThreads.remove(t);
        delete t;
        newWork.wakeAll();
    }
};
<commit_msg>Undo a part of the previous commit since it's broken<commit_after>/*
* Copyright (c) 2012 Fredrik Mellbin
*
* This file is part of VapourSynth.
*
* VapourSynth is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* VapourSynth is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with VapourSynth; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include "vscore.h"
#ifdef VS_TARGET_CPU_X86
#include "x86utils.h"
#endif

VSThread::VSThread(VSThreadPool *owner) : owner(owner), stop(false) {

}

void VSThread::stopThread() {
    stop = true;
}

void VSThread::run() {
#ifdef VS_TARGET_OS_WINDOWS
    if (!vs_isMMXStateOk())
        qFatal("Bad MMX state detected after creating new thread");
    if (!vs_isFPUStateOk())
        qFatal("Bad FPU state detected after creating new thread");
    if (!vs_isSSEStateOk())
        qFatal("Bad SSE state detected after creating new thread");
#endif

    owner->lock.lock();
	owner->activeThreads.ref();

    while (true) {
        bool ranTask = false;

        for (int i = 0; i < owner->tasks.count(); i++) {
            PFrameContext rCtx = owner->tasks[i];

            if (rCtx->frameDone && rCtx->returnedFrame) {
                owner->tasks.removeAt(i--);
                owner->returnFrame(rCtx, rCtx->returnedFrame);
                ranTask = true;
                break;
            } else if (rCtx->frameDone && rCtx->hasError()) {
                owner->tasks.removeAt(i--);
                owner->returnFrame(rCtx, rCtx->getErrorMessage());
                ranTask = true;
                break;
            } else {

                PFrameContext pCtx = rCtx;

                if (rCtx->returnedFrame || rCtx->hasError())
                    rCtx = rCtx->upstreamContext;

                Q_ASSERT(rCtx);
                Q_ASSERT(pCtx);

                // if an error has already been flagged upstream simply drop this task so a filter won't get multiple arError calls for the same frame
                if (rCtx->hasError()) {
                    owner->tasks.removeAt(i--);
                    continue;
                }

                bool isSingleInstance = (rCtx->clip->filterMode == fmParallelRequests && pCtx->returnedFrame && rCtx->numFrameRequests == 1 && pCtx != rCtx) || rCtx->clip->filterMode == fmSerial;

                // this check is common for both filter modes, it makes sure that multiple calls won't be made in parallel to a single filter to produce the same frame
                // special casing so serial unordered doesn't need yet another list
                if (owner->runningTasks.contains(FrameKey(rCtx->clip, rCtx->clip->filterMode == fmUnordered ? -1 : rCtx->n)))
                    continue;

                if (isSingleInstance) {
                    // this is the complicated case, a new frame may not be started until all calls are completed for the current one
                    if (owner->framesInProgress.contains(rCtx->clip) && owner->framesInProgress[rCtx->clip] != rCtx->n)
                        continue;
                }

                // mark task as active
                owner->tasks.removeAt(i--);
                owner->runningTasks.insert(FrameKey(rCtx->clip, rCtx->clip->filterMode == fmUnordered ? -1 : rCtx->n), rCtx);

                if (isSingleInstance)
                    owner->framesInProgress.insert(rCtx->clip, rCtx->n);

                VSActivationReason ar = arInitial;

                if (pCtx->hasError()) {
                    ar = arError;
                    rCtx->setError(pCtx->getErrorMessage());
                } else if (pCtx != rCtx && pCtx->returnedFrame) {
                    if (--rCtx->numFrameRequests)
                        ar = arFrameReady;
                    else
                        ar = arAllFramesReady;

                    Q_ASSERT(rCtx->numFrameRequests >= 0);
                    rCtx->availableFrames.insert(NodeOutputKey(pCtx->clip, pCtx->n, pCtx->index), pCtx->returnedFrame);
                    rCtx->lastCompletedN = pCtx->n;
                    rCtx->lastCompletedNode = pCtx->node;
                }

                owner->lock.unlock();
                // run task

                PVideoFrame f = rCtx->clip->getFrameInternal(rCtx->n, ar, rCtx);
                ranTask = true;
                owner->lock.lock();

                if (f && rCtx->numFrameRequests > 0)
					qFatal("Frame returned but there are still pending frame requests, filter: %s", rCtx->clip->name.constData());

                owner->runningTasks.remove(FrameKey(rCtx->clip, rCtx->clip->filterMode == fmUnordered ? -1 : rCtx->n));

                if (f || ar == arError || rCtx->hasError()) {
                    // free all input frames quickly since the frame processing is done
                    rCtx->availableFrames.clear();

                    if (isSingleInstance) {
                        if (owner->framesInProgress[rCtx->clip] != rCtx->n && !rCtx->hasError())
                            qWarning("Releasing unobtained frame lock");

                        owner->framesInProgress.remove(rCtx->clip);
                    }

                    owner->allContexts.remove(NodeOutputKey(rCtx->clip, rCtx->n, rCtx->index));
                }

                if (rCtx->hasError()) {
                    PFrameContext n;

                    do {
                        n = rCtx->notificationChain;

                        if (n) {
                            rCtx->notificationChain.clear();
                            n->setError(rCtx->getErrorMessage());
                        }

                        if (rCtx->upstreamContext) {
                            owner->startInternal(rCtx);
                        }

                        if (rCtx->frameDone) {
                            owner->returnFrame(rCtx, rCtx->getErrorMessage());
                        }
                    } while ((rCtx = n));
                } else if (f) {
                    Q_ASSERT(rCtx->numFrameRequests == 0);
                    PFrameContext n;

                    do {
                        n = rCtx->notificationChain;

                        if (n)
                            rCtx->notificationChain.clear();

                        if (rCtx->upstreamContext) {
                            rCtx->returnedFrame = f;
                            owner->startInternal(rCtx);
                        }

                        if (rCtx->frameDone)
                            owner->returnFrame(rCtx, f);
                    } while ((rCtx = n));
                } else if (rCtx->numFrameRequests > 0 || rCtx->n < 0) {
                    // already scheduled or in the case of negative n it is simply a cache notify message
                } else {
					qFatal("No frame returned at the end of processing by %s", rCtx->clip->name.constData());
                }

                break;
            }
        }

        if ((!ranTask && !stop) || (owner->activeThreadCount() > owner->threadCount())) {
			owner->activeThreads.deref();
			owner->idleThreads++;
            owner->newWork.wait(&owner->lock);
			owner->idleThreads--;
			owner->activeThreads.ref();
        }

		if (stop) {
			owner->idleThreads--;
			owner->activeThreads.deref();
            owner->lock.unlock();
            return;
        }
    }
}

VSThreadPool::VSThreadPool(VSCore *core, int threads) : core(core), activeThreads(0), idleThreads(0) {
	maxThreads = threads > 0 ? threads : QThread::idealThreadCount();
	QMutexLocker m(&lock);
}

int VSThreadPool::activeThreadCount() const {
    return activeThreads;
}

int VSThreadPool::threadCount() const {
    return maxThreads;
}

void VSThreadPool::spawnThread() {
    VSThread *vst = new VSThread(this);
    allThreads.insert(vst);
    vst->start();
}

void VSThreadPool::setThreadCount(int threads) {
	maxThreads = threads;
}

void VSThreadPool::wakeThread() {
	if (activeThreads < maxThreads && idleThreads == 0)
		spawnThread();

    if (activeThreads < maxThreads)
        newWork.wakeOne();
}

void VSThreadPool::releaseThread() {
	activeThreads.deref();
}

void VSThreadPool::reserveThread() {
    activeThreads.ref();
}

void VSThreadPool::notifyCaches(CacheActivation reason) {
    QMutexLocker lock(&core->cacheLock);
    for (int i = 0; i < core->caches.count(); i++)
        tasks.insert(0, PFrameContext(new FrameContext(reason, 0, core->caches[i], PFrameContext())));
}

void VSThreadPool::start(const PFrameContext &context) {
    Q_ASSERT(context);
    QMutexLocker m(&lock);
	startInternal(context);
}

void VSThreadPool::returnFrame(const PFrameContext &rCtx, const PVideoFrame &f) {
    Q_ASSERT(rCtx->frameDone);
    // we need to unlock here so the callback may request more frames without causing a deadlock
    // AND so that slow callbacks will only block operations in this thread, not all the others
    lock.unlock();
    VSFrameRef *ref = new VSFrameRef(f);
    callbackLock.lock();
    rCtx->frameDone(rCtx->userData, ref, rCtx->n, rCtx->node, NULL);
    callbackLock.unlock();
    lock.lock();
}

void VSThreadPool::returnFrame(const PFrameContext &rCtx, const QByteArray &errMsg) {
    Q_ASSERT(rCtx->frameDone);
    // we need to unlock here so the callback may request more frames without causing a deadlock
    // AND so that slow callbacks will only block operations in this thread, not all the others
    lock.unlock();
    callbackLock.lock();
    rCtx->frameDone(rCtx->userData, NULL, rCtx->n, rCtx->node, errMsg.constData());
    callbackLock.unlock();
    lock.lock();
}

void VSThreadPool::startInternal(const PFrameContext &context) {
    //technically this could be done by walking up the context chain and add a new notification to the correct one
    //unfortunately this would probably be quite slow for deep scripts so just hope the cache catches it

    if (context->n < 0)
		qFatal("Negative frame request by: %s", context->clip->getName().constData());

    // check to see if it's time to reevaluate cache sizes
    if (core->memory->isOverLimit()) {
        ticks = 0;
        notifyCaches(cNeedMemory);
    }

    // a normal tick for caches to adjust their sizes based on recent history
    if (!context->upstreamContext && ticks.fetchAndAddAcquire(1) == 99) {
        ticks = 0;
        notifyCaches(cCacheTick);
    }

    // add it immediately if the task is to return a completed frame and put it at the front of the line since it has no dependencies
    if (context->returnedFrame || context->hasError()) {
        tasks.append(context);
        wakeThread();
        return;
    } else {
        if (context->upstreamContext)
            context->upstreamContext->numFrameRequests++;

        ////////////////////////
        // see if the task is a duplicate
        foreach(const PFrameContext &ctx, tasks) {
            if (context->clip == ctx->clip && context->n == ctx->n && context->index == ctx->index) {
                if (ctx->returnedFrame) {
                    // special case where the requested frame is encountered "by accident"
                    context->returnedFrame = ctx->returnedFrame;
                    tasks.append(context);
                    wakeThread();
                    return;
                } else {
                    PFrameContext rCtx = ctx;

                    if (rCtx->returnedFrame)
                        rCtx = rCtx->upstreamContext;

                    if (context->clip == rCtx->clip && context->n == rCtx->n && context->index == ctx->index) {
                        PFrameContext t = rCtx;

                        while (t && t->notificationChain)
                            t = t->notificationChain;

                        t->notificationChain = context;
                        return;
                    }
                }
            }
        }

        NodeOutputKey p(context->clip, context->n, context->index);

        if (allContexts.contains(p)) {
            PFrameContext ctx = allContexts[p];
            Q_ASSERT(ctx);
            Q_ASSERT(context->clip == ctx->clip && context->n == ctx->n);

            if (ctx->returnedFrame) {
                // special case where the requested frame is encountered "by accident"
                context->returnedFrame = ctx->returnedFrame;
                tasks.append(context);
                wakeThread();
                return;
            } else {
                while (ctx->notificationChain)
                    ctx = ctx->notificationChain;
                ctx->notificationChain = context;
                return;
            }
        } else {
            allContexts[p] = context;
        }

        tasks.append(context);
        wakeThread();
        return;
    }

}

bool VSThreadPool::isWorkerThread() {
	QMutexLocker m(&lock);
	return allThreads.contains((VSThread *)QThread::currentThread());
}

void VSThreadPool::waitForDone() {
	// todo
}

VSThreadPool::~VSThreadPool() {
	QMutexLocker m(&lock);

	// fixme, hangs on free
    while (allThreads.count()) {
        VSThread *t = *allThreads.begin();
        t->stopThread();
        newWork.wakeAll();
        m.unlock();
        t->wait();
        m.relock();
        allThreads.remove(t);
        delete t;
        newWork.wakeAll();
    }
};
<|endoftext|>


<commit_before>// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

#include "trace.h"
#include "utils.h"
#include "pal.h"
#include "fx_ver.h"
#include "error_codes.h"

#if FEATURE_APPHOST
#define CURHOST_TYPE    _X("apphost")
#define CUREXE_PKG_VER APPHOST_PKG_VER
#else // !FEATURE_APPHOST
#define CURHOST_TYPE    _X("dotnet")
#define CUREXE_PKG_VER HOST_PKG_VER
#endif // !FEATURE_APPHOST

typedef int(*hostfxr_main_fn) (const int argc, const pal::char_t* argv[]);

#if FEATURE_APPHOST

/**
 * Detect if the apphost executable is allowed to load and execute a managed assembly.
 *
 *    - The exe is built with a known hash string at some offset in the image
 *    - The exe is useless as is with the built-in hash value, and will fail with an error message
 *    - The hash value should be replaced with the managed DLL filename using "NUL terminated UTF-8" by "dotnet build"
 *    - The exe may be signed at this point by the app publisher
 *    - When the exe runs, the managed DLL name is validated against the executable's own name
 *    - If validation passes, the embedded managed DLL name will be loaded by the exe
 *    - Note: the maximum size of the managed DLL file name can be 1024 bytes in UTF-8 (not including NUL)
 *        o https://en.wikipedia.org/wiki/Comparison_of_file_systems
 *          has more details on maximum file name sizes.
 */
#define EMBED_HASH_HI_PART_UTF8 "c3ab8ff13720e8ad9047dd39466b3c89" // SHA-256 of "foobar" in UTF-8
#define EMBED_HASH_LO_PART_UTF8 "74e592c2fa383d4a3960714caef0c4f2"
#define EMBED_HASH_FULL_UTF8    (EMBED_HASH_HI_PART_UTF8 EMBED_HASH_LO_PART_UTF8) // NUL terminated
bool is_exe_enabled_for_execution(const pal::string_t& own_path)
{
    constexpr int EMBED_SZ = sizeof(EMBED_HASH_FULL_UTF8) / sizeof(EMBED_HASH_FULL_UTF8[0]);
    constexpr int EMBED_MAX = (EMBED_SZ > 1025 ? EMBED_SZ : 1025); // 1024 DLL name length, 1 NUL
    static const char embed[EMBED_MAX] = EMBED_HASH_FULL_UTF8;     // series of NULs followed by embed hash string
    static const char hi_part[] = EMBED_HASH_HI_PART_UTF8;
    static const char lo_part[] = EMBED_HASH_LO_PART_UTF8;

    // At this point the "embed" variable may contain the embed hash value specified above at compile time
    // or the managed DLL name replaced by "dotnet build".
    std::string binding(&embed[0]);
    pal::string_t pal_binding;
    if (!pal::utf8_palstring(binding, &pal_binding))
    {
        trace::error(_X("The managed DLL bound to this executable could not be retrieved from the executable image."));
        return false;
    }

    // Since the single static string is replaced by editing the executable, a reference string is needed to do the compare.
    // So use two parts of the string that will be unaffected by the edit.
    size_t hi_len = (sizeof(hi_part) / sizeof(hi_part[0])) - 1;
    size_t lo_len = (sizeof(lo_part) / sizeof(lo_part[0])) - 1;

    if ((binding.size() >= (hi_len + lo_len)) &&
        binding.compare(0, hi_len, &hi_part[0]) == 0 &&
        binding.compare(hi_len, lo_len, &lo_part[0]) == 0)
    {
        trace::error(_X("This executable is not bound to a managed DLL to execute. The binding value is: '%s'"), pal_binding.c_str());
        return false;
    }

    pal::string_t own_name = get_filename(own_path);
    pal::string_t own_dll_filename = get_executable(own_name) + _X(".dll");

    if (pal::strcasecmp(own_dll_filename.c_str(), pal_binding.c_str()) != 0)
    {
        trace::error(_X("The managed DLL bound to this executable: '%s', did not match own name '%s'."), pal_binding.c_str(), own_dll_filename.c_str());
        return false;
    }

    trace::info(_X("The managed DLL bound to this executable is: '%s'"), pal_binding.c_str());
    return true;
}
#endif // FEATURE_APPHOST

pal::string_t resolve_fxr_path(const pal::string_t& own_dir)
{
#if FEATURE_APPHOST
    pal::string_t fxr_path;
    if (library_exists_in_dir(own_dir, LIBFXR_NAME, &fxr_path))
    {
        trace::info(_X("Resolved fxr [%s]..."), fxr_path.c_str());
        return fxr_path;
    }

    trace::error(_X("A fatal error occurred, the required library %s could not be found at [%s]"), LIBFXR_NAME, own_dir.c_str());
    return pal::string_t();
#else
    pal::string_t fxr_dir = own_dir;
    append_path(&fxr_dir, _X("host"));
    append_path(&fxr_dir, _X("fxr"));
    if (!pal::directory_exists(fxr_dir))
    {
        trace::error(_X("A fatal error occurred, the folder [%s] does not exist"), fxr_dir.c_str());
        return pal::string_t();
    }

    trace::info(_X("Reading fx resolver directory=[%s]"), fxr_dir.c_str());

    std::vector<pal::string_t> list;
    pal::readdir_onlydirectories(fxr_dir, &list);

    fx_ver_t max_ver(-1, -1, -1);
    for (const auto& dir : list)
    {
        trace::info(_X("Considering fxr version=[%s]..."), dir.c_str());

        pal::string_t ver = get_filename(dir);

        fx_ver_t fx_ver(-1, -1, -1);
        if (fx_ver_t::parse(ver, &fx_ver, false))
        {
            max_ver = std::max(max_ver, fx_ver);
        }
    }

    if (max_ver == fx_ver_t(-1, -1, -1))
    {
        trace::error(_X("A fatal error occurred, the folder [%s] does not contain any version-numbered child folders"), fxr_dir.c_str());
        return pal::string_t();
    }

    pal::string_t max_ver_str = max_ver.as_str();
    append_path(&fxr_dir, max_ver_str.c_str());
    trace::info(_X("Detected latest fxr version=[%s]..."), fxr_dir.c_str());

    pal::string_t ret_path;
    if (library_exists_in_dir(fxr_dir, LIBFXR_NAME, &ret_path))
    {
        trace::info(_X("Resolved fxr [%s]..."), ret_path.c_str());
        return ret_path;
    }

    trace::error(_X("A fatal error occurred, the required library %s could not be found in [%s]"), LIBFXR_NAME, fxr_dir.c_str());

    return pal::string_t();
#endif
}

int run(const int argc, const pal::char_t* argv[])
{
    pal::string_t own_path;
    if (!pal::get_own_executable_path(&own_path) || !pal::realpath(&own_path))
    {
        trace::error(_X("Failed to resolve full path of the current executable [%s]"), own_path.c_str());
        return StatusCode::CoreHostCurExeFindFailure;
    }

#ifdef FEATURE_APPHOST
    if (!is_exe_enabled_for_execution(own_path))
    {
        trace::error(_X("A fatal error was encountered. This executable was not bound to load a managed DLL."));
        return StatusCode::AppHostExeNotBoundFailure;
    }
#else
    pal::string_t own_name = get_executable(get_filename(own_path));

    if (pal::strcasecmp(own_name.c_str(), CURHOST_TYPE) != 0)
    {
        trace::error(_X("A fatal error was encountered. Cannot execute %s when renamed to  %s."), CURHOST_TYPE,own_name.c_str());
        return StatusCode::CoreHostEntryPointFailure;
    }

    if (argc <= 1)
    {
        trace::println();
        trace::println(_X("Usage: dotnet [options]"));
        trace::println(_X("Usage: dotnet [path-to-application]"));
        trace::println();
        trace::println(_X("Options:"));
        trace::println(_X("  -h|--help         Display help."));
        trace::println(_X("  --version         Display the current SDK version."));
        trace::println(_X("  --list-sdks       Display the installed SDKs."));
        trace::println(_X("  --list-runtimes   Display the installed runtimes."));
        trace::println();
        trace::println(_X("path-to-application:"));
        trace::println(_X("  The path to an application .dll file to execute."));
        return StatusCode::InvalidArgFailure;
    }
#endif

    pal::dll_t fxr;

    pal::string_t own_dir = get_directory(own_path);

    // Load library
    pal::string_t fxr_path = resolve_fxr_path(own_dir);
    if (fxr_path.empty())
    {
        return StatusCode::CoreHostLibMissingFailure;
    }

    if (!pal::load_library(&fxr_path, &fxr))
    {
        trace::error(_X("The library %s was found, but loading it from %s failed"), LIBFXR_NAME, fxr_path.c_str());
        trace::error(_X("  - Installing .NET Core prerequisites might help resolve this problem."));
        trace::error(_X("     %s"), DOTNET_CORE_URL);
        return StatusCode::CoreHostLibLoadFailure;
    }

    // Previous corehost trace messages must be printed before calling trace::setup in hostfxr
    trace::flush();

    // Obtain the entrypoints.
    hostfxr_main_fn main_fn = (hostfxr_main_fn) pal::get_symbol(fxr, "hostfxr_main");
    int code = main_fn(argc, argv);
    pal::unload_library(fxr);
    return code;
}

#if defined(_WIN32)
int __cdecl wmain(const int argc, const pal::char_t* argv[])
#else
int main(const int argc, const pal::char_t* argv[])
#endif
{
    trace::setup();

    if (trace::is_enabled())
    {
        trace::info(_X("--- Invoked %s [version: %s, commit hash: %s] main = {"), CURHOST_TYPE, _STRINGIFY(CUREXE_PKG_VER), _STRINGIFY(REPO_COMMIT_HASH));
        for (int i = 0; i < argc; ++i)
        {
            trace::info(_X("%s"), argv[i]);
        }
        trace::info(_X("}"));
    }

    return run(argc, argv);
}

<commit_msg>Fix issue with standalone exe name length >64 (#3659)<commit_after>// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

#include "trace.h"
#include "utils.h"
#include "pal.h"
#include "fx_ver.h"
#include "error_codes.h"

#if FEATURE_APPHOST
#define CURHOST_TYPE    _X("apphost")
#define CUREXE_PKG_VER APPHOST_PKG_VER
#else // !FEATURE_APPHOST
#define CURHOST_TYPE    _X("dotnet")
#define CUREXE_PKG_VER HOST_PKG_VER
#endif // !FEATURE_APPHOST

typedef int(*hostfxr_main_fn) (const int argc, const pal::char_t* argv[]);

#if FEATURE_APPHOST

/**
 * Detect if the apphost executable is allowed to load and execute a managed assembly.
 *
 *    - The exe is built with a known hash string at some offset in the image
 *    - The exe is useless as is with the built-in hash value, and will fail with an error message
 *    - The hash value should be replaced with the managed DLL filename using "NUL terminated UTF-8" by "dotnet build"
 *    - The exe may be signed at this point by the app publisher
 *    - When the exe runs, the managed DLL name is validated against the executable's own name
 *    - If validation passes, the embedded managed DLL name will be loaded by the exe
 *    - Note: the maximum size of the managed DLL file name can be 1024 bytes in UTF-8 (not including NUL)
 *        o https://en.wikipedia.org/wiki/Comparison_of_file_systems
 *          has more details on maximum file name sizes.
 */
#define EMBED_HASH_HI_PART_UTF8 "c3ab8ff13720e8ad9047dd39466b3c89" // SHA-256 of "foobar" in UTF-8
#define EMBED_HASH_LO_PART_UTF8 "74e592c2fa383d4a3960714caef0c4f2"
#define EMBED_HASH_FULL_UTF8    (EMBED_HASH_HI_PART_UTF8 EMBED_HASH_LO_PART_UTF8) // NUL terminated
bool is_exe_enabled_for_execution(const pal::string_t& own_path)
{
    constexpr int EMBED_SZ = sizeof(EMBED_HASH_FULL_UTF8) / sizeof(EMBED_HASH_FULL_UTF8[0]);
    constexpr int EMBED_MAX = (EMBED_SZ > 1025 ? EMBED_SZ : 1025); // 1024 DLL name length, 1 NUL

    // Contains the embed hash value at compile time or the managed DLL name replaced by "dotnet build".
    // Must not be 'const' because std::string(&embed[0]) below would bind to a const string ctor plus length
    // where length is determined at compile time (=64) instead of the actual length of the string at runtime.
    static char embed[EMBED_MAX] = EMBED_HASH_FULL_UTF8;     // series of NULs followed by embed hash string

    static const char hi_part[] = EMBED_HASH_HI_PART_UTF8;
    static const char lo_part[] = EMBED_HASH_LO_PART_UTF8;

    std::string binding(&embed[0]);
    pal::string_t pal_binding;
    if (!pal::utf8_palstring(binding, &pal_binding))
    {
        trace::error(_X("The managed DLL bound to this executable could not be retrieved from the executable image."));
        return false;
    }

    // Since the single static string is replaced by editing the executable, a reference string is needed to do the compare.
    // So use two parts of the string that will be unaffected by the edit.
    size_t hi_len = (sizeof(hi_part) / sizeof(hi_part[0])) - 1;
    size_t lo_len = (sizeof(lo_part) / sizeof(lo_part[0])) - 1;

    if ((binding.size() >= (hi_len + lo_len)) &&
        binding.compare(0, hi_len, &hi_part[0]) == 0 &&
        binding.compare(hi_len, lo_len, &lo_part[0]) == 0)
    {
        trace::error(_X("This executable is not bound to a managed DLL to execute. The binding value is: '%s'"), pal_binding.c_str());
        return false;
    }

    pal::string_t own_name = get_filename(own_path);
    pal::string_t own_dll_filename = get_executable(own_name) + _X(".dll");

    if (pal::strcasecmp(own_dll_filename.c_str(), pal_binding.c_str()) != 0)
    {
        trace::error(_X("The managed DLL bound to this executable: '%s', did not match own name '%s'."), pal_binding.c_str(), own_dll_filename.c_str());
        return false;
    }

    trace::info(_X("The managed DLL bound to this executable is: '%s'"), pal_binding.c_str());
    return true;
}
#endif // FEATURE_APPHOST

pal::string_t resolve_fxr_path(const pal::string_t& own_dir)
{
#if FEATURE_APPHOST
    pal::string_t fxr_path;
    if (library_exists_in_dir(own_dir, LIBFXR_NAME, &fxr_path))
    {
        trace::info(_X("Resolved fxr [%s]..."), fxr_path.c_str());
        return fxr_path;
    }

    trace::error(_X("A fatal error occurred, the required library %s could not be found at [%s]"), LIBFXR_NAME, own_dir.c_str());
    return pal::string_t();
#else
    pal::string_t fxr_dir = own_dir;
    append_path(&fxr_dir, _X("host"));
    append_path(&fxr_dir, _X("fxr"));
    if (!pal::directory_exists(fxr_dir))
    {
        trace::error(_X("A fatal error occurred, the folder [%s] does not exist"), fxr_dir.c_str());
        return pal::string_t();
    }

    trace::info(_X("Reading fx resolver directory=[%s]"), fxr_dir.c_str());

    std::vector<pal::string_t> list;
    pal::readdir_onlydirectories(fxr_dir, &list);

    fx_ver_t max_ver(-1, -1, -1);
    for (const auto& dir : list)
    {
        trace::info(_X("Considering fxr version=[%s]..."), dir.c_str());

        pal::string_t ver = get_filename(dir);

        fx_ver_t fx_ver(-1, -1, -1);
        if (fx_ver_t::parse(ver, &fx_ver, false))
        {
            max_ver = std::max(max_ver, fx_ver);
        }
    }

    if (max_ver == fx_ver_t(-1, -1, -1))
    {
        trace::error(_X("A fatal error occurred, the folder [%s] does not contain any version-numbered child folders"), fxr_dir.c_str());
        return pal::string_t();
    }

    pal::string_t max_ver_str = max_ver.as_str();
    append_path(&fxr_dir, max_ver_str.c_str());
    trace::info(_X("Detected latest fxr version=[%s]..."), fxr_dir.c_str());

    pal::string_t ret_path;
    if (library_exists_in_dir(fxr_dir, LIBFXR_NAME, &ret_path))
    {
        trace::info(_X("Resolved fxr [%s]..."), ret_path.c_str());
        return ret_path;
    }

    trace::error(_X("A fatal error occurred, the required library %s could not be found in [%s]"), LIBFXR_NAME, fxr_dir.c_str());

    return pal::string_t();
#endif
}

int run(const int argc, const pal::char_t* argv[])
{
    pal::string_t own_path;
    if (!pal::get_own_executable_path(&own_path) || !pal::realpath(&own_path))
    {
        trace::error(_X("Failed to resolve full path of the current executable [%s]"), own_path.c_str());
        return StatusCode::CoreHostCurExeFindFailure;
    }

#ifdef FEATURE_APPHOST
    if (!is_exe_enabled_for_execution(own_path))
    {
        trace::error(_X("A fatal error was encountered. This executable was not bound to load a managed DLL."));
        return StatusCode::AppHostExeNotBoundFailure;
    }
#else
    pal::string_t own_name = get_executable(get_filename(own_path));

    if (pal::strcasecmp(own_name.c_str(), CURHOST_TYPE) != 0)
    {
        trace::error(_X("A fatal error was encountered. Cannot execute %s when renamed to  %s."), CURHOST_TYPE,own_name.c_str());
        return StatusCode::CoreHostEntryPointFailure;
    }

    if (argc <= 1)
    {
        trace::println();
        trace::println(_X("Usage: dotnet [options]"));
        trace::println(_X("Usage: dotnet [path-to-application]"));
        trace::println();
        trace::println(_X("Options:"));
        trace::println(_X("  -h|--help         Display help."));
        trace::println(_X("  --version         Display the current SDK version."));
        trace::println(_X("  --list-sdks       Display the installed SDKs."));
        trace::println(_X("  --list-runtimes   Display the installed runtimes."));
        trace::println();
        trace::println(_X("path-to-application:"));
        trace::println(_X("  The path to an application .dll file to execute."));
        return StatusCode::InvalidArgFailure;
    }
#endif

    pal::dll_t fxr;

    pal::string_t own_dir = get_directory(own_path);

    // Load library
    pal::string_t fxr_path = resolve_fxr_path(own_dir);
    if (fxr_path.empty())
    {
        return StatusCode::CoreHostLibMissingFailure;
    }

    if (!pal::load_library(&fxr_path, &fxr))
    {
        trace::error(_X("The library %s was found, but loading it from %s failed"), LIBFXR_NAME, fxr_path.c_str());
        trace::error(_X("  - Installing .NET Core prerequisites might help resolve this problem."));
        trace::error(_X("     %s"), DOTNET_CORE_URL);
        return StatusCode::CoreHostLibLoadFailure;
    }

    // Previous corehost trace messages must be printed before calling trace::setup in hostfxr
    trace::flush();

    // Obtain the entrypoints.
    hostfxr_main_fn main_fn = (hostfxr_main_fn) pal::get_symbol(fxr, "hostfxr_main");
    int code = main_fn(argc, argv);
    pal::unload_library(fxr);
    return code;
}

#if defined(_WIN32)
int __cdecl wmain(const int argc, const pal::char_t* argv[])
#else
int main(const int argc, const pal::char_t* argv[])
#endif
{
    trace::setup();

    if (trace::is_enabled())
    {
        trace::info(_X("--- Invoked %s [version: %s, commit hash: %s] main = {"), CURHOST_TYPE, _STRINGIFY(CUREXE_PKG_VER), _STRINGIFY(REPO_COMMIT_HASH));
        for (int i = 0; i < argc; ++i)
        {
            trace::info(_X("%s"), argv[i]);
        }
        trace::info(_X("}"));
    }

    return run(argc, argv);
}

<|endoftext|>


<commit_before>#include "testApp.h"

//--------------------------------------------------------------
void testApp::setup(){
#if 0
	sink_ = new SinkNode();
	constant_ = new ConstantNode(1.0);
	sink_ << constant_;
#endif
}

//--------------------------------------------------------------
void testApp::update(){

}

//--------------------------------------------------------------
void testApp::draw(){
	plot(std::vector<double>(256, 5.0));
}

void testApp::plot(const std::vector<double>& data)
{
	static float radius = 2.0f;

	float x = 0.0f;
	float max = *std::max_element(data.begin(), data.end());

	for (double y : data) {
		ofCircle(x, -y / max * ofGetHeight() / 2 + (ofGetHeight() / 2), radius);
		x += ofGetWidth() / data.size();
	}
}

//--------------------------------------------------------------
void testApp::keyPressed(int key){

}

//--------------------------------------------------------------
void testApp::keyReleased(int key){

}

//--------------------------------------------------------------
void testApp::mouseMoved(int x, int y ){

}

//--------------------------------------------------------------
void testApp::mouseDragged(int x, int y, int button){

}

//--------------------------------------------------------------
void testApp::mousePressed(int x, int y, int button){

}

//--------------------------------------------------------------
void testApp::mouseReleased(int x, int y, int button){

}

//--------------------------------------------------------------
void testApp::windowResized(int w, int h){

}

//--------------------------------------------------------------
void testApp::gotMessage(ofMessage msg){

}

//--------------------------------------------------------------
void testApp::dragEvent(ofDragInfo dragInfo){

}
<commit_msg>データの最大値を1.0と仮定<commit_after>#include "testApp.h"

//--------------------------------------------------------------
void testApp::setup(){
#if 0
	sink_ = new SinkNode();
	constant_ = new ConstantNode(1.0);
	sink_ << constant_;
#endif
}

//--------------------------------------------------------------
void testApp::update(){

}

//--------------------------------------------------------------
void testApp::draw(){
	plot(std::vector<double>(256, -1.0));
}

// f[^̍ől1.0Ɖ肵ĕ`
void testApp::plot(const std::vector<double>& data)
{
	static float radius = 2.0f;

	float x = 0.0f;

	for (double y : data) {
		ofCircle(x, -y * ofGetHeight() / 2 + (ofGetHeight() / 2), radius);
		x += ofGetWidth() / data.size();
	}
}

//--------------------------------------------------------------
void testApp::keyPressed(int key){

}

//--------------------------------------------------------------
void testApp::keyReleased(int key){

}

//--------------------------------------------------------------
void testApp::mouseMoved(int x, int y ){

}

//--------------------------------------------------------------
void testApp::mouseDragged(int x, int y, int button){

}

//--------------------------------------------------------------
void testApp::mousePressed(int x, int y, int button){

}

//--------------------------------------------------------------
void testApp::mouseReleased(int x, int y, int button){

}

//--------------------------------------------------------------
void testApp::windowResized(int w, int h){

}

//--------------------------------------------------------------
void testApp::gotMessage(ofMessage msg){

}

//--------------------------------------------------------------
void testApp::dragEvent(ofDragInfo dragInfo){

}
<|endoftext|>


<commit_before>/*

Copyright (c) 2015, Project OSRM contributors
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*/

#ifndef TINY_COMPONENTS_HPP
#define TINY_COMPONENTS_HPP

#include "../typedefs.h"
#include "../data_structures/deallocating_vector.hpp"
#include "../data_structures/import_edge.hpp"
#include "../data_structures/query_node.hpp"
#include "../data_structures/percent.hpp"
#include "../data_structures/restriction.hpp"
#include "../data_structures/restriction_map.hpp"
#include "../data_structures/turn_instructions.hpp"

#include "../util/integer_range.hpp"
#include "../util/simple_logger.hpp"
#include "../util/std_hash.hpp"
#include "../util/timing_util.hpp"

#include <osrm/coordinate.hpp>

#include <boost/assert.hpp>

#include <tbb/parallel_sort.h>

#include <cstdint>

#include <memory>
#include <stack>
#include <unordered_map>
#include <unordered_set>
#include <vector>

template <typename GraphT> class TarjanSCC
{
    struct TarjanStackFrame
    {
        explicit TarjanStackFrame(NodeID v, NodeID parent) : v(v), parent(parent) {}
        NodeID v;
        NodeID parent;
    };

    struct TarjanNode
    {
        TarjanNode() : index(SPECIAL_NODEID), low_link(SPECIAL_NODEID), on_stack(false) {}
        unsigned index;
        unsigned low_link;
        bool on_stack;
    };

    std::vector<unsigned> components_index;
    std::vector<NodeID> component_size_vector;
    std::shared_ptr<GraphT> m_node_based_graph;
    std::unordered_set<NodeID> barrier_node_set;
    RestrictionMap m_restriction_map;
    std::size_t size_one_counter;

  public:
    template <class ContainerT>
    TarjanSCC(std::shared_ptr<GraphT> graph,
              const RestrictionMap &restrictions,
              const ContainerT &barrier_node_list)
        : components_index(graph->GetNumberOfNodes(), SPECIAL_NODEID), m_node_based_graph(graph),
          m_restriction_map(restrictions), size_one_counter(0)
    {
        barrier_node_set.insert(std::begin(barrier_node_list), std::end(barrier_node_list));
        BOOST_ASSERT(m_node_based_graph->GetNumberOfNodes() > 0);
    }

    void run()
    {
        TIMER_START(SCC_RUN);
        // The following is a hack to distinguish between stuff that happens
        // before the recursive call and stuff that happens after
        std::stack<TarjanStackFrame> recursion_stack;
        // true = stuff before, false = stuff after call
        std::stack<NodeID> tarjan_stack;
        std::vector<TarjanNode> tarjan_node_list(m_node_based_graph->GetNumberOfNodes());
        unsigned component_index = 0, size_of_current_component = 0;
        unsigned index = 0;
        const NodeID last_node = m_node_based_graph->GetNumberOfNodes();
        std::vector<bool> processing_node_before_recursion(m_node_based_graph->GetNumberOfNodes(),
                                                           true);
        for (const NodeID node : osrm::irange(0u, last_node))
        {
            if (SPECIAL_NODEID == components_index[node])
            {
                recursion_stack.emplace(TarjanStackFrame(node, node));
            }

            while (!recursion_stack.empty())
            {
                TarjanStackFrame currentFrame = recursion_stack.top();
                const NodeID u = currentFrame.parent;
                const NodeID v = currentFrame.v;
                recursion_stack.pop();

                const bool before_recursion = processing_node_before_recursion[v];

                if (before_recursion && tarjan_node_list[v].index != UINT_MAX)
                {
                    continue;
                }

                if (before_recursion)
                {
                    // Mark frame to handle tail of recursion
                    recursion_stack.emplace(currentFrame);
                    processing_node_before_recursion[v] = false;

                    // Mark essential information for SCC
                    tarjan_node_list[v].index = index;
                    tarjan_node_list[v].low_link = index;
                    tarjan_stack.push(v);
                    tarjan_node_list[v].on_stack = true;
                    ++index;

                    const NodeID to_node_of_only_restriction =
                        m_restriction_map.CheckForEmanatingIsOnlyTurn(u, v);

                    for (const auto current_edge : m_node_based_graph->GetAdjacentEdgeRange(v))
                    {
                        const auto vprime = m_node_based_graph->GetTarget(current_edge);

                        // Traverse outgoing edges
                        if (barrier_node_set.find(v) != barrier_node_set.end() && u != vprime)
                        {
                            // continue;
                        }

                        if (to_node_of_only_restriction != std::numeric_limits<unsigned>::max() &&
                            vprime == to_node_of_only_restriction)
                        {
                            // At an only_-restriction but not at the right turn
                            // continue;
                        }

                        if (m_restriction_map.CheckIfTurnIsRestricted(u, v, vprime))
                        {
                            // continue;
                        }

                        if (SPECIAL_NODEID == tarjan_node_list[vprime].index)
                        {
                            recursion_stack.emplace(TarjanStackFrame(vprime, v));
                        }
                        else
                        {
                            if (tarjan_node_list[vprime].on_stack &&
                                tarjan_node_list[vprime].index < tarjan_node_list[v].low_link)
                            {
                                tarjan_node_list[v].low_link = tarjan_node_list[vprime].index;
                            }
                        }
                    }
                }
                else
                {
                    processing_node_before_recursion[v] = true;
                    tarjan_node_list[currentFrame.parent].low_link =
                        std::min(tarjan_node_list[currentFrame.parent].low_link,
                                 tarjan_node_list[v].low_link);
                    // after recursion, lets do cycle checking
                    // Check if we found a cycle. This is the bottom part of the recursion
                    if (tarjan_node_list[v].low_link == tarjan_node_list[v].index)
                    {
                        NodeID vprime;
                        do
                        {
                            vprime = tarjan_stack.top();
                            tarjan_stack.pop();
                            tarjan_node_list[vprime].on_stack = false;
                            components_index[vprime] = component_index;
                            ++size_of_current_component;
                        } while (v != vprime);

                        component_size_vector.emplace_back(size_of_current_component);

                        if (size_of_current_component > 1000)
                        {
                            SimpleLogger().Write() << "large component [" << component_index
                                                   << "]=" << size_of_current_component;
                        }

                        ++component_index;
                        size_of_current_component = 0;
                    }
                }
            }
        }

        TIMER_STOP(SCC_RUN);
        SimpleLogger().Write() << "SCC run took: " << TIMER_MSEC(SCC_RUN) / 1000. << "s";

        size_one_counter = std::count_if(component_size_vector.begin(), component_size_vector.end(),
                                         [](unsigned value)
                                         {
                                             return 1 == value;
                                         });
    }

    std::size_t get_number_of_components() const { return component_size_vector.size(); }

    std::size_t get_size_one_count() const { return size_one_counter; }

    unsigned get_component_size(const NodeID node) const
    {
        return component_size_vector[components_index[node]];
    }

    unsigned get_component_id(const NodeID node) const { return components_index[node]; }
};

#endif /* TINY_COMPONENTS_HPP */
<commit_msg>make sure GetNumberOfNodes() is only called once<commit_after>/*

Copyright (c) 2015, Project OSRM contributors
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*/

#ifndef TINY_COMPONENTS_HPP
#define TINY_COMPONENTS_HPP

#include "../typedefs.h"
#include "../data_structures/deallocating_vector.hpp"
#include "../data_structures/import_edge.hpp"
#include "../data_structures/query_node.hpp"
#include "../data_structures/percent.hpp"
#include "../data_structures/restriction.hpp"
#include "../data_structures/restriction_map.hpp"
#include "../data_structures/turn_instructions.hpp"

#include "../util/integer_range.hpp"
#include "../util/simple_logger.hpp"
#include "../util/std_hash.hpp"
#include "../util/timing_util.hpp"

#include <osrm/coordinate.hpp>

#include <boost/assert.hpp>

#include <tbb/parallel_sort.h>

#include <cstdint>

#include <memory>
#include <stack>
#include <unordered_map>
#include <unordered_set>
#include <vector>

template <typename GraphT> class TarjanSCC
{
    struct TarjanStackFrame
    {
        explicit TarjanStackFrame(NodeID v, NodeID parent) : v(v), parent(parent) {}
        NodeID v;
        NodeID parent;
    };

    struct TarjanNode
    {
        TarjanNode() : index(SPECIAL_NODEID), low_link(SPECIAL_NODEID), on_stack(false) {}
        unsigned index;
        unsigned low_link;
        bool on_stack;
    };

    std::vector<unsigned> components_index;
    std::vector<NodeID> component_size_vector;
    std::shared_ptr<GraphT> m_node_based_graph;
    std::unordered_set<NodeID> barrier_node_set;
    RestrictionMap m_restriction_map;
    std::size_t size_one_counter;

  public:
    template <class ContainerT>
    TarjanSCC(std::shared_ptr<GraphT> graph,
              const RestrictionMap &restrictions,
              const ContainerT &barrier_node_list)
        : components_index(graph->GetNumberOfNodes(), SPECIAL_NODEID), m_node_based_graph(graph),
          m_restriction_map(restrictions), size_one_counter(0)
    {
        barrier_node_set.insert(std::begin(barrier_node_list), std::end(barrier_node_list));
        BOOST_ASSERT(m_node_based_graph->GetNumberOfNodes() > 0);
    }

    void run()
    {
        TIMER_START(SCC_RUN);
        const NodeID max_node_id = m_node_based_graph->GetNumberOfNodes();

        // The following is a hack to distinguish between stuff that happens
        // before the recursive call and stuff that happens after
        std::stack<TarjanStackFrame> recursion_stack;
        // true = stuff before, false = stuff after call
        std::stack<NodeID> tarjan_stack;
        std::vector<TarjanNode> tarjan_node_list(max_node_id);
        unsigned component_index = 0, size_of_current_component = 0;
        unsigned index = 0;
        std::vector<bool> processing_node_before_recursion(max_node_id, true);
        for (const NodeID node : osrm::irange(0u, max_node_id))
        {
            if (SPECIAL_NODEID == components_index[node])
            {
                recursion_stack.emplace(TarjanStackFrame(node, node));
            }

            while (!recursion_stack.empty())
            {
                TarjanStackFrame currentFrame = recursion_stack.top();
                const NodeID u = currentFrame.parent;
                const NodeID v = currentFrame.v;
                recursion_stack.pop();

                const bool before_recursion = processing_node_before_recursion[v];

                if (before_recursion && tarjan_node_list[v].index != UINT_MAX)
                {
                    continue;
                }

                if (before_recursion)
                {
                    // Mark frame to handle tail of recursion
                    recursion_stack.emplace(currentFrame);
                    processing_node_before_recursion[v] = false;

                    // Mark essential information for SCC
                    tarjan_node_list[v].index = index;
                    tarjan_node_list[v].low_link = index;
                    tarjan_stack.push(v);
                    tarjan_node_list[v].on_stack = true;
                    ++index;

                    const NodeID to_node_of_only_restriction =
                        m_restriction_map.CheckForEmanatingIsOnlyTurn(u, v);

                    for (const auto current_edge : m_node_based_graph->GetAdjacentEdgeRange(v))
                    {
                        const auto vprime = m_node_based_graph->GetTarget(current_edge);

                        // Traverse outgoing edges
                        if (barrier_node_set.find(v) != barrier_node_set.end() && u != vprime)
                        {
                            // continue;
                        }

                        if (to_node_of_only_restriction != std::numeric_limits<unsigned>::max() &&
                            vprime == to_node_of_only_restriction)
                        {
                            // At an only_-restriction but not at the right turn
                            // continue;
                        }

                        if (m_restriction_map.CheckIfTurnIsRestricted(u, v, vprime))
                        {
                            // continue;
                        }

                        if (SPECIAL_NODEID == tarjan_node_list[vprime].index)
                        {
                            recursion_stack.emplace(TarjanStackFrame(vprime, v));
                        }
                        else
                        {
                            if (tarjan_node_list[vprime].on_stack &&
                                tarjan_node_list[vprime].index < tarjan_node_list[v].low_link)
                            {
                                tarjan_node_list[v].low_link = tarjan_node_list[vprime].index;
                            }
                        }
                    }
                }
                else
                {
                    processing_node_before_recursion[v] = true;
                    tarjan_node_list[currentFrame.parent].low_link =
                        std::min(tarjan_node_list[currentFrame.parent].low_link,
                                 tarjan_node_list[v].low_link);
                    // after recursion, lets do cycle checking
                    // Check if we found a cycle. This is the bottom part of the recursion
                    if (tarjan_node_list[v].low_link == tarjan_node_list[v].index)
                    {
                        NodeID vprime;
                        do
                        {
                            vprime = tarjan_stack.top();
                            tarjan_stack.pop();
                            tarjan_node_list[vprime].on_stack = false;
                            components_index[vprime] = component_index;
                            ++size_of_current_component;
                        } while (v != vprime);

                        component_size_vector.emplace_back(size_of_current_component);

                        if (size_of_current_component > 1000)
                        {
                            SimpleLogger().Write() << "large component [" << component_index
                                                   << "]=" << size_of_current_component;
                        }

                        ++component_index;
                        size_of_current_component = 0;
                    }
                }
            }
        }

        TIMER_STOP(SCC_RUN);
        SimpleLogger().Write() << "SCC run took: " << TIMER_MSEC(SCC_RUN) / 1000. << "s";

        size_one_counter = std::count_if(component_size_vector.begin(), component_size_vector.end(),
                                         [](unsigned value)
                                         {
                                             return 1 == value;
                                         });
    }

    std::size_t get_number_of_components() const { return component_size_vector.size(); }

    std::size_t get_size_one_count() const { return size_one_counter; }

    unsigned get_component_size(const NodeID node) const
    {
        return component_size_vector[components_index[node]];
    }

    unsigned get_component_id(const NodeID node) const { return components_index[node]; }
};

#endif /* TINY_COMPONENTS_HPP */
<|endoftext|>


<commit_before>/// HEADER
#include <csapex.h>

/// PROJECT
#include <csapex/core/csapex_core.h>
#include <csapex/core/drag_io.h>
#include <csapex/core/graphio.h>
#include <csapex/core/settings.h>
#include <csapex/scheduling/thread_pool.h>
#include <csapex/model/graph.h>
#include <csapex/model/graph_worker.h>
#include <csapex/model/node_factory.h>
#include <csapex/model/node_factory.h>
#include <csapex/model/node_worker.h>
#include <csapex/plugin/plugin_locator.h>
#include <csapex/utility/error_handling.h>
#include <csapex/utility/thread.h>
#include <csapex/view/activity_legend.h>
#include <csapex/view/activity_timeline.h>
#include <csapex/view/box.h>
#include <csapex/view/csapex_window.h>
#include <csapex/view/designer.h>
#include <csapex/view/designer_scene.h>
#include <csapex/view/designer_view.h>
#include <csapex/view/minimap_widget.h>
#include <csapex/view/node_adapter_factory.h>
#include <csapex/view/widget_controller.h>
#include <utils_param/parameter_factory.h>

/// SYSTEM
#include <boost/program_options.hpp>
#include <QtGui>
#include <execinfo.h>
#include <stdlib.h>
#include <console_bridge/console.h>

namespace po = boost::program_options;

using namespace csapex;

CsApexApp::CsApexApp(int& argc, char** argv, bool fatal_exceptions)
    : QApplication(argc, argv), fatal_exceptions_(fatal_exceptions)
{}

CsApexCoreApp::CsApexCoreApp(int& argc, char** argv, bool fatal_exceptions)
    : QCoreApplication(argc, argv), fatal_exceptions_(fatal_exceptions)
{}

bool CsApexApp::notify(QObject* receiver, QEvent* event) {
    try {
        return QApplication::notify(receiver, event);

    } catch(const std::exception& e) {
        ErrorState* er = dynamic_cast<ErrorState*> (receiver);
        NodeWorker* bw = dynamic_cast<NodeWorker*> (receiver);

        if(fatal_exceptions_) {
            std::cerr << "caught an exception in --fatal-exceptions mode: Abort!" << std::endl;
            std::abort();
        }

        std::cerr << "exception: " << e.what() << std::endl;

        if(er) {
            er->setError(true, e.what());
        } else if(bw) {
            bw->triggerError(true, e.what());
        } else {
            std::cerr << "Uncatched exception:" << e.what() << std::endl;
        }

        return false;

    } catch(const std::string& s) {
        std::cerr << "Uncatched exception (string) exception: " << s << std::endl;
    } catch(...) {
        std::cerr << "Uncatched exception of unknown type and origin!" << std::endl;
        std::abort();
    }

    return true;
}

bool CsApexCoreApp::notify(QObject* receiver, QEvent* event) {
    try {
        return QCoreApplication::notify(receiver, event);

    } catch(const std::exception& e) {
        ErrorState* er = dynamic_cast<ErrorState*> (receiver);
        NodeWorker* bw = dynamic_cast<NodeWorker*> (receiver);

        if(fatal_exceptions_) {
            std::cerr << "caught an exception in --fatal-exceptions mode: Abort!" << std::endl;
            std::abort();
        }

        std::cerr << "exception: " << e.what() << std::endl;

        if(er) {
            er->setError(true, e.what());
        } else if(bw) {
            bw->triggerError(true, e.what());
        } else {
            std::cerr << "Uncatched exception:" << e.what() << std::endl;
        }

        return false;

    } catch(const std::string& s) {
        std::cerr << "Uncatched exception (string) exception: " << s << std::endl;
    } catch(...) {
        std::cerr << "Uncatched exception of unknown type and origin!" << std::endl;
        std::abort();
    }

    return true;
}

Main::Main(std::unique_ptr<QCoreApplication> &&a)
    : app(std::move(a)), splash(nullptr)
{
    csapex::thread::set_name("cs::APEX main");
}

Main::~Main()
{
    delete splash;
}

int Main::run()
{
    csapex::error_handling::init();

    int result = app->exec();

    return result;
}

int Main::main(bool headless, bool threadless, bool paused, bool thread_grouping, const std::string& config, const std::string& path_to_bin, const std::vector<std::string>& additional_args)
{
//    console_bridge::setLogLevel(console_bridge::CONSOLE_BRIDGE_LOG_DEBUG);
    if(!headless) {
        QPixmap pm(":/apex_splash.png");
        splash = new QSplashScreen (pm);
        splash->show();
        showMessage("loading libraries");
    }

    Settings settings;
    settings.set("config", config);

    if(!settings.knows("path_to_bin")) {
        settings.add(param::ParameterFactory::declareFileInputPath("path_to_bin", path_to_bin));
    } else {
        settings.set("path_to_bin", path_to_bin);
    }

    if(!settings.knows("headless")) {
        settings.add(param::ParameterFactory::declareBool("headless", headless));
    } else {
        settings.set("headless", headless);
    }

    if(!settings.knows("threadless")) {
        settings.add(param::ParameterFactory::declareBool("threadless", threadless));
    } else {
        settings.set("threadless", threadless);
    }

    if(!settings.knows("thread_grouping")) {
        settings.add(param::ParameterFactory::declareBool("thread_grouping", thread_grouping));
    } else {
        settings.set("thread_grouping", thread_grouping);
    }

    if(!settings.knows("additional_args")) {
        settings.add(param::ParameterFactory::declareValue< std::vector<std::string> >("additional_args", additional_args));
    } else {
        settings.set("additional_args", additional_args);
    }

    PluginLocatorPtr plugin_locator =std::make_shared<PluginLocator>(settings);

    GraphPtr graph = std::make_shared<Graph>();
    ThreadPool thread_pool(!threadless, thread_grouping, paused);
    GraphWorkerPtr graph_worker = std::make_shared<GraphWorker>(thread_pool, graph.get());

    graph_worker->generatorAdded.connect([&thread_pool](TaskGeneratorPtr tg) {
        thread_pool.add(tg.get());
    });
    graph_worker->generatorRemoved.connect([&thread_pool](TaskGeneratorPtr tg) {
        thread_pool.remove(tg.get());
    });
    graph_worker->paused.connect([&thread_pool](bool pause) {
        thread_pool.setPause(pause);
    });
    graph_worker->stopped.connect([&thread_pool]() {
        thread_pool.stop();
    });

    NodeFactoryPtr node_factory = std::make_shared<NodeFactory>(settings, plugin_locator.get());
    NodeAdapterFactoryPtr node_adapter_factory = std::make_shared<NodeAdapterFactory>(settings, plugin_locator.get());
    CommandDispatcher dispatcher(settings, graph_worker, graph, &thread_pool, node_factory.get());

    // TODO: core must be destroyed AFTER the factories -> refactor it
    CsApexCorePtr core = std::make_shared<CsApexCore>(settings, plugin_locator,
                                                      graph_worker, graph,
                                                      thread_pool, node_factory.get(), node_adapter_factory.get(), &dispatcher);

    core->saveSettingsRequest.connect([&thread_pool](YAML::Node& n){ thread_pool.saveSettings(n); });
    core->loadSettingsRequest.connect([&thread_pool](YAML::Node& n){ thread_pool.loadSettings(n); });

    int res;
    if(!headless) {
        app->processEvents();

        app->connect(app.get(), SIGNAL(lastWindowClosed()), app.get(), SLOT(quit()));

        WidgetControllerPtr widget_control = std::make_shared<WidgetController>(settings, dispatcher, graph_worker, node_factory.get(), node_adapter_factory.get());
        DragIO drag_io(graph.get(), &dispatcher, widget_control);

        DesignerStyleable style;
        DesignerScene* scene = new DesignerScene(graph, &dispatcher, widget_control, &style);
        DesignerView* view = new DesignerView(scene, graph, settings, thread_pool, &dispatcher, widget_control, drag_io, &style);
        MinimapWidget* minimap = new MinimapWidget(view, scene);

        Designer* designer = new Designer(settings, graph, &dispatcher, widget_control, view, scene, minimap);

        ActivityLegend* legend = new ActivityLegend;
        ActivityTimeline* timeline = new ActivityTimeline;

        QObject::connect(legend, SIGNAL(nodeSelectionChanged(QList<NodeWorker*>)), timeline, SLOT(setSelection(QList<NodeWorker*>)));

        graph->nodeAdded.connect([legend](NodeWorkerPtr n) { legend->startTrackingNode(n); });
        graph->nodeRemoved.connect([legend](NodeWorkerPtr n) { legend->stopTrackingNode(n); });

        QObject::connect(legend, SIGNAL(nodeAdded(NodeWorker*)), timeline, SLOT(addNode(NodeWorker*)));
        QObject::connect(legend, SIGNAL(nodeRemoved(NodeWorker*)), timeline, SLOT(removeNode(NodeWorker*)));

        widget_control->setDesigner(designer);

        CsApexWindow w(*core, &dispatcher, widget_control,
                       graph_worker, graph,
                       thread_pool, designer, minimap, legend, timeline, plugin_locator.get());
        QObject::connect(&w, SIGNAL(statusChanged(QString)), this, SLOT(showMessage(QString)));

        csapex::error_handling::stop_request().connect(std::bind(&CsApexWindow::close, &w));

        core->init(&drag_io);
        w.start();
        core->startup();

        w.show();
        splash->finish(&w);

        res = run();

        delete designer;

    } else {
        core->init(nullptr);
        csapex::error_handling::stop_request().connect(std::bind(&csapex::error_handling::kill));
        core->startup();
        res = run();
    }

    node_factory.reset();
    node_adapter_factory.reset();

    return res;
}

void Main::showMessage(const QString& msg)
{
    if(splash->isVisible()) {
        splash->showMessage(msg, Qt::AlignTop | Qt::AlignRight, Qt::black);
    }
    app->processEvents();
}

int main(int argc, char** argv)
{
//    console_bridge::setLogLevel(console_bridge::CONSOLE_BRIDGE_LOG_DEBUG);

    std::string path_to_bin(argv[0]);

    po::options_description desc("Allowed options");
    desc.add_options()
            ("help", "show help message")
            ("dump", "show variables")
            ("paused", "start paused")
            ("headless", "run without gui")
            ("threadless", "run without threading")
            ("fatal_exceptions", "abort execution on exception")
            ("thread_grouping", "create one thread per graph component")
            ("input", "config file to load")
            ;

    po::positional_options_description p;
    p.add("input", 1);

    // first check for --headless or --fatal_exceptions parameter
    // this has to be done before the qapp can be created, which
    // has to be done before parameters can be read.
    bool headless = false;
    bool fatal_exceptions = false;
    for(int i = 1; i < argc; ++i) {
        std::string arg(argv[i]);
        if(arg == "--headless") {
            headless = true;
        } else if(arg == "--fatal_exceptions") {
            fatal_exceptions = true;
        }
    }

    if(!headless) {
        // if headless not requested, check if there is a display
        // if not, we enforce headless mode
        if(!getenv("DISPLAY")) {
            headless = true;
            std::cout << "warning: enforcing headless mode because there is no display detected" << std::endl;
        }
    }

    // filters all qt parameters from argv
    std::unique_ptr<QCoreApplication> app;
    if(headless) {
        app.reset(new CsApexCoreApp(argc, argv, fatal_exceptions));
    } else {
        app.reset(new CsApexApp(argc, argv, fatal_exceptions));
    }

    // filters ros remappings
    std::vector<std::string> remapping_args;
    std::vector<std::string> rest_args;
    for(int i = 1; i < argc; ++i) {
        std::string arg(argv[i]);
        if(arg.find(":=") != std::string::npos)  {
            remapping_args.push_back(arg);
        } else {
            rest_args.push_back(arg);
        }
    }

    // now check for remaining parameters
    po::variables_map vm;
    std::vector<std::string> additional_args;

    try {
        po::parsed_options parsed = po::command_line_parser(rest_args).options(desc).positional(p).run();

        po::store(parsed, vm);

        po::notify(vm);

        additional_args = po::collect_unrecognized(parsed.options, po::include_positional);

    } catch(const std::exception& e) {
        std::cerr << "cannot parse parameters: " << e.what() << std::endl;
        return 4;
    }

    // add ros remappings
    additional_args.insert(additional_args.end(), remapping_args.begin(), remapping_args.end());

    // display help?
    if(vm.count("help")) {
        std::cerr << desc << std::endl;
        return 1;
    }
    if(vm.count("dump")) {
        std::cout << "to be passed on:\n";
        for(std::size_t i = 0; i < additional_args.size(); ++i) {
            std::cout << additional_args[i] << "\n";
        }
        std::cout << std::flush;
        return 0;
    }

    // which file to use?
    std::string input;
    if (vm.count("input")) {
        input = vm["input"].as<std::string>();
    } else {
        input = Settings::default_config;
    }

    bool threadless = vm.count("threadless");
    bool thread_grouping = vm.count("thread_grouping");
    bool paused = vm.count("paused");

    // start the app
    Main m(std::move(app));
    return m.main(headless, threadless, paused, thread_grouping, input, path_to_bin, additional_args);
}

/// MOC
#include "moc_csapex.cpp"
<commit_msg>quick fix<commit_after>/// HEADER
#include <csapex.h>

/// PROJECT
#include <csapex/core/csapex_core.h>
#include <csapex/core/drag_io.h>
#include <csapex/core/graphio.h>
#include <csapex/core/settings.h>
#include <csapex/scheduling/thread_pool.h>
#include <csapex/model/graph.h>
#include <csapex/model/graph_worker.h>
#include <csapex/model/node_factory.h>
#include <csapex/model/node_factory.h>
#include <csapex/model/node_worker.h>
#include <csapex/plugin/plugin_locator.h>
#include <csapex/utility/error_handling.h>
#include <csapex/utility/thread.h>
#include <csapex/view/activity_legend.h>
#include <csapex/view/activity_timeline.h>
#include <csapex/view/box.h>
#include <csapex/view/csapex_window.h>
#include <csapex/view/designer.h>
#include <csapex/view/designer_scene.h>
#include <csapex/view/designer_view.h>
#include <csapex/view/minimap_widget.h>
#include <csapex/view/node_adapter_factory.h>
#include <csapex/view/widget_controller.h>
#include <utils_param/parameter_factory.h>

/// SYSTEM
#include <boost/program_options.hpp>
#include <QtGui>
#include <execinfo.h>
#include <stdlib.h>
#include <console_bridge/console.h>

namespace po = boost::program_options;

using namespace csapex;

CsApexApp::CsApexApp(int& argc, char** argv, bool fatal_exceptions)
    : QApplication(argc, argv), fatal_exceptions_(fatal_exceptions)
{}

CsApexCoreApp::CsApexCoreApp(int& argc, char** argv, bool fatal_exceptions)
    : QCoreApplication(argc, argv), fatal_exceptions_(fatal_exceptions)
{}

bool CsApexApp::notify(QObject* receiver, QEvent* event) {
    try {
        return QApplication::notify(receiver, event);

    } catch(const std::exception& e) {
        ErrorState* er = dynamic_cast<ErrorState*> (receiver);
        NodeWorker* bw = dynamic_cast<NodeWorker*> (receiver);

        if(fatal_exceptions_) {
            std::cerr << "caught an exception in --fatal-exceptions mode: Abort!" << std::endl;
            std::abort();
        }

        std::cerr << "exception: " << e.what() << std::endl;

        if(er) {
            er->setError(true, e.what());
        } else if(bw) {
            bw->triggerError(true, e.what());
        } else {
            std::cerr << "Uncatched exception:" << e.what() << std::endl;
        }

        return false;

    } catch(const std::string& s) {
        std::cerr << "Uncatched exception (string) exception: " << s << std::endl;
    } catch(...) {
        std::cerr << "Uncatched exception of unknown type and origin!" << std::endl;
        std::abort();
    }

    return true;
}

bool CsApexCoreApp::notify(QObject* receiver, QEvent* event) {
    try {
        return QCoreApplication::notify(receiver, event);

    } catch(const std::exception& e) {
        ErrorState* er = dynamic_cast<ErrorState*> (receiver);
        NodeWorker* bw = dynamic_cast<NodeWorker*> (receiver);

        if(fatal_exceptions_) {
            std::cerr << "caught an exception in --fatal-exceptions mode: Abort!" << std::endl;
            std::abort();
        }

        std::cerr << "exception: " << e.what() << std::endl;

        if(er) {
            er->setError(true, e.what());
        } else if(bw) {
            bw->triggerError(true, e.what());
        } else {
            std::cerr << "Uncatched exception:" << e.what() << std::endl;
        }

        return false;

    } catch(const std::string& s) {
        std::cerr << "Uncatched exception (string) exception: " << s << std::endl;
    } catch(...) {
        std::cerr << "Uncatched exception of unknown type and origin!" << std::endl;
        std::abort();
    }

    return true;
}

Main::Main(std::unique_ptr<QCoreApplication> &&a)
    : app(std::move(a)), splash(nullptr)
{
    csapex::thread::set_name("cs::APEX main");
}

Main::~Main()
{
    delete splash;
}

int Main::run()
{
    csapex::error_handling::init();

    int result = app->exec();

    return result;
}

int Main::main(bool headless, bool threadless, bool paused, bool thread_grouping, const std::string& config, const std::string& path_to_bin, const std::vector<std::string>& additional_args)
{
//    console_bridge::setLogLevel(console_bridge::CONSOLE_BRIDGE_LOG_DEBUG);
    if(!headless) {
        QPixmap pm(":/apex_splash.png");
        splash = new QSplashScreen (pm);
        splash->show();
        showMessage("loading libraries");
    }

    Settings settings;
    settings.set("config", config);

    if(!settings.knows("path_to_bin")) {
        settings.add(param::ParameterFactory::declareFileInputPath("path_to_bin", path_to_bin));
    } else {
        settings.set("path_to_bin", path_to_bin);
    }

    if(!settings.knows("headless")) {
        settings.add(param::ParameterFactory::declareBool("headless", headless));
    } else {
        settings.set("headless", headless);
    }

    if(!settings.knows("threadless")) {
        settings.add(param::ParameterFactory::declareBool("threadless", threadless));
    } else {
        settings.set("threadless", threadless);
    }

    if(!settings.knows("thread_grouping")) {
        settings.add(param::ParameterFactory::declareBool("thread_grouping", thread_grouping));
    } else {
        settings.set("thread_grouping", thread_grouping);
    }

    if(!settings.knows("additional_args")) {
        settings.add(param::ParameterFactory::declareValue< std::vector<std::string> >("additional_args", additional_args));
    } else {
        settings.set("additional_args", additional_args);
    }

    PluginLocatorPtr plugin_locator =std::make_shared<PluginLocator>(settings);

    GraphPtr graph = std::make_shared<Graph>();
    ThreadPool thread_pool(!threadless, thread_grouping, paused);
    GraphWorkerPtr graph_worker = std::make_shared<GraphWorker>(thread_pool, graph.get());

    graph_worker->generatorAdded.connect([&thread_pool](TaskGeneratorPtr tg) {
        thread_pool.add(tg.get());
    });
    graph_worker->generatorRemoved.connect([&thread_pool](TaskGeneratorPtr tg) {
        thread_pool.remove(tg.get());
    });
    graph_worker->paused.connect([&thread_pool](bool pause) {
        thread_pool.setPause(pause);
    });
    graph_worker->stopped.connect([&thread_pool]() {
        thread_pool.stop();
    });

    NodeFactoryPtr node_factory = std::make_shared<NodeFactory>(settings, plugin_locator.get());
    NodeAdapterFactoryPtr node_adapter_factory = std::make_shared<NodeAdapterFactory>(settings, plugin_locator.get());
    CommandDispatcher dispatcher(settings, graph_worker, graph, &thread_pool, node_factory.get());

    // TODO: core must be destroyed AFTER the factories -> refactor it
    CsApexCorePtr core = std::make_shared<CsApexCore>(settings, plugin_locator,
                                                      graph_worker, graph,
                                                      thread_pool, node_factory.get(), node_adapter_factory.get(), &dispatcher);

    core->saveSettingsRequest.connect([&thread_pool](YAML::Node& n){ thread_pool.saveSettings(n); });
    core->loadSettingsRequest.connect([&thread_pool](YAML::Node& n){ thread_pool.loadSettings(n); });

    int res;
    if(!headless) {
        app->processEvents();

        app->connect(app.get(), SIGNAL(lastWindowClosed()), app.get(), SLOT(quit()));

        WidgetControllerPtr widget_control = std::make_shared<WidgetController>(settings, dispatcher, graph_worker, node_factory.get(), node_adapter_factory.get());
        DragIO drag_io(graph.get(), &dispatcher, widget_control);

        DesignerStyleable style;
        DesignerScene* scene = new DesignerScene(graph, &dispatcher, widget_control, &style);
        DesignerView* view = new DesignerView(scene, graph, settings, thread_pool, &dispatcher, widget_control, drag_io, &style);
        MinimapWidget* minimap = new MinimapWidget(view, scene);

        Designer* designer = new Designer(settings, graph, &dispatcher, widget_control, view, scene, minimap);

        ActivityLegend* legend = new ActivityLegend;
        ActivityTimeline* timeline = new ActivityTimeline;

        QObject::connect(legend, SIGNAL(nodeSelectionChanged(QList<NodeWorker*>)), timeline, SLOT(setSelection(QList<NodeWorker*>)));

        graph->nodeAdded.connect([legend](NodeWorkerPtr n) { legend->startTrackingNode(n); });
        graph->nodeRemoved.connect([legend](NodeWorkerPtr n) { legend->stopTrackingNode(n); });

        QObject::connect(legend, SIGNAL(nodeAdded(NodeWorker*)), timeline, SLOT(addNode(NodeWorker*)));
        QObject::connect(legend, SIGNAL(nodeRemoved(NodeWorker*)), timeline, SLOT(removeNode(NodeWorker*)));

        widget_control->setDesigner(designer);

        CsApexWindow w(*core, &dispatcher, widget_control,
                       graph_worker, graph,
                       thread_pool, designer, minimap, legend, timeline, plugin_locator.get());
        QObject::connect(&w, SIGNAL(statusChanged(QString)), this, SLOT(showMessage(QString)));

        csapex::error_handling::stop_request().connect(std::bind(&CsApexWindow::close, &w));

        core->init(&drag_io);
        w.start();
        core->startup();

        w.show();
        splash->finish(&w);

        res = run();

        // FIXME: this is necessary for some adapted nodes.
        graph->reset();
        delete designer;

    } else {
        core->init(nullptr);
        csapex::error_handling::stop_request().connect(std::bind(&csapex::error_handling::kill));
        core->startup();
        res = run();
    }

    node_factory.reset();
    node_adapter_factory.reset();

    return res;
}

void Main::showMessage(const QString& msg)
{
    if(splash->isVisible()) {
        splash->showMessage(msg, Qt::AlignTop | Qt::AlignRight, Qt::black);
    }
    app->processEvents();
}

int main(int argc, char** argv)
{
//    console_bridge::setLogLevel(console_bridge::CONSOLE_BRIDGE_LOG_DEBUG);

    std::string path_to_bin(argv[0]);

    po::options_description desc("Allowed options");
    desc.add_options()
            ("help", "show help message")
            ("dump", "show variables")
            ("paused", "start paused")
            ("headless", "run without gui")
            ("threadless", "run without threading")
            ("fatal_exceptions", "abort execution on exception")
            ("thread_grouping", "create one thread per graph component")
            ("input", "config file to load")
            ;

    po::positional_options_description p;
    p.add("input", 1);

    // first check for --headless or --fatal_exceptions parameter
    // this has to be done before the qapp can be created, which
    // has to be done before parameters can be read.
    bool headless = false;
    bool fatal_exceptions = false;
    for(int i = 1; i < argc; ++i) {
        std::string arg(argv[i]);
        if(arg == "--headless") {
            headless = true;
        } else if(arg == "--fatal_exceptions") {
            fatal_exceptions = true;
        }
    }

    if(!headless) {
        // if headless not requested, check if there is a display
        // if not, we enforce headless mode
        if(!getenv("DISPLAY")) {
            headless = true;
            std::cout << "warning: enforcing headless mode because there is no display detected" << std::endl;
        }
    }

    // filters all qt parameters from argv
    std::unique_ptr<QCoreApplication> app;
    if(headless) {
        app.reset(new CsApexCoreApp(argc, argv, fatal_exceptions));
    } else {
        app.reset(new CsApexApp(argc, argv, fatal_exceptions));
    }

    // filters ros remappings
    std::vector<std::string> remapping_args;
    std::vector<std::string> rest_args;
    for(int i = 1; i < argc; ++i) {
        std::string arg(argv[i]);
        if(arg.find(":=") != std::string::npos)  {
            remapping_args.push_back(arg);
        } else {
            rest_args.push_back(arg);
        }
    }

    // now check for remaining parameters
    po::variables_map vm;
    std::vector<std::string> additional_args;

    try {
        po::parsed_options parsed = po::command_line_parser(rest_args).options(desc).positional(p).run();

        po::store(parsed, vm);

        po::notify(vm);

        additional_args = po::collect_unrecognized(parsed.options, po::include_positional);

    } catch(const std::exception& e) {
        std::cerr << "cannot parse parameters: " << e.what() << std::endl;
        return 4;
    }

    // add ros remappings
    additional_args.insert(additional_args.end(), remapping_args.begin(), remapping_args.end());

    // display help?
    if(vm.count("help")) {
        std::cerr << desc << std::endl;
        return 1;
    }
    if(vm.count("dump")) {
        std::cout << "to be passed on:\n";
        for(std::size_t i = 0; i < additional_args.size(); ++i) {
            std::cout << additional_args[i] << "\n";
        }
        std::cout << std::flush;
        return 0;
    }

    // which file to use?
    std::string input;
    if (vm.count("input")) {
        input = vm["input"].as<std::string>();
    } else {
        input = Settings::default_config;
    }

    bool threadless = vm.count("threadless");
    bool thread_grouping = vm.count("thread_grouping");
    bool paused = vm.count("paused");

    // start the app
    Main m(std::move(app));
    return m.main(headless, threadless, paused, thread_grouping, input, path_to_bin, additional_args);
}

/// MOC
#include "moc_csapex.cpp"
<|endoftext|>