<commit_before>/*****************************************************************************
 * Media Library
 *****************************************************************************
 * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN
 *
 * Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
 *
 * This program 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.
 *
 * This program 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 this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#if HAVE_CONFIG_H
# include "config.h"
#endif

#include "UnitTests.h"

#include "Media.h"
#include "File.h"
#include "common/NoopCallback.h"
#include "utils/ModificationsNotifier.h"
#include "compat/Mutex.h"

class MockCallback : public mock::NoopCallback
{
public:
    MockCallback() : m_nbMedia( 0 ), m_nbTotalMedia( 0 ) {}
private:
    virtual void onMediaDeleted( std::set<int64_t> batch ) override
    {
        std::lock_guard<compat::Mutex> lock( m_lock );
        m_nbMedia = batch.size();
        m_nbTotalMedia += batch.size();
        m_cond.notify_all();
    }

public:
    void resetCount()
    {
        m_nbMedia = 0;
        m_nbTotalMedia = 0;
    }

    std::unique_lock<compat::Mutex> prepareWait()
    {
        std::unique_lock<compat::Mutex> lock{ m_lock };
        resetCount();
        return lock;
    }

    unsigned int waitForNotif( std::unique_lock<compat::Mutex>& preparedLock,
                               std::chrono::duration<int64_t> timeout, bool& hasTimedout )
    {
        hasTimedout = !m_cond.wait_for( preparedLock, timeout, [this]() {
            return m_nbMedia != 0;
        });

        return m_nbMedia;
    }

    uint32_t getNbTotalMediaDeleted()
    {
        std::lock_guard<compat::Mutex> lock( m_lock );
        return m_nbTotalMedia;
    }

private:
    compat::Mutex m_lock;
    compat::ConditionVariable m_cond;
    uint32_t m_nbMedia;
    uint32_t m_nbTotalMedia;
};

struct RemovalNotifierTests : public UnitTests<MockCallback>
{
    virtual void InstantiateMediaLibrary( const std::string& dbPath,
                                          const std::string& mlFolderDir ) override
    {
        ml.reset( new MediaLibraryWithNotifier ( dbPath, mlFolderDir ) );
    }
};

static void DeleteOne( RemovalNotifierTests* T )
{
    auto m = std::static_pointer_cast<Media>( T->ml->addMedia( "media.avi", IMedia::Type::Video ) );
    auto lock = T->cbMock->prepareWait();
    bool hasTimedout;
    m->removeFile( static_cast<File&>( *m->files()[0] ) );
    // This media doesn't have any associated files, and should be removed by a sqlite hook
    // The notification will arrive "late", as it will need to timeout first
    auto res = T->cbMock->waitForNotif( lock,
        std::chrono::duration_cast<std::chrono::seconds>( std::chrono::milliseconds{ 1500 } ),
        hasTimedout );
    ASSERT_FALSE( hasTimedout );
    ASSERT_EQ( 1u, res );

    // Re-run a notification after the queues have been used before
    T->cbMock->resetCount();

    m = std::static_pointer_cast<Media>( T->ml->addMedia( "media.avi", IMedia::Type::Video ) );
    m->removeFile( static_cast<File&>( *m->files()[0] ) );

    // Wait for a notification for 500ms. It shouldn't arrive, and we should timeout
    res = T->cbMock->waitForNotif( lock,
        std::chrono::duration_cast<std::chrono::seconds>( std::chrono::milliseconds{ 500 } ),
        hasTimedout );
    ASSERT_TRUE( hasTimedout );
    ASSERT_EQ( 0u, res );

    // Wait again, now it should arrive.
    res = T->cbMock->waitForNotif( lock,
        std::chrono::duration_cast<std::chrono::seconds>( std::chrono::milliseconds{ 1000 } ),
        hasTimedout );
    ASSERT_FALSE( hasTimedout );
    ASSERT_EQ( 1u, res );
}

static void DeleteBatch( RemovalNotifierTests* T )
{
    for ( auto i = 0u; i < 10; ++i )
        T->ml->addMedia( std::string{ "media" } + std::to_string( i ) + ".mkv", IMedia::Type::Video );

    auto lock = T->cbMock->prepareWait();
    bool hasTimedout;

    for ( auto i = 0u; i < 10; ++i )
        T->ml->deleteMedia( i + 1 );

    // This media doesn't have any associated files, and should be removed by a sqlite hook
    // The notification will arrive "late", as it will need to timeout first
    uint32_t nbTotalNotified = 0;
    while ( nbTotalNotified != 10 )
    {
        auto nbNotified = T->cbMock->waitForNotif( lock,
            std::chrono::duration_cast<std::chrono::seconds>( std::chrono::milliseconds{ 1500 } ),
            hasTimedout );
        ASSERT_NE( 0u, nbNotified );
        ASSERT_FALSE( hasTimedout );
        ASSERT_TRUE( nbNotified != 1 || nbNotified + nbTotalNotified == 10 );
        nbTotalNotified += nbNotified;
        T->cbMock->resetCount();
    }
    ASSERT_EQ( 10u, nbTotalNotified );
}

static void Flush( RemovalNotifierTests* T )
{
    for ( auto i = 0u; i < 10; ++i )
        T->ml->addMedia( std::string{ "media" } + std::to_string( i ) + ".mkv", IMedia::Type::Video );

    for ( auto i = 0u; i < 10; ++i )
        T->ml->deleteMedia( i + 1 );

    // We can't lock here since flush is blocking until the callbacks have been called
    T->ml->getNotifier()->flush();
    auto nbMediaSignaled = T->cbMock->getNbTotalMediaDeleted();
    ASSERT_EQ( 10u, nbMediaSignaled );
}

int main( int ac, char** av )
{
    INIT_TESTS_C( RemovalNotifierTests );

    ADD_TEST( DeleteOne );
    ADD_TEST( DeleteBatch );
    ADD_TEST( Flush );

    END_TESTS
}
<commit_msg>test: RemovalNotifier: Increase timeout<commit_after>/*****************************************************************************
 * Media Library
 *****************************************************************************
 * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN
 *
 * Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
 *
 * This program 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.
 *
 * This program 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 this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#if HAVE_CONFIG_H
# include "config.h"
#endif

#include "UnitTests.h"

#include "Media.h"
#include "File.h"
#include "common/NoopCallback.h"
#include "utils/ModificationsNotifier.h"
#include "compat/Mutex.h"

class MockCallback : public mock::NoopCallback
{
public:
    MockCallback() : m_nbMedia( 0 ), m_nbTotalMedia( 0 ) {}
private:
    virtual void onMediaDeleted( std::set<int64_t> batch ) override
    {
        std::lock_guard<compat::Mutex> lock( m_lock );
        m_nbMedia = batch.size();
        m_nbTotalMedia += batch.size();
        m_cond.notify_all();
    }

public:
    void resetCount()
    {
        m_nbMedia = 0;
        m_nbTotalMedia = 0;
    }

    std::unique_lock<compat::Mutex> prepareWait()
    {
        std::unique_lock<compat::Mutex> lock{ m_lock };
        resetCount();
        return lock;
    }

    unsigned int waitForNotif( std::unique_lock<compat::Mutex>& preparedLock,
                               std::chrono::duration<int64_t> timeout, bool& hasTimedout )
    {
        hasTimedout = !m_cond.wait_for( preparedLock, timeout, [this]() {
            return m_nbMedia != 0;
        });

        return m_nbMedia;
    }

    uint32_t getNbTotalMediaDeleted()
    {
        std::lock_guard<compat::Mutex> lock( m_lock );
        return m_nbTotalMedia;
    }

private:
    compat::Mutex m_lock;
    compat::ConditionVariable m_cond;
    uint32_t m_nbMedia;
    uint32_t m_nbTotalMedia;
};

struct RemovalNotifierTests : public UnitTests<MockCallback>
{
    virtual void InstantiateMediaLibrary( const std::string& dbPath,
                                          const std::string& mlFolderDir ) override
    {
        ml.reset( new MediaLibraryWithNotifier ( dbPath, mlFolderDir ) );
    }
};

static void DeleteOne( RemovalNotifierTests* T )
{
    auto m = std::static_pointer_cast<Media>( T->ml->addMedia( "media.avi", IMedia::Type::Video ) );
    auto lock = T->cbMock->prepareWait();
    bool hasTimedout;
    m->removeFile( static_cast<File&>( *m->files()[0] ) );
    // This media doesn't have any associated files, and should be removed by a sqlite hook
    // The notification will arrive "late", as it will need to timeout first
    auto res = T->cbMock->waitForNotif( lock,
        std::chrono::duration_cast<std::chrono::seconds>( std::chrono::milliseconds{ 2000 } ),
        hasTimedout );
    ASSERT_FALSE( hasTimedout );
    ASSERT_EQ( 1u, res );

    // Re-run a notification after the queues have been used before
    T->cbMock->resetCount();

    m = std::static_pointer_cast<Media>( T->ml->addMedia( "media.avi", IMedia::Type::Video ) );
    m->removeFile( static_cast<File&>( *m->files()[0] ) );

    // Wait for a notification for 500ms. It shouldn't arrive, and we should timeout
    res = T->cbMock->waitForNotif( lock,
        std::chrono::duration_cast<std::chrono::seconds>( std::chrono::milliseconds{ 500 } ),
        hasTimedout );
    ASSERT_TRUE( hasTimedout );
    ASSERT_EQ( 0u, res );

    // Wait again, now it should arrive.
    res = T->cbMock->waitForNotif( lock,
        std::chrono::duration_cast<std::chrono::seconds>( std::chrono::milliseconds{ 2000 } ),
        hasTimedout );
    ASSERT_FALSE( hasTimedout );
    ASSERT_EQ( 1u, res );
}

static void DeleteBatch( RemovalNotifierTests* T )
{
    for ( auto i = 0u; i < 10; ++i )
        T->ml->addMedia( std::string{ "media" } + std::to_string( i ) + ".mkv", IMedia::Type::Video );

    auto lock = T->cbMock->prepareWait();
    bool hasTimedout;

    for ( auto i = 0u; i < 10; ++i )
        T->ml->deleteMedia( i + 1 );

    // This media doesn't have any associated files, and should be removed by a sqlite hook
    // The notification will arrive "late", as it will need to timeout first
    uint32_t nbTotalNotified = 0;
    while ( nbTotalNotified != 10 )
    {
        auto nbNotified = T->cbMock->waitForNotif( lock,
            std::chrono::duration_cast<std::chrono::seconds>( std::chrono::milliseconds{ 2000 } ),
            hasTimedout );
        ASSERT_NE( 0u, nbNotified );
        ASSERT_FALSE( hasTimedout );
        ASSERT_TRUE( nbNotified != 1 || nbNotified + nbTotalNotified == 10 );
        nbTotalNotified += nbNotified;
        T->cbMock->resetCount();
    }
    ASSERT_EQ( 10u, nbTotalNotified );
}

static void Flush( RemovalNotifierTests* T )
{
    for ( auto i = 0u; i < 10; ++i )
        T->ml->addMedia( std::string{ "media" } + std::to_string( i ) + ".mkv", IMedia::Type::Video );

    for ( auto i = 0u; i < 10; ++i )
        T->ml->deleteMedia( i + 1 );

    // We can't lock here since flush is blocking until the callbacks have been called
    T->ml->getNotifier()->flush();
    auto nbMediaSignaled = T->cbMock->getNbTotalMediaDeleted();
    ASSERT_EQ( 10u, nbMediaSignaled );
}

int main( int ac, char** av )
{
    INIT_TESTS_C( RemovalNotifierTests );

    ADD_TEST( DeleteOne );
    ADD_TEST( DeleteBatch );
    ADD_TEST( Flush );

    END_TESTS
}
<|endoftext|>


<commit_before>#include <QQmlApplicationEngine>
#include <QTest>
#include <Automator>
#include <QtConcurrent>
#include <asyncfuture.h>
#include <QTimer>
#include "example.h"
#include "testfunctions.h"
#include "asyncfutureutils.h"

using namespace AsyncFuture;
using namespace AsyncFutureUtils;
using namespace Test;

static QString delayedFakeRead(const QString& fileName) {
    Q_UNUSED(fileName);

    Automator::wait(100);
    return "";
}

Example::Example(QObject *parent) : QObject(parent)
{

    // This function do nothing but could make Qt Creator Autotests plugin recognize this test
    auto ref =[=]() {
        QTest::qExec(this, 0, 0);
    };
    Q_UNUSED(ref);

}

/*
 * 1. Convert a signal from QObject into a QFuture object
 * https://github.com/benlau/asyncfuture
 *
 */

void Example::example_Timer_timeout()
{
    QTimer *timer = new QTimer(this);
    timer->setInterval(50);

    // Convert a signal from QObject into QFuture
    QFuture<void> future = observe(timer,
                                   &QTimer::timeout).future();

    // Listen from the future without using QFutureWatcher<T>
    observe(future).subscribe([]() {
        // onCompleted. It is invoked when the observed future is finished successfully
        qDebug() << "onCompleted";
    },[]() {
        // onCanceled
        qDebug() << "onCancel";
    });

    // It is chainable. Listen from a timeout signal only once

    observe(timer, &QTimer::timeout).subscribe([=]() { /*…*/ });

    timer->start();
    waitUntil(future);
}

/*
 *
 * 2. Combine multiple futures with different type into a single future object
 * https://github.com/benlau/asyncfuture
 *
 */

void Example::example_combine_multiple_future()
{
    auto readImage = [](QString) -> QImage {
        // Not a real image reader
        Automator::wait(50);
        return QImage();
    };

    QTimer *timer = new QTimer(this);
    timer->setInterval(80);

    // Combine multiple futures with different type into a single future
    QFuture<QImage> f1 = QtConcurrent::run(readImage, QString("image.jpg"));

    QFuture<void> f2 = observe(timer, &QTimer::timeout).future();

    QFuture<QImage> result = (combine() << f1 << f2).subscribe([=](){
        // Read an image but do not return before timeout
        return f1.result();
    }).future();

    timer->start();

    QVERIFY(waitUntil(result, 1000));

    timer->deleteLater();
}

void Example::example_worker_context()
{

    auto readImage = [](QString) -> QImage {
        // Not a real image reader
        Automator::wait(50);
        return QImage();
    };

    auto validator = [](QImage image) {
        return image.isNull();
    };

    QObject* contextObject = new QObject(this);

    // Start a thread and process its result in main thread

    QFuture<QImage> reading = QtConcurrent::run(readImage, QString("image.jpg"));

    QFuture<bool> validating = observe(reading).context(contextObject, validator).future();

    // Read image by a thread, when it is ready, run the validator function
    // in the thread of the contextObject(e.g main thread)
    // And it return another QFuture to represent the final result.

    QVERIFY(waitUntil(validating, 1000));

    QCOMPARE(validating.result(), true);

}

static int mapFunc(int value) {
    return value * value;
}

void Example::example_context_return_future()
{

    auto reducerFunc = [](QList<int> input ) -> int {
        return input.size();
    };

    QList<int> input;
    for (int i = 0 ; i < 10;i++) {
        input << i;
    }

    QObject* contextObject = this;

    QFuture<int> f1 = QtConcurrent::mapped(input, mapFunc);

    // You may use QFuture as the input argument of your callback function
    // It will be set to the observed future object. So that you may obtain
    // the value of results()
    QFuture<int> f2 = observe(f1).context(contextObject, [=](QFuture<int> future) {
        qDebug() << future.results();

        // Return another QFuture is possible.
        return QtConcurrent::run(reducerFunc, future.results());
    }).future();

    // f2 is constructed before the QtConcurrent::run statement
    // But its value is equal to the result of reducerFunc

    waitUntil(f2);

    QCOMPARE(f2.result(), 10);
}

void Example::example_promise_like()
{
    // Complete / cancel a future on your own choice
    auto d = deferred<bool>();

    observe(d.future()).subscribe([]() {
        qDebug() << "onCompleted";
    }, []() {
        qDebug() << "onCancel";
    });

    d.complete(true);
    d.cancel();

    QCOMPARE(d.future().isFinished(), true);
    QCOMPARE(d.future().isCanceled(), false);

    QVERIFY(waitUntil(d.future(), 1000));
}

void Example::example_promise_like_complete_future()
{
    // Complete / cancel a future according to another future object.
    auto timeout = []() -> void {
        Automator::wait(50);
    };

    auto d = deferred<void>();

    d.complete(QtConcurrent::run(timeout));

    QCOMPARE(d.future().isFinished(), false);
    QCOMPARE(d.future().isCanceled(), false);

    QVERIFY(waitUntil(d.future(), 1000));
}

void Example::example_promise_like_timeout()
{
    QTimer* timer = new QTimer(this);
    timer->setInterval(50);

    auto readFileworker = [](QString fileName) -> QString {
        Q_UNUSED(fileName);
        Automator::wait(1000);
        return "";
    };

    auto read = [=](const QString &fileName) {
        timer->start();

        auto timeout = observe(timer, &QTimer::timeout).future();

        auto defer = deferred<QString>();

        defer.complete(QtConcurrent::run(readFileworker, fileName));
        defer.cancel(timeout);

        return defer.future();
    };

    auto future = read("input.txt");

    bool canceldCalled = false;

    observe(future).subscribe([](QString content) {
        Q_UNUSED(content);
        // onCompleted
    },[&]() {
        // onCanceled due to timeout
        canceldCalled = true;
    });

    waitUntil(future);
    QCOMPARE(canceldCalled, true);
    timer->deleteLater();
    Automator::wait(500); // wait until the read is finished
}

void Example::example_fileactor()
{
    /* FileActor- read data from a file.
     - If the cache is available, return a QFuture with the content
     - If cache is missed, check do it have any worker thread is ready, return the future of the threadd
     - If no thread is found, start a new one
     */

    class FileActor : public QObject {
    public:

        QFuture<QString> read(QString fileName) {
            QFuture<QString> future;

            if (cache.contains(fileName)) {
                // Cache hit. But it still return a QFuture [finished].

                auto defer = deferred<QString>();
                defer.complete(*cache.object(fileName));
                future = defer.future();

            } else if (workers.contains(fileName)) {

                // If any worker are reading, just return the future
                future = workers[fileName];

            } else {

                class Session {
                public:
                    QString fileName;
                    QString content;
                };

                auto loader = [=](QString fileName){
                    Automator::wait(50);

                    Session session;
                    session.content = fileName + "+data";
                    session.fileName = fileName;

                    return session;
                };

                QFuture<Session> worker = QtConcurrent::run(loader, fileName);
                auto observer = observe(worker).context(this, [=](Session session) {

                    workers.remove(session.fileName);
                    cache.insert(fileName, new QString(session.content));
                    return session.content;
                });

                future = observer.future();
                workers[fileName] = future;
            }

            return future;
        }

        QCache<QString, QString> cache;
        QMap<QString, QFuture<QString> > workers;
    };

    FileActor actor;
    QString fileName = "input.txt";

    QFuture<QString> future = actor.read(fileName);

    QCOMPARE(future.isFinished(), false);
    QVERIFY(actor.workers.contains(fileName));

    QVERIFY(waitUntil(future, 1000));

    QCOMPARE(future.isFinished(), true);

    QVERIFY(future.result() == "input.txt+data");

    QVERIFY(!actor.workers.contains(fileName));

    // Read the content again
    future = actor.read(fileName);

    QVERIFY(future.isFinished());
    QVERIFY(future.result() == "input.txt+data");

    QString content;

    observe(future).subscribe([&](QString result) {
        content = result;
    });

    tick();

    QVERIFY(future.result() == content);

}

template <typename T, typename Sequence, typename Functor>
QFuture<T> mapped(Sequence input, Functor func){
    auto defer = AsyncFuture::deferred<T>();

    QList<QFuture<T>> futures;
    auto combinator = AsyncFuture::combine();

    for (int i = 0 ; i < input.size() ; i++) {
        auto future = QtConcurrent::run(func, input[i]);
        combinator << future;
        futures << future;
    }

    AsyncFuture::observe(combinator.future()).subscribe([=]() mutable {
        QList<T> res;
        for (int i = 0 ; i < futures.size(); i++) {
            res << futures[i].result();
        }
        defer.complete(res);
    });

    return defer.future();
}

/// To simulate a QtConcurrent::mapped
void Example::example_simulate_qtconcurrent_mapped()
{
    auto worker = [=](int value) {
        Automator::wait(10);
        return value * value;
    };

    QList<int> input, expected;
    for (int i = 0; i < 3; i++) {
        input << i;
        expected << i * i;
    }

    QFuture<int> future = mapped<int>(input, worker);

    Test::waitUntil(future);

    QCOMPARE(future.isFinished(), true);

    QList<int> result = future.results();

    QVERIFY(result == expected);
}

static QMutex semaphoreWorkerMutex;
static int semaphoreWorkerRunningCount = 0;
static int semaphoreWorkerFinishedCount = 0;
static QSemaphore internalSemaphore(1);

static int semaphoreWorker(int value) {
    semaphoreWorkerMutex.lock();
    semaphoreWorkerRunningCount++;
    semaphoreWorkerMutex.unlock();
    internalSemaphore.acquire();
    semaphoreWorkerMutex.lock();
    semaphoreWorkerFinishedCount++;
    semaphoreWorkerMutex.unlock();
    internalSemaphore.release();
    return value;
}

void Example::example_qtconcurrent_mapped_cancel()
{
    //@TODO - It need an improved cancellation token mechanism

    semaphoreWorkerRunningCount = 0;
    semaphoreWorkerFinishedCount = 0;
    internalSemaphore.acquire(1);

    int count = QThreadPool::globalInstance()->maxThreadCount() * 2;

    // The input size should be double than the no. of thread available in QThreadPool
    QList<int> input;

    for (int i = 0 ;i < count;i++) {
        input << i;
    }

    auto cancelToken = deferred<void>();

    auto f1 = timeout(100);

    auto f2 = observe(f1).subscribe([&](){
        auto future = QtConcurrent::mapped(input, semaphoreWorker);
        cancelToken.subscribe([](){}, [=]() {
            auto f = future;
            f.cancel();
        });

        return future;
    }).future();

    observe(f2).subscribe([](){}, [=]() {
        auto d = cancelToken;
        d.cancel();
    });

    await(f1);

    // Wait until all the thread are blocked;
    // QtConcurrent::mapped coult not take QThreadPool as input argument.
    Automator::wait(1000);

    QVERIFY(semaphoreWorkerRunningCount > 0);
    QCOMPARE(semaphoreWorkerFinishedCount, 0);
    int runningCount = semaphoreWorkerRunningCount;

    // Cancel f2. No more worker should be executed
    f2.cancel();

    Automator::wait(100);

    internalSemaphore.release();

    QTRY_COMPARE(semaphoreWorkerFinishedCount, runningCount);
    Automator::wait(100);

    QCOMPARE(semaphoreWorkerRunningCount, runningCount);
    QCOMPARE(semaphoreWorkerFinishedCount, runningCount);

}

template <typename Functor, typename Token>
auto cancellationWrapper(Functor functor, Token token) -> std::function<QFuture<void>()> {

    return [=]() mutable -> QFuture<void> {
        if (token.isCanceled()) {
            return token;
        }

        functor();
        auto defer = deferred<void>();
        defer.complete();
        return defer.future();
    };
}

void Example::example_CancellationToken()
{
    Deferred<void> cancellation = deferred<void>();

    QFuture<void> cancellationToken = cancellation.future();

    Deferred<void> start;

    QList<qreal> sequence;
    QList<qreal> expectedSquence;
    expectedSquence << 1 << 1.5;

    auto f1 = start.future();

    auto f2 = observe(f1).subscribe(cancellationWrapper([&]() {
        sequence << 1;
    }, cancellationToken)).future();

    observe(f2).subscribe([&]() {
        sequence << 1.5;
        cancellation.cancel();
    });

    auto f3 = observe(f2).subscribe(cancellationWrapper([&]() mutable {
        sequence << 2;
    },cancellationToken)).future();

    observe(f3).subscribe(cancellationWrapper([&]() {
        sequence << 3;
    }, cancellationToken)).future();

    start.complete();
    Automator::wait(500);

    QCOMPARE(sequence, expectedSquence);

}

void Example::example_Combinator_timeout()
{
    QFuture<QString> f1 = QtConcurrent::run(delayedFakeRead, QString("input1.txt"));

    QFuture<QString> f2 = QtConcurrent::run(delayedFakeRead, QString("input2.txt"));

    auto combinator = AsyncFuture::combine();
    combinator << f1 << f2;

    auto defer = deferred<void>();

    defer.complete(combinator.future());
    defer.cancel(AsyncFutureUtils::timeout(10));

    auto returningFuture = defer.future();

    await(returningFuture);

    QCOMPARE(returningFuture.isCanceled(), true);
    QCOMPARE(returningFuture.isFinished(), true);

    QCOMPARE(returningFuture.progressMaximum(), 2);
}

<commit_msg>example mapped<T> - Rewritten to support cancelation<commit_after>#include <QQmlApplicationEngine>
#include <QTest>
#include <Automator>
#include <QtConcurrent>
#include <asyncfuture.h>
#include <QTimer>
#include "example.h"
#include "testfunctions.h"
#include "asyncfutureutils.h"

using namespace AsyncFuture;
using namespace AsyncFutureUtils;
using namespace Test;

static QString delayedFakeRead(const QString& fileName) {
    Q_UNUSED(fileName);

    Automator::wait(100);
    return "";
}

Example::Example(QObject *parent) : QObject(parent)
{

    // This function do nothing but could make Qt Creator Autotests plugin recognize this test
    auto ref =[=]() {
        QTest::qExec(this, 0, 0);
    };
    Q_UNUSED(ref);

}

/*
 * 1. Convert a signal from QObject into a QFuture object
 * https://github.com/benlau/asyncfuture
 *
 */

void Example::example_Timer_timeout()
{
    QTimer *timer = new QTimer(this);
    timer->setInterval(50);

    // Convert a signal from QObject into QFuture
    QFuture<void> future = observe(timer,
                                   &QTimer::timeout).future();

    // Listen from the future without using QFutureWatcher<T>
    observe(future).subscribe([]() {
        // onCompleted. It is invoked when the observed future is finished successfully
        qDebug() << "onCompleted";
    },[]() {
        // onCanceled
        qDebug() << "onCancel";
    });

    // It is chainable. Listen from a timeout signal only once

    observe(timer, &QTimer::timeout).subscribe([=]() { /*…*/ });

    timer->start();
    waitUntil(future);
}

/*
 *
 * 2. Combine multiple futures with different type into a single future object
 * https://github.com/benlau/asyncfuture
 *
 */

void Example::example_combine_multiple_future()
{
    auto readImage = [](QString) -> QImage {
        // Not a real image reader
        Automator::wait(50);
        return QImage();
    };

    QTimer *timer = new QTimer(this);
    timer->setInterval(80);

    // Combine multiple futures with different type into a single future
    QFuture<QImage> f1 = QtConcurrent::run(readImage, QString("image.jpg"));

    QFuture<void> f2 = observe(timer, &QTimer::timeout).future();

    QFuture<QImage> result = (combine() << f1 << f2).subscribe([=](){
        // Read an image but do not return before timeout
        return f1.result();
    }).future();

    timer->start();

    QVERIFY(waitUntil(result, 1000));

    timer->deleteLater();
}

void Example::example_worker_context()
{

    auto readImage = [](QString) -> QImage {
        // Not a real image reader
        Automator::wait(50);
        return QImage();
    };

    auto validator = [](QImage image) {
        return image.isNull();
    };

    QObject* contextObject = new QObject(this);

    // Start a thread and process its result in main thread

    QFuture<QImage> reading = QtConcurrent::run(readImage, QString("image.jpg"));

    QFuture<bool> validating = observe(reading).context(contextObject, validator).future();

    // Read image by a thread, when it is ready, run the validator function
    // in the thread of the contextObject(e.g main thread)
    // And it return another QFuture to represent the final result.

    QVERIFY(waitUntil(validating, 1000));

    QCOMPARE(validating.result(), true);

}

static int mapFunc(int value) {
    return value * value;
}

void Example::example_context_return_future()
{

    auto reducerFunc = [](QList<int> input ) -> int {
        return input.size();
    };

    QList<int> input;
    for (int i = 0 ; i < 10;i++) {
        input << i;
    }

    QObject* contextObject = this;

    QFuture<int> f1 = QtConcurrent::mapped(input, mapFunc);

    // You may use QFuture as the input argument of your callback function
    // It will be set to the observed future object. So that you may obtain
    // the value of results()
    QFuture<int> f2 = observe(f1).context(contextObject, [=](QFuture<int> future) {
        qDebug() << future.results();

        // Return another QFuture is possible.
        return QtConcurrent::run(reducerFunc, future.results());
    }).future();

    // f2 is constructed before the QtConcurrent::run statement
    // But its value is equal to the result of reducerFunc

    waitUntil(f2);

    QCOMPARE(f2.result(), 10);
}

void Example::example_promise_like()
{
    // Complete / cancel a future on your own choice
    auto d = deferred<bool>();

    observe(d.future()).subscribe([]() {
        qDebug() << "onCompleted";
    }, []() {
        qDebug() << "onCancel";
    });

    d.complete(true);
    d.cancel();

    QCOMPARE(d.future().isFinished(), true);
    QCOMPARE(d.future().isCanceled(), false);

    QVERIFY(waitUntil(d.future(), 1000));
}

void Example::example_promise_like_complete_future()
{
    // Complete / cancel a future according to another future object.
    auto timeout = []() -> void {
        Automator::wait(50);
    };

    auto d = deferred<void>();

    d.complete(QtConcurrent::run(timeout));

    QCOMPARE(d.future().isFinished(), false);
    QCOMPARE(d.future().isCanceled(), false);

    QVERIFY(waitUntil(d.future(), 1000));
}

void Example::example_promise_like_timeout()
{
    QTimer* timer = new QTimer(this);
    timer->setInterval(50);

    auto readFileworker = [](QString fileName) -> QString {
        Q_UNUSED(fileName);
        Automator::wait(1000);
        return "";
    };

    auto read = [=](const QString &fileName) {
        timer->start();

        auto timeout = observe(timer, &QTimer::timeout).future();

        auto defer = deferred<QString>();

        defer.complete(QtConcurrent::run(readFileworker, fileName));
        defer.cancel(timeout);

        return defer.future();
    };

    auto future = read("input.txt");

    bool canceldCalled = false;

    observe(future).subscribe([](QString content) {
        Q_UNUSED(content);
        // onCompleted
    },[&]() {
        // onCanceled due to timeout
        canceldCalled = true;
    });

    waitUntil(future);
    QCOMPARE(canceldCalled, true);
    timer->deleteLater();
    Automator::wait(500); // wait until the read is finished
}

void Example::example_fileactor()
{
    /* FileActor- read data from a file.
     - If the cache is available, return a QFuture with the content
     - If cache is missed, check do it have any worker thread is ready, return the future of the threadd
     - If no thread is found, start a new one
     */

    class FileActor : public QObject {
    public:

        QFuture<QString> read(QString fileName) {
            QFuture<QString> future;

            if (cache.contains(fileName)) {
                // Cache hit. But it still return a QFuture [finished].

                auto defer = deferred<QString>();
                defer.complete(*cache.object(fileName));
                future = defer.future();

            } else if (workers.contains(fileName)) {

                // If any worker are reading, just return the future
                future = workers[fileName];

            } else {

                class Session {
                public:
                    QString fileName;
                    QString content;
                };

                auto loader = [=](QString fileName){
                    Automator::wait(50);

                    Session session;
                    session.content = fileName + "+data";
                    session.fileName = fileName;

                    return session;
                };

                QFuture<Session> worker = QtConcurrent::run(loader, fileName);
                auto observer = observe(worker).context(this, [=](Session session) {

                    workers.remove(session.fileName);
                    cache.insert(fileName, new QString(session.content));
                    return session.content;
                });

                future = observer.future();
                workers[fileName] = future;
            }

            return future;
        }

        QCache<QString, QString> cache;
        QMap<QString, QFuture<QString> > workers;
    };

    FileActor actor;
    QString fileName = "input.txt";

    QFuture<QString> future = actor.read(fileName);

    QCOMPARE(future.isFinished(), false);
    QVERIFY(actor.workers.contains(fileName));

    QVERIFY(waitUntil(future, 1000));

    QCOMPARE(future.isFinished(), true);

    QVERIFY(future.result() == "input.txt+data");

    QVERIFY(!actor.workers.contains(fileName));

    // Read the content again
    future = actor.read(fileName);

    QVERIFY(future.isFinished());
    QVERIFY(future.result() == "input.txt+data");

    QString content;

    observe(future).subscribe([&](QString result) {
        content = result;
    });

    tick();

    QVERIFY(future.result() == content);

}

template <typename T, typename Sequence, typename Functor>
QFuture<T> mapped(Sequence input, Functor func){
    class Context {
    public:
        QMutex mutex;
        Sequence input;
        QVector<T> output;
        int finishedCount;
        int index;
        std::function<void(int)> worker;
    };
    QThreadPool *pool = QThreadPool::globalInstance();

    auto defer = AsyncFuture::deferred<T>();

    /// Allow to be canceled by user
    defer.onCanceled(defer);

    int insertCount = qMin(pool->maxThreadCount(),input.size());

    QSharedPointer<Context> context(new Context());
    context->input = input;
    context->index = insertCount;
    context->finishedCount = 0;
    context->output = QVector<T>(input.size());

    context->worker = [defer, pool, context, func](int pos) mutable {
        T res = func(context->input[pos]);
        //@TODO - update prgress value

        context->mutex.lock();
        context->output[pos] = res;

        if (!defer.future().isCanceled()) {
            int index = context->index;
            if (index < context->input.size()) {
                QtConcurrent::run(pool, context->worker, index++);
                context->index = index;
            }

            context->finishedCount++;
            if (context->finishedCount >= context->input.size()) {
                defer.complete(context->output.toList());
                context->worker = nullptr;
            }
        }
        context->mutex.unlock();

    };

    for (int i = 0 ; i < insertCount ; i++) {
        QtConcurrent::run(pool, context->worker, i);
    }

    return defer.future();
}

/// To simulate a QtConcurrent::mapped
void Example::example_simulate_qtconcurrent_mapped()
{
    auto worker = [=](int value) {
        Automator::wait(10);
        return value * value;
    };

    QList<int> input, expected;
    for (int i = 0; i < 20; i++) {
        input << i;
        expected << i * i;
    }

    QFuture<int> future = mapped<int>(input, worker);

    Test::waitUntil(future);

    QCOMPARE(future.isFinished(), true);

    QList<int> result = future.results();

    QVERIFY(result == expected);
}

static QMutex semaphoreWorkerMutex;
static int semaphoreWorkerRunningCount = 0;
static int semaphoreWorkerFinishedCount = 0;
static QSemaphore internalSemaphore(1);

static int semaphoreWorker(int value) {
    semaphoreWorkerMutex.lock();
    semaphoreWorkerRunningCount++;
    semaphoreWorkerMutex.unlock();
    internalSemaphore.acquire();
    semaphoreWorkerMutex.lock();
    semaphoreWorkerFinishedCount++;
    semaphoreWorkerMutex.unlock();
    internalSemaphore.release();
    return value;
}

void Example::example_qtconcurrent_mapped_cancel()
{
    //@TODO - It need an improved cancellation token mechanism

    semaphoreWorkerRunningCount = 0;
    semaphoreWorkerFinishedCount = 0;
    internalSemaphore.acquire(1);

    int count = QThreadPool::globalInstance()->maxThreadCount() * 2;

    // The input size should be double than the no. of thread available in QThreadPool
    QList<int> input;

    for (int i = 0 ;i < count;i++) {
        input << i;
    }

    auto cancelToken = deferred<void>();

    auto f1 = timeout(100);

    auto f2 = observe(f1).subscribe([&](){
        auto future = QtConcurrent::mapped(input, semaphoreWorker);
        cancelToken.onCanceled(future);
        return future;
    }).future();

    observe(f2).subscribe([](){}, [=]() {
        auto d = cancelToken;
        d.cancel();
    });

    await(f1);

    // Wait until all the thread are blocked;
    // QtConcurrent::mapped coult not take QThreadPool as input argument.
    Automator::wait(1000);

    QVERIFY(semaphoreWorkerRunningCount > 0);
    QCOMPARE(semaphoreWorkerFinishedCount, 0);
    int runningCount = semaphoreWorkerRunningCount;

    // Cancel f2. No more worker should be executed
    f2.cancel();

    Automator::wait(100);

    internalSemaphore.release();

    QTRY_COMPARE(semaphoreWorkerFinishedCount, runningCount);
    Automator::wait(100);

    QCOMPARE(semaphoreWorkerRunningCount, runningCount);
    QCOMPARE(semaphoreWorkerFinishedCount, runningCount);

}

template <typename Functor, typename Token>
auto cancellationWrapper(Functor functor, Token token) -> std::function<QFuture<void>()> {

    return [=]() mutable -> QFuture<void> {
        if (token.isCanceled()) {
            return token;
        }

        functor();
        auto defer = deferred<void>();
        defer.complete();
        return defer.future();
    };
}

void Example::example_CancellationToken()
{
    //@TODO - It is over complicated
    Deferred<void> cancellation = deferred<void>();

    QFuture<void> cancellationToken = cancellation.future();

    Deferred<void> start;

    QList<qreal> sequence;
    QList<qreal> expectedSquence;
    expectedSquence << 1 << 1.5;

    auto f1 = start.future();

    auto f2 = observe(f1).subscribe(cancellationWrapper([&]() {
        sequence << 1;
    }, cancellationToken)).future();

    observe(f2).subscribe([&]() {
        sequence << 1.5;
        cancellation.cancel();
    });

    auto f3 = observe(f2).subscribe(cancellationWrapper([&]() mutable {
        sequence << 2;
    },cancellationToken)).future();

    observe(f3).subscribe(cancellationWrapper([&]() {
        sequence << 3;
    }, cancellationToken)).future();

    start.complete();
    Automator::wait(500);

    QCOMPARE(sequence, expectedSquence);

}

void Example::example_Combinator_timeout()
{
    QFuture<QString> f1 = QtConcurrent::run(delayedFakeRead, QString("input1.txt"));

    QFuture<QString> f2 = QtConcurrent::run(delayedFakeRead, QString("input2.txt"));

    auto combinator = AsyncFuture::combine();
    combinator << f1 << f2;

    auto defer = deferred<void>();

    defer.complete(combinator.future());
    defer.cancel(AsyncFutureUtils::timeout(10));

    auto returningFuture = defer.future();

    await(returningFuture);

    QCOMPARE(returningFuture.isCanceled(), true);
    QCOMPARE(returningFuture.isFinished(), true);

    QCOMPARE(returningFuture.progressMaximum(), 2);
}

<|endoftext|>


<commit_before>//===-- SILOpt.cpp - SIL Optimization Driver ------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This is the entry point to the swift app.
//
//===----------------------------------------------------------------------===//

#include "swift/Subsystems.h"
#include "swift/IRGen/Options.h"
#include "swift/AST/AST.h"
#include "swift/AST/ASTVisitor.h"
#include "swift/AST/Diagnostics.h"
#include "swift/Parse/Lexer.h"
#include "swift/SIL/SILModule.h"
#include "swift/Basic/DiagnosticConsumer.h"
#include "swift/Frontend/Frontend.h"
#include "swift/Frontend/PrintingDiagnosticConsumer.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/system_error.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/ToolOutputFile.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/ADT/Triple.h"
using namespace swift;

enum class PassKind {
  AllocBoxPromotion,
  StackToSSA,
  DataflowDiagnostics
};

static llvm::cl::opt<bool>
UseMalloc("use-malloc",
          llvm::cl::desc("allocate internal data structures using malloc "
                         "(for memory debugging)"));

static llvm::cl::opt<std::string>
InputFilename(llvm::cl::desc("input file"), llvm::cl::init("-"),
              llvm::cl::Positional);

static llvm::cl::opt<std::string>
OutputFilename("o", llvm::cl::desc("output filename"), llvm::cl::init("-"));

static llvm::cl::list<std::string>
ImportPaths("I", llvm::cl::desc("add a directory to the import search path"));

static llvm::cl::list<PassKind>
Passes(llvm::cl::desc("Passes:"),
       llvm::cl::values(clEnumValN(PassKind::AllocBoxPromotion,
                                   "allocbox-promotion", "Promote memory"),
                        clEnumValN(PassKind::StackToSSA,
                                   "stacktossa", "alloc_stack to SSA"),
                        clEnumValN(PassKind::DataflowDiagnostics,
                                   "dataflow-diagnostics",
                                   "Emit SIL diagnostics"),
                        clEnumValEnd));

static llvm::cl::opt<bool>
PrintStats("print-stats", llvm::cl::desc("Print various statistics"));

// This function isn't referenced outside its translation unit, but it
// can't use the "static" keyword because its address is used for
// getMainExecutable (since some platforms don't support taking the
// address of main, and some platforms can't implement getMainExecutable
// without being given the address of a function in the main executable).
void anchorForGetMainExecutable() {}

int main(int argc, char **argv) {
  // Print a stack trace if we signal out.
  llvm::sys::PrintStackTraceOnErrorSignal();
  llvm::PrettyStackTraceProgram X(argc, argv);

  llvm::cl::ParseCommandLineOptions(argc, argv, "Swift SIL optimizer\n");

  if (PrintStats)
    llvm::EnableStatistics();

  llvm::IntrusiveRefCntPtr<CompilerInvocation> Invocation(
      new CompilerInvocation());

  Invocation->setMainExecutablePath(
      llvm::sys::fs::getMainExecutable(argv[0],
          reinterpret_cast<void *>(&anchorForGetMainExecutable)));

  // Give the context the list of search paths to use for modules.
  Invocation->setImportSearchPaths(ImportPaths);

  PrintingDiagnosticConsumer PrintDiags;
  Invocation->addDiagnosticConsumer(&PrintDiags);
  Invocation->setModuleName("main");
  Invocation->setTUKind(TranslationUnit::SIL);
  CompilerInstance CI(Invocation);

  // Open the input file.
  llvm::OwningPtr<llvm::MemoryBuffer> InputFile;
  if (llvm::error_code Err =
        llvm::MemoryBuffer::getFileOrSTDIN(InputFilename, InputFile)) {
    llvm::errs() << "swift: error opening input file: " << Err.message()
                 << '\n';
    return 1;
  }

  // Transfer ownership of the MemoryBuffer to the SourceMgr.
  CI.addBufferID(CI.getSourceMgr().AddNewSourceBuffer(InputFile.take(),
                                                      llvm::SMLoc()));
  CI.setup();
  CI.doIt();

  for (auto Pass : Passes) {
    switch (Pass) {
    case PassKind::AllocBoxPromotion:
      performSILAllocBoxPromotion(CI.getSILModule());
      break;
    case PassKind::StackToSSA:
      performSILStackToSSA(CI.getSILModule());
      break;
    case PassKind::DataflowDiagnostics:
      emitSILDataflowDiagnostics(CI.getSILModule());
      break;
    }
  }

  std::string ErrorInfo;
  llvm::raw_fd_ostream OS(OutputFilename.c_str(), ErrorInfo);
  if (!ErrorInfo.empty()) {
    llvm::errs() << "while opening '" << OutputFilename << "': "
                 << ErrorInfo << '\n';
    return 1;
  }
  CI.getSILModule()->print(OS);

  // Print statistics.
  if (PrintStats)
    llvm::PrintStatistics(llvm::errs());

  return CI.getASTContext().hadError();
}
<commit_msg>tidy this up.<commit_after>//===-- SILOpt.cpp - SIL Optimization Driver ------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This is the entry point.
//
//===----------------------------------------------------------------------===//

#include "swift/Subsystems.h"
#include "swift/Frontend/Frontend.h"
#include "swift/Frontend/PrintingDiagnosticConsumer.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Signals.h"
using namespace swift;

enum class PassKind {
  AllocBoxPromotion,
  StackToSSA,
  DataflowDiagnostics
};

static llvm::cl::opt<std::string>
InputFilename(llvm::cl::desc("input file"), llvm::cl::init("-"),
              llvm::cl::Positional);

static llvm::cl::opt<std::string>
OutputFilename("o", llvm::cl::desc("output filename"), llvm::cl::init("-"));

static llvm::cl::list<std::string>
ImportPaths("I", llvm::cl::desc("add a directory to the import search path"));

static llvm::cl::list<PassKind>
Passes(llvm::cl::desc("Passes:"),
       llvm::cl::values(clEnumValN(PassKind::AllocBoxPromotion,
                                   "allocbox-promotion", "Promote memory"),
                        clEnumValN(PassKind::StackToSSA,
                                   "stacktossa", "alloc_stack to SSA"),
                        clEnumValN(PassKind::DataflowDiagnostics,
                                   "dataflow-diagnostics",
                                   "Emit SIL diagnostics"),
                        clEnumValEnd));

static llvm::cl::opt<bool>
PrintStats("print-stats", llvm::cl::desc("Print various statistics"));

// This function isn't referenced outside its translation unit, but it
// can't use the "static" keyword because its address is used for
// getMainExecutable (since some platforms don't support taking the
// address of main, and some platforms can't implement getMainExecutable
// without being given the address of a function in the main executable).
void anchorForGetMainExecutable() {}

int main(int argc, char **argv) {
  // Print a stack trace if we signal out.
  llvm::sys::PrintStackTraceOnErrorSignal();
  llvm::PrettyStackTraceProgram X(argc, argv);

  llvm::cl::ParseCommandLineOptions(argc, argv, "Swift SIL optimizer\n");

  if (PrintStats)
    llvm::EnableStatistics();

  llvm::IntrusiveRefCntPtr<CompilerInvocation> Invocation(
      new CompilerInvocation());

  Invocation->setMainExecutablePath(
      llvm::sys::fs::getMainExecutable(argv[0],
          reinterpret_cast<void *>(&anchorForGetMainExecutable)));

  // Give the context the list of search paths to use for modules.
  Invocation->setImportSearchPaths(ImportPaths);

  PrintingDiagnosticConsumer PrintDiags;
  Invocation->addDiagnosticConsumer(&PrintDiags);
  Invocation->setModuleName("main");
  Invocation->setTUKind(TranslationUnit::SIL);
  CompilerInstance CI(Invocation);

  // Open the input file.
  llvm::OwningPtr<llvm::MemoryBuffer> InputFile;
  if (llvm::error_code Err =
        llvm::MemoryBuffer::getFileOrSTDIN(InputFilename, InputFile)) {
    llvm::errs() << "swift: error opening input file: " << Err.message()
                 << '\n';
    return 1;
  }

  // Transfer ownership of the MemoryBuffer to the SourceMgr.
  CI.addBufferID(CI.getSourceMgr().AddNewSourceBuffer(InputFile.take(),
                                                      llvm::SMLoc()));
  CI.setup();
  CI.doIt();

  for (auto Pass : Passes) {
    switch (Pass) {
    case PassKind::AllocBoxPromotion:
      performSILAllocBoxPromotion(CI.getSILModule());
      break;
    case PassKind::StackToSSA:
      performSILStackToSSA(CI.getSILModule());
      break;
    case PassKind::DataflowDiagnostics:
      emitSILDataflowDiagnostics(CI.getSILModule());
      break;
    }
  }

  std::string ErrorInfo;
  llvm::raw_fd_ostream OS(OutputFilename.c_str(), ErrorInfo);
  if (!ErrorInfo.empty()) {
    llvm::errs() << "while opening '" << OutputFilename << "': "
                 << ErrorInfo << '\n';
    return 1;
  }
  CI.getSILModule()->print(OS);

  // Print statistics.
  if (PrintStats)
    llvm::PrintStatistics(llvm::errs());

  return CI.getASTContext().hadError();
}
<|endoftext|>


<commit_before>/* Copyright (c) 2010-2012, AOYAMA Kazuharu
 * All rights reserved.
 *
 * This software may be used and distributed according to the terms of
 * the New BSD License, which is incorporated herein by reference.
 */

#include <QtCore>
#include <QSysInfo>
#include <TWebApplication>
#include <TSystemGlobal>
#include "servermanager.h"
#include "processinfo.h"

#ifdef Q_OS_UNIX
# include <sys/utsname.h>
#elsif Q_OS_WIN
# include <stdlib.h>
#endif

namespace TreeFrog {

#ifdef Q_OS_WIN
extern void WINAPI winServiceMain(DWORD argc, LPTSTR *argv);
#endif

enum CommandOption {
    Invalid = 0,
    EnvironmentSpecified,
    SocketSpecified,
    PrintVersion,
    PrintUsage,
    DaemonMode,
    WindowsServiceMode,
    SendSignal,
};

typedef QHash<int, QString> VersionHash;

#ifdef Q_OS_WIN
Q_GLOBAL_STATIC_WITH_INITIALIZER(VersionHash, winVersion,
{
    x->insert(QSysInfo::WV_XP,       "Windows XP");
    x->insert(QSysInfo::WV_2003,     "Windows Server 2003");
    x->insert(QSysInfo::WV_VISTA,    "Windows Vista or Windows Server 2008");
    x->insert(QSysInfo::WV_WINDOWS7, "Windows 7 or Windows Server 2008 R2");
})
#endif

#ifdef Q_OS_DARWIN
Q_GLOBAL_STATIC_WITH_INITIALIZER(VersionHash, macVersion,
{
    x->insert(QSysInfo::MV_10_3, "Mac OS X 10.3 Panther");
    x->insert(QSysInfo::MV_10_4, "Mac OS X 10.4 Tiger");
    x->insert(QSysInfo::MV_10_5, "Mac OS X 10.5 Leopard");
    x->insert(QSysInfo::MV_10_6, "Mac OS X 10.6 Snow Leopard");
#if QT_VERSION >= 0x040800
    x->insert(QSysInfo::MV_10_7, "Mac OS X 10.7 Lion");
#endif
})
#endif

typedef QHash<QString, int> OptionHash;
Q_GLOBAL_STATIC_WITH_INITIALIZER(OptionHash, options,
{
    x->insert("-e", EnvironmentSpecified);
    x->insert("-s", SocketSpecified);
    x->insert("-v", PrintVersion);
    x->insert("-h", PrintUsage);
    x->insert("-d", DaemonMode);
    x->insert("-w", WindowsServiceMode);
    x->insert("-k", SendSignal);
})

static void usage()
{
    char text[] =
        "Usage: %1 [-d] [-e environment] [application-directory]\n"     \
        "Usage: %1 [-k stop|abort] [application-directory]\n"           \
        "Options:\n"                                                    \
        "  -d              : run as a daemon process\n"                 \
        "  -e environment  : specify an environment of the database settings\n" \
        "  -k              : send signal to a manager process\n\n"      \
        "Type '%1 -h' to show this information.\n"                      \
        "Type '%1 -v' to show the program version.";

    QString cmd = QFileInfo(QCoreApplication::applicationFilePath()).fileName();
    puts(qPrintable(QString(text).arg(cmd)));
}

static bool checkArguments()
{
    for (QStringListIterator i(QCoreApplication::arguments()); i.hasNext(); ) {
        const QString &arg = i.next();
        if (arg.startsWith('-') && options()->value(arg, Invalid) == Invalid) {
            fprintf(stderr, "invalid argument\n");
            return false;
        }
    }
    return true;
}

static bool startDaemon()
{
    bool success;
    QStringList args = QCoreApplication::arguments();
    args.removeAll("-d");
#ifdef Q_OS_WIN
    PROCESS_INFORMATION pinfo;
    STARTUPINFOW startupInfo = { sizeof(STARTUPINFO), 0, 0, 0,
                                 (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
                                 (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
                                 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
    };
    success = CreateProcess(0, (wchar_t*)args.join(" ").utf16(),
                            0, 0, FALSE, CREATE_UNICODE_ENVIRONMENT, 0,
                            (wchar_t*)QDir::currentPath().utf16(),
                            &startupInfo, &pinfo);
    if (success) {
        CloseHandle(pinfo.hThread);
        CloseHandle(pinfo.hProcess);
    }
#else
    args.removeFirst();  // Removes the program name
    success = QProcess::startDetached(QCoreApplication::applicationFilePath(), args, QDir::currentPath());
#endif

    return success;
}

static void writeStartupLog()
{
    tSystemInfo("TreeFrog Framework version " TF_VERSION_STR);

    QString qtversion = "Qt " QT_VERSION_STR;
#if defined(Q_OS_WIN)
    qtversion += QLatin1String(" / ") + winVersion()->value(QSysInfo::WindowsVersion, "Windows");
#elif defined(Q_OS_DARWIN)
    qtversion += QLatin1String(" / ") + macVersion()->value(QSysInfo::MacintoshVersion, "Mac OS X");
#elif defined(Q_OS_UNIX)
    struct utsname uts;
    if (uname(&uts) == 0) {
        qtversion += QString(" / %1 %2").arg(uts.sysname).arg(uts.release);
    }
#endif
    tSystemInfo("%s", qtversion.toLatin1().data());
}

static QString pidFilePath()
{
    QString base = QFileInfo(QCoreApplication::applicationFilePath()).baseName();
    return Tf::app()->tmpPath() + base + ".pid";
}

static qint64 readPidFileOfApplication()
{
    QFile pidf(pidFilePath());
    if (pidf.open(QIODevice::ReadOnly)) {
        qint64 pid = pidf.readLine(100).toLongLong();
        if (pid > 0) {
            return pid;
        }
    }
    return -1;
}

static qint64 runningApplicationPid()
{
    qint64 pid = readPidFileOfApplication();
    if (pid > 0) {
        QString name = ProcessInfo(pid).processName().toLower();
        if (name == "treefrog" || name == "treefrogd")
            return pid;
    }
    return -1;
}

static int killTreeFrogProcess(const QString &cmd)
{
    qint64 pid = runningApplicationPid();
    if (pid < 0) {
        printf("TreeFrog server not running\n");
        return 1;
    }

    ProcessInfo pi(pid);

    if (cmd == "stop") {  // stop command
        pi.terminate();
        if (pi.waitForTerminated()) {
            printf("TreeFrog application servers shutdown completed\n");
        } else {
            fprintf(stderr, "TreeFrog application servers shutdown failed\n");
        }

    } else if (cmd == "abort") {  // abort command
        pi.kill();
        tSystemInfo("Killed TreeFrog manager process  pid:%ld", (long)pid);
        ::unlink(pidFilePath().toLatin1().data());

        // kill all 'tadpole' processes
#if defined(Q_OS_WIN) && !defined(TF_NO_DEBUG)
        ProcessInfo::killProcesses("tadpoled");
#else
        ProcessInfo::killProcesses("tadpole");
#endif
        tSystemInfo("Killed TreeFrog application server processes");
        printf("Killed TreeFrog application server processes\n");

    } else if (cmd == "restart") {  // restart command
        pi.restart();
        printf("Sent a restart request\n");

    } else {
        usage();
        return 1;
    }
    return 0;
}

int managerMain(int argc, char *argv[])
{
    TWebApplication app(argc, argv);

    // Creates the semaphore for system log
    QSystemSemaphore semaphore("TreeFrogSystemLog", 1, QSystemSemaphore::Create);
    tSetupSystemLoggers();

#if defined(Q_OS_UNIX)
    app.watchUnixSignal(SIGTERM);
    app.watchUnixSignal(SIGINT);
    app.watchUnixSignal(SIGHUP);
#elif defined(Q_OS_WIN)
    app.watchConsoleSignal();
#endif

    // Sets codec
    QTextCodec *codec = QTextCodec::codecForName("UTF-8");
    QTextCodec::setCodecForLocale(codec);

    if (!checkArguments()) {
        return 1;
    }

    bool daemonMode = false;
    QString signalCmd;

    QStringList args = QCoreApplication::arguments();
    args.removeFirst();
    for (QStringListIterator i(args); i.hasNext(); ) {
        const QString &arg = i.next();
        int cmd = options()->value(arg, Invalid);
        switch (cmd) {
        case PrintVersion:
            printf("%s version " TF_VERSION_STR " (r%d) (built on %s)\n", qPrintable(QFileInfo(argv[0]).baseName()), TF_SRC_REVISION, __DATE__);
            return 0;
            break;

        case PrintUsage:
            usage();
            return 0;
            break;

        case DaemonMode:
            daemonMode = true;
            break;

        case WindowsServiceMode:
            // ignore
            break;

        case SendSignal:
            signalCmd = i.next(); // assign a command
            break;

        default:
            // do nothing
            break;
        }
    }

    if (!app.appSettingsFileExists()) {
        fprintf(stderr, "INI file not found [%s]\n\n", qPrintable(app.appSettingsFilePath()));
        return 1;
    }

    if (!signalCmd.isEmpty()) {
        return killTreeFrogProcess(signalCmd);
    }

    // Check TreeFrog processes are running
    qint64 pid = runningApplicationPid();
    if (pid > 0) {
        fprintf(stderr, "Already running  pid:%ld\n", (long)pid);
        return 1;
    }

    if (!app.isValidDatabaseSettings()) {
        tSystemError("Database settings not found  [environment: %s]", qPrintable(app.databaseEnvironment()));
        fprintf(stderr, "database settings not found  [environment: %s]\n\n", qPrintable(app.databaseEnvironment()));
        usage();
        return 1;
    }

    // Check a port number
    quint16 listenPort = 0;
    QString svrname = app.appSettings().value("ListenPort").toString();
    if (svrname.startsWith("unix:", Qt::CaseInsensitive)) {
        svrname.remove(0, 5);
    } else {
        int port = svrname.toInt();
        if (port <= 0 || port > USHRT_MAX) {
            tSystemError("Invalid port number: %d", port);
            return 1;
        }
        listenPort = port;
        svrname.clear();
    }

    // start daemon process
    if (daemonMode) {
        if ( !startDaemon() ) {
            fprintf(stderr, "Failed to create process\n\n");
            return 1;
        }
        return 0;
    }

    int ret = 0;
    QFile pidfile;

    for (;;) {
        ServerManager *manager = 0;
        switch ( app.multiProcessingModule() ) {
        case TWebApplication::Thread: {
            manager = new ServerManager(1, 1, 0, &app);
            break; }

        case TWebApplication::Prefork: {
            int max = app.appSettings().value("MPM.prefork.MaxServers").toInt();
            int min = app.appSettings().value("MPM.prefork.MinServers").toInt();
            int spare = app.appSettings().value("MPM.prefork.SpareServers").toInt();
            manager = new ServerManager(max, min, spare, &app);
            break; }

        default:
            tSystemError("Invalid MPM specified");
            return 1;
        }

        // Startup
        writeStartupLog();
        bool started;
        if (listenPort > 0) {
            // TCP/IP
            started = manager->start(QHostAddress::Any, listenPort);
        } else {
            // UNIX domain
            started = manager->start(svrname);
        }

        if (!started) {
            tSystemError("TreeFrog application server startup failed");
            fprintf(stderr, "TreeFrog application server startup failed\n\n");
            return 1;
        }

        // tmp directory
        QDir tmpDir(app.tmpPath());
        if (!tmpDir.exists()) {
            tmpDir.mkpath(".");
        }

        // Writes the PID
        pidfile.setFileName(pidFilePath());
        if (pidfile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
            pid = QCoreApplication::applicationPid();
            pidfile.write( QString::number(pid).toLatin1() );
            pidfile.close();
        } else {
            tSystemError("File open failed: %s", qPrintable(pidfile.fileName()));
        }

        ret = app.exec();
        tSystemDebug("tfmanager returnCode:%d", ret);
        manager->stop();

        if (ret == 1) {  // means SIGHUP
            tSystemDebug("Restarts TreeFrog application servers");
            //continue;
        } else {
            break;
        }
    }

    if (!svrname.isEmpty()) {  // UNIX domain file
        QFile(svrname).remove();
    }
    pidfile.remove();  // Removes the PID file
    return 0;
}

} // namespace TreeFrog

int main(int argc, char *argv[])
{
#ifdef Q_OS_WIN
    for (int i = 1; i < argc; ++i) {
        if (strcmp(argv[i], "-w") == 0) {
            // Windows service mode
            SERVICE_TABLE_ENTRY entry[] = { { (LPTSTR)TEXT(""), TreeFrog::winServiceMain }, { 0, 0 } };
            StartServiceCtrlDispatcher(entry);
            return 0;
        }
    }
#endif
    return TreeFrog::managerMain(argc, argv);
}
<commit_msg>Deleted unnecessary codes.<commit_after>/* Copyright (c) 2010-2012, AOYAMA Kazuharu
 * All rights reserved.
 *
 * This software may be used and distributed according to the terms of
 * the New BSD License, which is incorporated herein by reference.
 */

#include <QtCore>
#include <QSysInfo>
#include <TWebApplication>
#include <TSystemGlobal>
#include "servermanager.h"
#include "processinfo.h"

#ifdef Q_OS_UNIX
# include <sys/utsname.h>
#endif

namespace TreeFrog {

#ifdef Q_OS_WIN
extern void WINAPI winServiceMain(DWORD argc, LPTSTR *argv);
#endif

enum CommandOption {
    Invalid = 0,
    EnvironmentSpecified,
    SocketSpecified,
    PrintVersion,
    PrintUsage,
    DaemonMode,
    WindowsServiceMode,
    SendSignal,
};

typedef QHash<int, QString> VersionHash;

#ifdef Q_OS_WIN
Q_GLOBAL_STATIC_WITH_INITIALIZER(VersionHash, winVersion,
{
    x->insert(QSysInfo::WV_XP,       "Windows XP");
    x->insert(QSysInfo::WV_2003,     "Windows Server 2003");
    x->insert(QSysInfo::WV_VISTA,    "Windows Vista or Windows Server 2008");
    x->insert(QSysInfo::WV_WINDOWS7, "Windows 7 or Windows Server 2008 R2");
})
#endif

#ifdef Q_OS_DARWIN
Q_GLOBAL_STATIC_WITH_INITIALIZER(VersionHash, macVersion,
{
    x->insert(QSysInfo::MV_10_3, "Mac OS X 10.3 Panther");
    x->insert(QSysInfo::MV_10_4, "Mac OS X 10.4 Tiger");
    x->insert(QSysInfo::MV_10_5, "Mac OS X 10.5 Leopard");
    x->insert(QSysInfo::MV_10_6, "Mac OS X 10.6 Snow Leopard");
#if QT_VERSION >= 0x040800
    x->insert(QSysInfo::MV_10_7, "Mac OS X 10.7 Lion");
#endif
})
#endif

typedef QHash<QString, int> OptionHash;
Q_GLOBAL_STATIC_WITH_INITIALIZER(OptionHash, options,
{
    x->insert("-e", EnvironmentSpecified);
    x->insert("-s", SocketSpecified);
    x->insert("-v", PrintVersion);
    x->insert("-h", PrintUsage);
    x->insert("-d", DaemonMode);
    x->insert("-w", WindowsServiceMode);
    x->insert("-k", SendSignal);
})

static void usage()
{
    char text[] =
        "Usage: %1 [-d] [-e environment] [application-directory]\n"     \
        "Usage: %1 [-k stop|abort] [application-directory]\n"           \
        "Options:\n"                                                    \
        "  -d              : run as a daemon process\n"                 \
        "  -e environment  : specify an environment of the database settings\n" \
        "  -k              : send signal to a manager process\n\n"      \
        "Type '%1 -h' to show this information.\n"                      \
        "Type '%1 -v' to show the program version.";

    QString cmd = QFileInfo(QCoreApplication::applicationFilePath()).fileName();
    puts(qPrintable(QString(text).arg(cmd)));
}

static bool checkArguments()
{
    for (QStringListIterator i(QCoreApplication::arguments()); i.hasNext(); ) {
        const QString &arg = i.next();
        if (arg.startsWith('-') && options()->value(arg, Invalid) == Invalid) {
            fprintf(stderr, "invalid argument\n");
            return false;
        }
    }
    return true;
}

static bool startDaemon()
{
    bool success;
    QStringList args = QCoreApplication::arguments();
    args.removeAll("-d");
#ifdef Q_OS_WIN
    PROCESS_INFORMATION pinfo;
    STARTUPINFOW startupInfo = { sizeof(STARTUPINFO), 0, 0, 0,
                                 (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
                                 (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
                                 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
    };
    success = CreateProcess(0, (wchar_t*)args.join(" ").utf16(),
                            0, 0, FALSE, CREATE_UNICODE_ENVIRONMENT, 0,
                            (wchar_t*)QDir::currentPath().utf16(),
                            &startupInfo, &pinfo);
    if (success) {
        CloseHandle(pinfo.hThread);
        CloseHandle(pinfo.hProcess);
    }
#else
    args.removeFirst();  // Removes the program name
    success = QProcess::startDetached(QCoreApplication::applicationFilePath(), args, QDir::currentPath());
#endif

    return success;
}

static void writeStartupLog()
{
    tSystemInfo("TreeFrog Framework version " TF_VERSION_STR);

    QString qtversion = "Qt " QT_VERSION_STR;
#if defined(Q_OS_WIN)
    qtversion += QLatin1String(" / ") + winVersion()->value(QSysInfo::WindowsVersion, "Windows");
#elif defined(Q_OS_DARWIN)
    qtversion += QLatin1String(" / ") + macVersion()->value(QSysInfo::MacintoshVersion, "Mac OS X");
#elif defined(Q_OS_UNIX)
    struct utsname uts;
    if (uname(&uts) == 0) {
        qtversion += QString(" / %1 %2").arg(uts.sysname).arg(uts.release);
    }
#endif
    tSystemInfo("%s", qtversion.toLatin1().data());
}

static QString pidFilePath()
{
    QString base = QFileInfo(QCoreApplication::applicationFilePath()).baseName();
    return Tf::app()->tmpPath() + base + ".pid";
}

static qint64 readPidFileOfApplication()
{
    QFile pidf(pidFilePath());
    if (pidf.open(QIODevice::ReadOnly)) {
        qint64 pid = pidf.readLine(100).toLongLong();
        if (pid > 0) {
            return pid;
        }
    }
    return -1;
}

static qint64 runningApplicationPid()
{
    qint64 pid = readPidFileOfApplication();
    if (pid > 0) {
        QString name = ProcessInfo(pid).processName().toLower();
        if (name == "treefrog" || name == "treefrogd")
            return pid;
    }
    return -1;
}

static int killTreeFrogProcess(const QString &cmd)
{
    qint64 pid = runningApplicationPid();
    if (pid < 0) {
        printf("TreeFrog server not running\n");
        return 1;
    }

    ProcessInfo pi(pid);

    if (cmd == "stop") {  // stop command
        pi.terminate();
        if (pi.waitForTerminated()) {
            printf("TreeFrog application servers shutdown completed\n");
        } else {
            fprintf(stderr, "TreeFrog application servers shutdown failed\n");
        }

    } else if (cmd == "abort") {  // abort command
        pi.kill();
        tSystemInfo("Killed TreeFrog manager process  pid:%ld", (long)pid);
        ::unlink(pidFilePath().toLatin1().data());

        // kill all 'tadpole' processes
#if defined(Q_OS_WIN) && !defined(TF_NO_DEBUG)
        ProcessInfo::killProcesses("tadpoled");
#else
        ProcessInfo::killProcesses("tadpole");
#endif
        tSystemInfo("Killed TreeFrog application server processes");
        printf("Killed TreeFrog application server processes\n");

    } else if (cmd == "restart") {  // restart command
        pi.restart();
        printf("Sent a restart request\n");

    } else {
        usage();
        return 1;
    }
    return 0;
}

int managerMain(int argc, char *argv[])
{
    TWebApplication app(argc, argv);

    // Creates the semaphore for system log
    QSystemSemaphore semaphore("TreeFrogSystemLog", 1, QSystemSemaphore::Create);
    tSetupSystemLoggers();

#if defined(Q_OS_UNIX)
    app.watchUnixSignal(SIGTERM);
    app.watchUnixSignal(SIGINT);
    app.watchUnixSignal(SIGHUP);
#elif defined(Q_OS_WIN)
    app.watchConsoleSignal();
#endif

    // Sets codec
    QTextCodec *codec = QTextCodec::codecForName("UTF-8");
    QTextCodec::setCodecForLocale(codec);

    if (!checkArguments()) {
        return 1;
    }

    bool daemonMode = false;
    QString signalCmd;

    QStringList args = QCoreApplication::arguments();
    args.removeFirst();
    for (QStringListIterator i(args); i.hasNext(); ) {
        const QString &arg = i.next();
        int cmd = options()->value(arg, Invalid);
        switch (cmd) {
        case PrintVersion:
            printf("%s version " TF_VERSION_STR " (r%d) (built on %s)\n", qPrintable(QFileInfo(argv[0]).baseName()), TF_SRC_REVISION, __DATE__);
            return 0;
            break;

        case PrintUsage:
            usage();
            return 0;
            break;

        case DaemonMode:
            daemonMode = true;
            break;

        case WindowsServiceMode:
            // ignore
            break;

        case SendSignal:
            signalCmd = i.next(); // assign a command
            break;

        default:
            // do nothing
            break;
        }
    }

    if (!app.appSettingsFileExists()) {
        fprintf(stderr, "INI file not found [%s]\n\n", qPrintable(app.appSettingsFilePath()));
        return 1;
    }

    if (!signalCmd.isEmpty()) {
        return killTreeFrogProcess(signalCmd);
    }

    // Check TreeFrog processes are running
    qint64 pid = runningApplicationPid();
    if (pid > 0) {
        fprintf(stderr, "Already running  pid:%ld\n", (long)pid);
        return 1;
    }

    if (!app.isValidDatabaseSettings()) {
        tSystemError("Database settings not found  [environment: %s]", qPrintable(app.databaseEnvironment()));
        fprintf(stderr, "database settings not found  [environment: %s]\n\n", qPrintable(app.databaseEnvironment()));
        usage();
        return 1;
    }

    // Check a port number
    quint16 listenPort = 0;
    QString svrname = app.appSettings().value("ListenPort").toString();
    if (svrname.startsWith("unix:", Qt::CaseInsensitive)) {
        svrname.remove(0, 5);
    } else {
        int port = svrname.toInt();
        if (port <= 0 || port > USHRT_MAX) {
            tSystemError("Invalid port number: %d", port);
            return 1;
        }
        listenPort = port;
        svrname.clear();
    }

    // start daemon process
    if (daemonMode) {
        if ( !startDaemon() ) {
            fprintf(stderr, "Failed to create process\n\n");
            return 1;
        }
        return 0;
    }

    int ret = 0;
    QFile pidfile;

    for (;;) {
        ServerManager *manager = 0;
        switch ( app.multiProcessingModule() ) {
        case TWebApplication::Thread: {
            manager = new ServerManager(1, 1, 0, &app);
            break; }

        case TWebApplication::Prefork: {
            int max = app.appSettings().value("MPM.prefork.MaxServers").toInt();
            int min = app.appSettings().value("MPM.prefork.MinServers").toInt();
            int spare = app.appSettings().value("MPM.prefork.SpareServers").toInt();
            manager = new ServerManager(max, min, spare, &app);
            break; }

        default:
            tSystemError("Invalid MPM specified");
            return 1;
        }

        // Startup
        writeStartupLog();
        bool started;
        if (listenPort > 0) {
            // TCP/IP
            started = manager->start(QHostAddress::Any, listenPort);
        } else {
            // UNIX domain
            started = manager->start(svrname);
        }

        if (!started) {
            tSystemError("TreeFrog application server startup failed");
            fprintf(stderr, "TreeFrog application server startup failed\n\n");
            return 1;
        }

        // tmp directory
        QDir tmpDir(app.tmpPath());
        if (!tmpDir.exists()) {
            tmpDir.mkpath(".");
        }

        // Writes the PID
        pidfile.setFileName(pidFilePath());
        if (pidfile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
            pid = QCoreApplication::applicationPid();
            pidfile.write( QString::number(pid).toLatin1() );
            pidfile.close();
        } else {
            tSystemError("File open failed: %s", qPrintable(pidfile.fileName()));
        }

        ret = app.exec();
        tSystemDebug("tfmanager returnCode:%d", ret);
        manager->stop();

        if (ret == 1) {  // means SIGHUP
            tSystemDebug("Restarts TreeFrog application servers");
            //continue;
        } else {
            break;
        }
    }

    if (!svrname.isEmpty()) {  // UNIX domain file
        QFile(svrname).remove();
    }
    pidfile.remove();  // Removes the PID file
    return 0;
}

} // namespace TreeFrog

int main(int argc, char *argv[])
{
#ifdef Q_OS_WIN
    for (int i = 1; i < argc; ++i) {
        if (strcmp(argv[i], "-w") == 0) {
            // Windows service mode
            SERVICE_TABLE_ENTRY entry[] = { { (LPTSTR)TEXT(""), TreeFrog::winServiceMain }, { 0, 0 } };
            StartServiceCtrlDispatcher(entry);
            return 0;
        }
    }
#endif
    return TreeFrog::managerMain(argc, argv);
}
<|endoftext|>