<commit_before>/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the Qt Build Suite.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://www.qt.io/licensing.  For further information
** use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "transformer.h"

#include "artifact.h"
#include "command.h"
#include "rulesevaluationcontext.h"
#include <jsextensions/moduleproperties.h>
#include <language/language.h>
#include <language/scriptengine.h>
#include <logging/translator.h>
#include <tools/error.h>
#include <tools/persistence.h>
#include <tools/scripttools.h>
#include <tools/qbsassert.h>

#include <QDir>

namespace qbs {
namespace Internal {

Transformer::Transformer()
{
}

Transformer::~Transformer()
{
}

static QScriptValue js_baseName(QScriptContext *ctx, QScriptEngine *engine, void *arg)
{
    Q_UNUSED(ctx);
    Q_UNUSED(engine);
    Artifact *artifact = reinterpret_cast<Artifact *>(arg);
    return QScriptValue(FileInfo::baseName(artifact->filePath()));
}

static QScriptValue js_completeBaseName(QScriptContext *ctx, QScriptEngine *engine, void *arg)
{
    Q_UNUSED(ctx);
    Q_UNUSED(engine);
    Artifact *artifact = reinterpret_cast<Artifact *>(arg);
    return QScriptValue(FileInfo::completeBaseName(artifact->filePath()));
}

static QScriptValue js_baseDir(QScriptContext *ctx, QScriptEngine *engine, void *arg)
{
    Q_UNUSED(ctx);
    Q_UNUSED(engine);
    Artifact *artifact = reinterpret_cast<Artifact *>(arg);
    QString basedir;
    if (artifact->artifactType == Artifact::SourceFile) {
        QDir sourceDir(artifact->product->sourceDirectory);
        basedir = FileInfo::path(sourceDir.relativeFilePath(artifact->filePath()));
    } else {
        QDir buildDir(artifact->product->buildDirectory());
        basedir = FileInfo::path(buildDir.relativeFilePath(artifact->filePath()));
    }
    return basedir;
}

static void setArtifactProperty(QScriptValue &obj, const QString &name,
        QScriptEngine::FunctionWithArgSignature func, Artifact *artifact)
{
    obj.setProperty(name, obj.engine()->newFunction(func, (void *)artifact),
                    QScriptValue::PropertyGetter);
}

QScriptValue Transformer::translateFileConfig(QScriptEngine *scriptEngine, Artifact *artifact, const QString &defaultModuleName)
{
    QScriptValue obj = scriptEngine->newObject();
    attachPointerTo(obj, artifact);
    ModuleProperties::init(obj, artifact);
    obj.setProperty(QLatin1String("fileName"), artifact->fileName());
    obj.setProperty(QLatin1String("filePath"), artifact->filePath());
    setArtifactProperty(obj, QLatin1String("baseName"), js_baseName, artifact);
    setArtifactProperty(obj, QLatin1String("completeBaseName"), js_completeBaseName, artifact);
    setArtifactProperty(obj, QLatin1String("baseDir"), js_baseDir, artifact);
    const QStringList fileTags = artifact->fileTags().toStringList();
    obj.setProperty(QLatin1String("fileTags"), scriptEngine->toScriptValue(fileTags));
    if (!defaultModuleName.isEmpty())
        obj.setProperty(QLatin1String("moduleName"), defaultModuleName);
    return obj;
}

QScriptValue Transformer::translateInOutputs(QScriptEngine *scriptEngine, const ArtifactSet &artifacts, const QString &defaultModuleName)
{
    typedef QMap<QString, QList<Artifact*> > TagArtifactsMap;
    TagArtifactsMap tagArtifactsMap;
    foreach (Artifact *artifact, artifacts)
        foreach (const FileTag &fileTag, artifact->fileTags())
            tagArtifactsMap[fileTag.toString()].append(artifact);

    QScriptValue jsTagFiles = scriptEngine->newObject();
    for (TagArtifactsMap::const_iterator tag = tagArtifactsMap.constBegin(); tag != tagArtifactsMap.constEnd(); ++tag) {
        const QList<Artifact*> &artifacts = tag.value();
        QScriptValue jsFileConfig = scriptEngine->newArray(artifacts.count());
        int i=0;
        foreach (Artifact *artifact, artifacts) {
            jsFileConfig.setProperty(i++, translateFileConfig(scriptEngine, artifact, defaultModuleName));
        }
        jsTagFiles.setProperty(tag.key(), jsFileConfig);
    }

    return jsTagFiles;
}

ResolvedProductPtr Transformer::product() const
{
    if (outputs.isEmpty())
        return ResolvedProductPtr();
    return (*outputs.begin())->product;
}

void Transformer::setupInputs(QScriptValue targetScriptValue, const ArtifactSet &inputs,
        const QString &defaultModuleName)
{
    QScriptEngine *const scriptEngine = targetScriptValue.engine();
    QScriptValue scriptValue = translateInOutputs(scriptEngine, inputs, defaultModuleName);
    targetScriptValue.setProperty(QLatin1String("inputs"), scriptValue);
    QScriptValue inputScriptValue;
    if (inputs.count() == 1) {
        Artifact *input = *inputs.begin();
        const FileTags &fileTags = input->fileTags();
        QBS_ASSERT(!fileTags.isEmpty(), return);
        QScriptValue inputsForFileTag = scriptValue.property(fileTags.begin()->toString());
        inputScriptValue = inputsForFileTag.property(0);
    }
    targetScriptValue.setProperty(QLatin1String("input"), inputScriptValue);
}

void Transformer::setupInputs(QScriptValue targetScriptValue)
{
    setupInputs(targetScriptValue, inputs, rule->module->name);
}

void Transformer::setupOutputs(QScriptEngine *scriptEngine, QScriptValue targetScriptValue)
{
    const QString &defaultModuleName = rule->module->name;
    QScriptValue scriptValue = translateInOutputs(scriptEngine, outputs, defaultModuleName);
    targetScriptValue.setProperty(QLatin1String("outputs"), scriptValue);
    QScriptValue outputScriptValue;
    if (outputs.count() == 1) {
        Artifact *output = *outputs.begin();
        const FileTags &fileTags = output->fileTags();
        QBS_ASSERT(!fileTags.isEmpty(), return);
        QScriptValue outputsForFileTag = scriptValue.property(fileTags.begin()->toString());
        outputScriptValue = outputsForFileTag.property(0);
    }
    targetScriptValue.setProperty(QLatin1String("output"), outputScriptValue);
}

static AbstractCommandPtr createCommandFromScriptValue(const QScriptValue &scriptValue,
                                                       const CodeLocation &codeLocation)
{
    AbstractCommandPtr cmdBase;
    if (scriptValue.isUndefined() || !scriptValue.isValid())
        return cmdBase;
    QString className = scriptValue.property(QLatin1String("className")).toString();
    if (className == QLatin1String("Command"))
        cmdBase = ProcessCommand::create();
    else if (className == QLatin1String("JavaScriptCommand"))
        cmdBase = JavaScriptCommand::create();
    if (cmdBase)
        cmdBase->fillFromScriptValue(&scriptValue, codeLocation);
    return cmdBase;
}

void Transformer::createCommands(const ScriptFunctionConstPtr &script,
        const RulesEvaluationContextPtr &evalContext, const QScriptValueList &args)
{
    ScriptEngine * const engine = evalContext->engine();
    if (!script->scriptFunction.isValid() || script->scriptFunction.engine() != engine) {
        script->scriptFunction = engine->evaluate(script->sourceCode);
        if (Q_UNLIKELY(!script->scriptFunction.isFunction()))
            throw ErrorInfo(Tr::tr("Invalid prepare script."), script->location);
    }

    QScriptValue scriptValue = script->scriptFunction.call(QScriptValue(), args);
    propertiesRequestedInPrepareScript = engine->propertiesRequestedInScript();
    propertiesRequestedFromArtifactInPrepareScript = engine->propertiesRequestedFromArtifact();
    engine->clearRequestedProperties();
    if (Q_UNLIKELY(engine->hasErrorOrException(scriptValue)))
        throw ErrorInfo(Tr::tr("evaluating prepare script: ")
                        + engine->uncaughtException().toString(),
                    CodeLocation(script->location.fileName(),
                                 script->location.line() + engine->uncaughtExceptionLineNumber() - 1));

    commands.clear();
    if (scriptValue.isArray()) {
        const int count = scriptValue.property(QLatin1String("length")).toInt32();
        for (qint32 i = 0; i < count; ++i) {
            QScriptValue item = scriptValue.property(i);
            if (item.isValid() && !item.isUndefined()) {
                const AbstractCommandPtr cmd = createCommandFromScriptValue(item, script->location);
                if (cmd)
                    commands += cmd;
            }
        }
    } else {
        const AbstractCommandPtr cmd = createCommandFromScriptValue(scriptValue, script->location);
        if (cmd)
            commands += cmd;
    }
}

static void restorePropertyList(PersistentPool &pool, PropertyList &list)
{
    int count;
    pool.stream() >> count;
    list.reserve(count);
    while (--count >= 0) {
        Property p;
        p.moduleName = pool.idLoadString();
        p.propertyName = pool.idLoadString();
        int k;
        pool.stream() >> p.value >> k;
        p.kind = static_cast<Property::Kind>(k);
        list += p;
    }
}

void Transformer::load(PersistentPool &pool)
{
    rule = pool.idLoadS<Rule>();
    pool.loadContainer(inputs);
    pool.loadContainer(outputs);
    restorePropertyList(pool, propertiesRequestedInPrepareScript);
    restorePropertyList(pool, propertiesRequestedInCommands);
    int count;
    pool.stream() >> count;
    propertiesRequestedFromArtifactInPrepareScript.reserve(count);
    while (--count >= 0) {
        const QString artifactName = pool.idLoadString();
        int listCount;
        pool.stream() >> listCount;
        PropertyList list;
        list.reserve(listCount);
        while (--listCount >= 0) {
            Property p;
            p.moduleName = pool.idLoadString();
            p.propertyName = pool.idLoadString();
            pool.stream() >> p.value;
            p.kind = Property::PropertyInModule;
            list += p;
        }
        propertiesRequestedFromArtifactInPrepareScript.insert(artifactName, list);
    }
    commands = loadCommandList(pool);
}

static void storePropertyList(PersistentPool &pool, const PropertyList &list)
{
    pool.stream() << list.count();
    foreach (const Property &p, list) {
        pool.storeString(p.moduleName);
        pool.storeString(p.propertyName);
        pool.stream() << p.value << static_cast<int>(p.kind);
    }
}

void Transformer::store(PersistentPool &pool) const
{
    pool.store(rule);
    pool.storeContainer(inputs);
    pool.storeContainer(outputs);
    storePropertyList(pool, propertiesRequestedInPrepareScript);
    storePropertyList(pool, propertiesRequestedInCommands);
    pool.stream() << propertiesRequestedFromArtifactInPrepareScript.count();
    for (QHash<QString, PropertyList>::ConstIterator it = propertiesRequestedFromArtifactInPrepareScript.constBegin();
         it != propertiesRequestedFromArtifactInPrepareScript.constEnd(); ++it) {
        pool.storeString(it.key());
        const PropertyList &properties = it.value();
        pool.stream() << properties.count();
        foreach (const Property &p, properties) {
            pool.storeString(p.moduleName);
            pool.storeString(p.propertyName);
            pool.stream() << p.value; // kind is always PropertyInModule
        }
    }
    storeCommandList(commands, pool);
}

} // namespace Internal
} // namespace qbs
<commit_msg>Sort inputs and outputs before setting up the respective JS lists.<commit_after>/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the Qt Build Suite.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://www.qt.io/licensing.  For further information
** use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "transformer.h"

#include "artifact.h"
#include "command.h"
#include "rulesevaluationcontext.h"
#include <jsextensions/moduleproperties.h>
#include <language/language.h>
#include <language/scriptengine.h>
#include <logging/translator.h>
#include <tools/error.h>
#include <tools/persistence.h>
#include <tools/scripttools.h>
#include <tools/qbsassert.h>

#include <QDir>

#include <algorithm>

namespace qbs {
namespace Internal {

Transformer::Transformer()
{
}

Transformer::~Transformer()
{
}

static QScriptValue js_baseName(QScriptContext *ctx, QScriptEngine *engine, void *arg)
{
    Q_UNUSED(ctx);
    Q_UNUSED(engine);
    Artifact *artifact = reinterpret_cast<Artifact *>(arg);
    return QScriptValue(FileInfo::baseName(artifact->filePath()));
}

static QScriptValue js_completeBaseName(QScriptContext *ctx, QScriptEngine *engine, void *arg)
{
    Q_UNUSED(ctx);
    Q_UNUSED(engine);
    Artifact *artifact = reinterpret_cast<Artifact *>(arg);
    return QScriptValue(FileInfo::completeBaseName(artifact->filePath()));
}

static QScriptValue js_baseDir(QScriptContext *ctx, QScriptEngine *engine, void *arg)
{
    Q_UNUSED(ctx);
    Q_UNUSED(engine);
    Artifact *artifact = reinterpret_cast<Artifact *>(arg);
    QString basedir;
    if (artifact->artifactType == Artifact::SourceFile) {
        QDir sourceDir(artifact->product->sourceDirectory);
        basedir = FileInfo::path(sourceDir.relativeFilePath(artifact->filePath()));
    } else {
        QDir buildDir(artifact->product->buildDirectory());
        basedir = FileInfo::path(buildDir.relativeFilePath(artifact->filePath()));
    }
    return basedir;
}

static void setArtifactProperty(QScriptValue &obj, const QString &name,
        QScriptEngine::FunctionWithArgSignature func, Artifact *artifact)
{
    obj.setProperty(name, obj.engine()->newFunction(func, (void *)artifact),
                    QScriptValue::PropertyGetter);
}

QScriptValue Transformer::translateFileConfig(QScriptEngine *scriptEngine, Artifact *artifact, const QString &defaultModuleName)
{
    QScriptValue obj = scriptEngine->newObject();
    attachPointerTo(obj, artifact);
    ModuleProperties::init(obj, artifact);
    obj.setProperty(QLatin1String("fileName"), artifact->fileName());
    obj.setProperty(QLatin1String("filePath"), artifact->filePath());
    setArtifactProperty(obj, QLatin1String("baseName"), js_baseName, artifact);
    setArtifactProperty(obj, QLatin1String("completeBaseName"), js_completeBaseName, artifact);
    setArtifactProperty(obj, QLatin1String("baseDir"), js_baseDir, artifact);
    const QStringList fileTags = artifact->fileTags().toStringList();
    obj.setProperty(QLatin1String("fileTags"), scriptEngine->toScriptValue(fileTags));
    if (!defaultModuleName.isEmpty())
        obj.setProperty(QLatin1String("moduleName"), defaultModuleName);
    return obj;
}

static bool compareByFilePath(const Artifact *a1, const Artifact *a2)
{
    return a1->filePath() < a2->filePath();
}

QScriptValue Transformer::translateInOutputs(QScriptEngine *scriptEngine, const ArtifactSet &artifacts, const QString &defaultModuleName)
{
    typedef QMap<QString, QList<Artifact*> > TagArtifactsMap;
    TagArtifactsMap tagArtifactsMap;
    foreach (Artifact *artifact, artifacts)
        foreach (const FileTag &fileTag, artifact->fileTags())
            tagArtifactsMap[fileTag.toString()].append(artifact);
    for (TagArtifactsMap::Iterator it = tagArtifactsMap.begin(); it != tagArtifactsMap.end(); ++it)
        std::sort(it.value().begin(), it.value().end(), compareByFilePath);

    QScriptValue jsTagFiles = scriptEngine->newObject();
    for (TagArtifactsMap::const_iterator tag = tagArtifactsMap.constBegin(); tag != tagArtifactsMap.constEnd(); ++tag) {
        const QList<Artifact*> &artifacts = tag.value();
        QScriptValue jsFileConfig = scriptEngine->newArray(artifacts.count());
        int i=0;
        foreach (Artifact *artifact, artifacts) {
            jsFileConfig.setProperty(i++, translateFileConfig(scriptEngine, artifact, defaultModuleName));
        }
        jsTagFiles.setProperty(tag.key(), jsFileConfig);
    }

    return jsTagFiles;
}

ResolvedProductPtr Transformer::product() const
{
    if (outputs.isEmpty())
        return ResolvedProductPtr();
    return (*outputs.begin())->product;
}

void Transformer::setupInputs(QScriptValue targetScriptValue, const ArtifactSet &inputs,
        const QString &defaultModuleName)
{
    QScriptEngine *const scriptEngine = targetScriptValue.engine();
    QScriptValue scriptValue = translateInOutputs(scriptEngine, inputs, defaultModuleName);
    targetScriptValue.setProperty(QLatin1String("inputs"), scriptValue);
    QScriptValue inputScriptValue;
    if (inputs.count() == 1) {
        Artifact *input = *inputs.begin();
        const FileTags &fileTags = input->fileTags();
        QBS_ASSERT(!fileTags.isEmpty(), return);
        QScriptValue inputsForFileTag = scriptValue.property(fileTags.begin()->toString());
        inputScriptValue = inputsForFileTag.property(0);
    }
    targetScriptValue.setProperty(QLatin1String("input"), inputScriptValue);
}

void Transformer::setupInputs(QScriptValue targetScriptValue)
{
    setupInputs(targetScriptValue, inputs, rule->module->name);
}

void Transformer::setupOutputs(QScriptEngine *scriptEngine, QScriptValue targetScriptValue)
{
    const QString &defaultModuleName = rule->module->name;
    QScriptValue scriptValue = translateInOutputs(scriptEngine, outputs, defaultModuleName);
    targetScriptValue.setProperty(QLatin1String("outputs"), scriptValue);
    QScriptValue outputScriptValue;
    if (outputs.count() == 1) {
        Artifact *output = *outputs.begin();
        const FileTags &fileTags = output->fileTags();
        QBS_ASSERT(!fileTags.isEmpty(), return);
        QScriptValue outputsForFileTag = scriptValue.property(fileTags.begin()->toString());
        outputScriptValue = outputsForFileTag.property(0);
    }
    targetScriptValue.setProperty(QLatin1String("output"), outputScriptValue);
}

static AbstractCommandPtr createCommandFromScriptValue(const QScriptValue &scriptValue,
                                                       const CodeLocation &codeLocation)
{
    AbstractCommandPtr cmdBase;
    if (scriptValue.isUndefined() || !scriptValue.isValid())
        return cmdBase;
    QString className = scriptValue.property(QLatin1String("className")).toString();
    if (className == QLatin1String("Command"))
        cmdBase = ProcessCommand::create();
    else if (className == QLatin1String("JavaScriptCommand"))
        cmdBase = JavaScriptCommand::create();
    if (cmdBase)
        cmdBase->fillFromScriptValue(&scriptValue, codeLocation);
    return cmdBase;
}

void Transformer::createCommands(const ScriptFunctionConstPtr &script,
        const RulesEvaluationContextPtr &evalContext, const QScriptValueList &args)
{
    ScriptEngine * const engine = evalContext->engine();
    if (!script->scriptFunction.isValid() || script->scriptFunction.engine() != engine) {
        script->scriptFunction = engine->evaluate(script->sourceCode);
        if (Q_UNLIKELY(!script->scriptFunction.isFunction()))
            throw ErrorInfo(Tr::tr("Invalid prepare script."), script->location);
    }

    QScriptValue scriptValue = script->scriptFunction.call(QScriptValue(), args);
    propertiesRequestedInPrepareScript = engine->propertiesRequestedInScript();
    propertiesRequestedFromArtifactInPrepareScript = engine->propertiesRequestedFromArtifact();
    engine->clearRequestedProperties();
    if (Q_UNLIKELY(engine->hasErrorOrException(scriptValue)))
        throw ErrorInfo(Tr::tr("evaluating prepare script: ")
                        + engine->uncaughtException().toString(),
                    CodeLocation(script->location.fileName(),
                                 script->location.line() + engine->uncaughtExceptionLineNumber() - 1));

    commands.clear();
    if (scriptValue.isArray()) {
        const int count = scriptValue.property(QLatin1String("length")).toInt32();
        for (qint32 i = 0; i < count; ++i) {
            QScriptValue item = scriptValue.property(i);
            if (item.isValid() && !item.isUndefined()) {
                const AbstractCommandPtr cmd = createCommandFromScriptValue(item, script->location);
                if (cmd)
                    commands += cmd;
            }
        }
    } else {
        const AbstractCommandPtr cmd = createCommandFromScriptValue(scriptValue, script->location);
        if (cmd)
            commands += cmd;
    }
}

static void restorePropertyList(PersistentPool &pool, PropertyList &list)
{
    int count;
    pool.stream() >> count;
    list.reserve(count);
    while (--count >= 0) {
        Property p;
        p.moduleName = pool.idLoadString();
        p.propertyName = pool.idLoadString();
        int k;
        pool.stream() >> p.value >> k;
        p.kind = static_cast<Property::Kind>(k);
        list += p;
    }
}

void Transformer::load(PersistentPool &pool)
{
    rule = pool.idLoadS<Rule>();
    pool.loadContainer(inputs);
    pool.loadContainer(outputs);
    restorePropertyList(pool, propertiesRequestedInPrepareScript);
    restorePropertyList(pool, propertiesRequestedInCommands);
    int count;
    pool.stream() >> count;
    propertiesRequestedFromArtifactInPrepareScript.reserve(count);
    while (--count >= 0) {
        const QString artifactName = pool.idLoadString();
        int listCount;
        pool.stream() >> listCount;
        PropertyList list;
        list.reserve(listCount);
        while (--listCount >= 0) {
            Property p;
            p.moduleName = pool.idLoadString();
            p.propertyName = pool.idLoadString();
            pool.stream() >> p.value;
            p.kind = Property::PropertyInModule;
            list += p;
        }
        propertiesRequestedFromArtifactInPrepareScript.insert(artifactName, list);
    }
    commands = loadCommandList(pool);
}

static void storePropertyList(PersistentPool &pool, const PropertyList &list)
{
    pool.stream() << list.count();
    foreach (const Property &p, list) {
        pool.storeString(p.moduleName);
        pool.storeString(p.propertyName);
        pool.stream() << p.value << static_cast<int>(p.kind);
    }
}

void Transformer::store(PersistentPool &pool) const
{
    pool.store(rule);
    pool.storeContainer(inputs);
    pool.storeContainer(outputs);
    storePropertyList(pool, propertiesRequestedInPrepareScript);
    storePropertyList(pool, propertiesRequestedInCommands);
    pool.stream() << propertiesRequestedFromArtifactInPrepareScript.count();
    for (QHash<QString, PropertyList>::ConstIterator it = propertiesRequestedFromArtifactInPrepareScript.constBegin();
         it != propertiesRequestedFromArtifactInPrepareScript.constEnd(); ++it) {
        pool.storeString(it.key());
        const PropertyList &properties = it.value();
        pool.stream() << properties.count();
        foreach (const Property &p, properties) {
            pool.storeString(p.moduleName);
            pool.storeString(p.propertyName);
            pool.stream() << p.value; // kind is always PropertyInModule
        }
    }
    storeCommandList(commands, pool);
}

} // namespace Internal
} // namespace qbs
<|endoftext|>


<commit_before>#include "taskqueuer.h"

WARNINGS_DISABLE
#include <QCoreApplication>
#include <QEventLoop>
#include <QVariant>
WARNINGS_ENABLE

#include "cmdlinetask.h"

struct TaskMeta
{
    CmdlineTask *task;
    bool         isExclusive;
    bool         isBackup;
};

TaskQueuer::TaskQueuer() : _threadPool(QThreadPool::globalInstance())
{
#ifdef QT_TESTLIB_LIB
    _fakeNextTask = false;
#endif
}

TaskQueuer::~TaskQueuer()
{
    // Wait up to 1 second to finish any background tasks
    _threadPool->waitForDone(1000);
    // Wait up to 1 second to delete objects scheduled with ->deleteLater()
    QCoreApplication::processEvents(QEventLoop::AllEvents, 1000);
}

void TaskQueuer::stopTasks(bool interrupt, bool running, bool queued)
{
    // Clear the queue first, to avoid starting a queued task
    // after already clearing the running task(s).
    if(queued)
    {
        while(!_taskQueue.isEmpty())
        {
            TaskMeta *   tm   = _taskQueue.dequeue();
            CmdlineTask *task = tm->task;
            if(task)
            {
                task->emitCanceled();
                task->deleteLater();
            }
        }
        emit message("Cleared queued tasks.");
    }

    // Deal with a running backup.
    if(interrupt)
    {
        // Sending a SIGQUIT will cause the tarsnap binary to
        // create a checkpoint.  Non-tarsnap binaries should be
        // receive a CmdlineTask::stop() instead of a SIGQUIT.
        if(!_runningTasks.isEmpty())
            _runningTasks.first()->task->sigquit();
        emit message("Interrupting current backup.");
    }

    // Stop running tasks.
    if(running)
    {
        for(TaskMeta *tm : _runningTasks)
        {
            CmdlineTask *task = tm->task;
            if(task)
                task->stop();
        }
        emit message("Stopped running tasks.");
    }
}

void TaskQueuer::queueTask(CmdlineTask *task, bool exclusive, bool isBackup)
{
    // Sanity check.
    Q_ASSERT(task != nullptr);

    // Create & initialize the TaskMeta object.
    TaskMeta *tm    = new TaskMeta;
    tm->task        = task;
    tm->isExclusive = exclusive;
    tm->isBackup    = isBackup;

    // Add to the queue and trigger starting a new task.
    _taskQueue.enqueue(tm);
    startTasks();
}

void TaskQueuer::startTasks()
{
    while(!_taskQueue.isEmpty() && !isExclusiveTaskRunning())
    {
        // Bail if the next task requires exclusive running.
        if(!_runningTasks.isEmpty() && _taskQueue.head()->isExclusive)
            return;

        startTask();
    }
}

void TaskQueuer::startTask()
{
    // Bail if there's nothing to do.
    if(_taskQueue.isEmpty())
        return;

    // Check for exclusive.
    if(isExclusiveTaskRunning())
        return;

    // Get a new task.
    TaskMeta *tm = _taskQueue.dequeue();

    // Set up the task ending.
    CmdlineTask *task = tm->task;
    connect(task, &CmdlineTask::dequeue, this, &TaskQueuer::dequeueTask);
    task->setAutoDelete(false);

    // Record this thread as "running", even though it hasn't actually
    // started yet.  QThreadPool::start() is non-blocking, and in fact
    // explicitly states that a QRunnable can be added to an internal
    // run queue if it's exceeded QThreadPoll::maxThreadCount().
    //
    // However, for the purpose of this TaskQueuer, the task should not
    // be recorded in our _taskQueue (because we've just dequeued()'d it).
    // The "strictly correct" solution would be to add a
    // _waitingForStart queue, and move items out of that queue when the
    // relevant CmdlineTask::started signal was emitted.  At the moment,
    // I don't think that step is necessary, but I might need to revisit
    // that decision later.
    _runningTasks.append(tm);

    // Start the task.
#ifdef QT_TESTLIB_LIB
    if(_fakeNextTask)
        task->fake();
#endif
    _threadPool->start(task);

    // Update the task numbers.
    bool backupTaskRunning = isBackupTaskRunning();
    emit numTasks(backupTaskRunning, _runningTasks.count(), _taskQueue.count());
}

void TaskQueuer::dequeueTask()
{
    // Get the task.
    CmdlineTask *task = qobject_cast<CmdlineTask *>(sender());

    // Sanity check.
    if(task == nullptr)
        return;

    // Clean up task.
    for(TaskMeta *tm : _runningTasks)
    {
        if(tm->task == task)
        {
            _runningTasks.removeOne(tm);
            delete tm;
            break;
        }
    }
    task->deleteLater();

    // Start another task(s) if applicable.
    startTasks();

    // Update the task numbers.
    bool backupTaskRunning = isBackupTaskRunning();
    emit numTasks(backupTaskRunning, _runningTasks.count(), _taskQueue.count());
}

bool TaskQueuer::isExclusiveTaskRunning()
{
    for(TaskMeta *tm : _runningTasks)
    {
        if(tm->isExclusive)
            return true;
    }
    return false;
}

bool TaskQueuer::isBackupTaskRunning()
{
    for(TaskMeta *tm : _runningTasks)
    {
        if(tm->isBackup)
            return true;
    }
    return false;
}

void TaskQueuer::getTaskInfo()
{
    bool backupTaskRunning = isBackupTaskRunning();
    emit taskInfo(backupTaskRunning, _runningTasks.count(), _taskQueue.count());
}

#ifdef QT_TESTLIB_LIB
void TaskQueuer::fakeNextTask()
{
    _fakeNextTask = true;
}

void TaskQueuer::waitUntilIdle()
{
    while(!(_taskQueue.isEmpty() && _runningTasks.isEmpty()))
        QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
}
#endif
<commit_msg>TaskQueuer: add missing doxygen comments<commit_after>#include "taskqueuer.h"

WARNINGS_DISABLE
#include <QCoreApplication>
#include <QEventLoop>
#include <QVariant>
WARNINGS_ENABLE

#include "cmdlinetask.h"

/*! Track info about tasks. */
struct TaskMeta
{
    /*! Actual task. */
    CmdlineTask *task;
    /*! Does this task need to be the only one happening? */
    bool isExclusive;
    /*! This is a "create archive" task? */
    bool isBackup;
};

TaskQueuer::TaskQueuer() : _threadPool(QThreadPool::globalInstance())
{
#ifdef QT_TESTLIB_LIB
    _fakeNextTask = false;
#endif
}

TaskQueuer::~TaskQueuer()
{
    // Wait up to 1 second to finish any background tasks
    _threadPool->waitForDone(1000);
    // Wait up to 1 second to delete objects scheduled with ->deleteLater()
    QCoreApplication::processEvents(QEventLoop::AllEvents, 1000);
}

void TaskQueuer::stopTasks(bool interrupt, bool running, bool queued)
{
    // Clear the queue first, to avoid starting a queued task
    // after already clearing the running task(s).
    if(queued)
    {
        while(!_taskQueue.isEmpty())
        {
            TaskMeta *   tm   = _taskQueue.dequeue();
            CmdlineTask *task = tm->task;
            if(task)
            {
                task->emitCanceled();
                task->deleteLater();
            }
        }
        emit message("Cleared queued tasks.");
    }

    // Deal with a running backup.
    if(interrupt)
    {
        // Sending a SIGQUIT will cause the tarsnap binary to
        // create a checkpoint.  Non-tarsnap binaries should be
        // receive a CmdlineTask::stop() instead of a SIGQUIT.
        if(!_runningTasks.isEmpty())
            _runningTasks.first()->task->sigquit();
        emit message("Interrupting current backup.");
    }

    // Stop running tasks.
    if(running)
    {
        for(TaskMeta *tm : _runningTasks)
        {
            CmdlineTask *task = tm->task;
            if(task)
                task->stop();
        }
        emit message("Stopped running tasks.");
    }
}

void TaskQueuer::queueTask(CmdlineTask *task, bool exclusive, bool isBackup)
{
    // Sanity check.
    Q_ASSERT(task != nullptr);

    // Create & initialize the TaskMeta object.
    TaskMeta *tm    = new TaskMeta;
    tm->task        = task;
    tm->isExclusive = exclusive;
    tm->isBackup    = isBackup;

    // Add to the queue and trigger starting a new task.
    _taskQueue.enqueue(tm);
    startTasks();
}

void TaskQueuer::startTasks()
{
    while(!_taskQueue.isEmpty() && !isExclusiveTaskRunning())
    {
        // Bail if the next task requires exclusive running.
        if(!_runningTasks.isEmpty() && _taskQueue.head()->isExclusive)
            return;

        startTask();
    }
}

void TaskQueuer::startTask()
{
    // Bail if there's nothing to do.
    if(_taskQueue.isEmpty())
        return;

    // Check for exclusive.
    if(isExclusiveTaskRunning())
        return;

    // Get a new task.
    TaskMeta *tm = _taskQueue.dequeue();

    // Set up the task ending.
    CmdlineTask *task = tm->task;
    connect(task, &CmdlineTask::dequeue, this, &TaskQueuer::dequeueTask);
    task->setAutoDelete(false);

    // Record this thread as "running", even though it hasn't actually
    // started yet.  QThreadPool::start() is non-blocking, and in fact
    // explicitly states that a QRunnable can be added to an internal
    // run queue if it's exceeded QThreadPoll::maxThreadCount().
    //
    // However, for the purpose of this TaskQueuer, the task should not
    // be recorded in our _taskQueue (because we've just dequeued()'d it).
    // The "strictly correct" solution would be to add a
    // _waitingForStart queue, and move items out of that queue when the
    // relevant CmdlineTask::started signal was emitted.  At the moment,
    // I don't think that step is necessary, but I might need to revisit
    // that decision later.
    _runningTasks.append(tm);

    // Start the task.
#ifdef QT_TESTLIB_LIB
    if(_fakeNextTask)
        task->fake();
#endif
    _threadPool->start(task);

    // Update the task numbers.
    bool backupTaskRunning = isBackupTaskRunning();
    emit numTasks(backupTaskRunning, _runningTasks.count(), _taskQueue.count());
}

void TaskQueuer::dequeueTask()
{
    // Get the task.
    CmdlineTask *task = qobject_cast<CmdlineTask *>(sender());

    // Sanity check.
    if(task == nullptr)
        return;

    // Clean up task.
    for(TaskMeta *tm : _runningTasks)
    {
        if(tm->task == task)
        {
            _runningTasks.removeOne(tm);
            delete tm;
            break;
        }
    }
    task->deleteLater();

    // Start another task(s) if applicable.
    startTasks();

    // Update the task numbers.
    bool backupTaskRunning = isBackupTaskRunning();
    emit numTasks(backupTaskRunning, _runningTasks.count(), _taskQueue.count());
}

bool TaskQueuer::isExclusiveTaskRunning()
{
    for(TaskMeta *tm : _runningTasks)
    {
        if(tm->isExclusive)
            return true;
    }
    return false;
}

bool TaskQueuer::isBackupTaskRunning()
{
    for(TaskMeta *tm : _runningTasks)
    {
        if(tm->isBackup)
            return true;
    }
    return false;
}

void TaskQueuer::getTaskInfo()
{
    bool backupTaskRunning = isBackupTaskRunning();
    emit taskInfo(backupTaskRunning, _runningTasks.count(), _taskQueue.count());
}

#ifdef QT_TESTLIB_LIB
void TaskQueuer::fakeNextTask()
{
    _fakeNextTask = true;
}

void TaskQueuer::waitUntilIdle()
{
    while(!(_taskQueue.isEmpty() && _runningTasks.isEmpty()))
        QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
}
#endif
<|endoftext|>


<commit_before>#include "05.hpp"

//////////////
// Includes //
#include "../parser.hpp"
#include "../json.hpp"

//////////
// Code //

// Testing the empty object.
bool testEmptyObject(const JValue& emptyObject) {
    if (!emptyObject.isObject())
        return true;

    auto map = emptyObject.jObject();
    int n = 0;

    for (auto it = map.begin(); it != map.end(); it++)
        n++;

    if (n > 0)
        return true;

    return false;
}

// Testing the simple object.
bool testSimpleObject(const JValue& simpleObject) {
    if (!simpleObject.isObject())
        return true;

    std::map<std::string, JValue> map = simpleObject.jObject();

    if (map.count("test") == 0)
        return true;

    if (!map["test"].isBool())
        return true;

    if (map["test"].jBool() != true)
        return true;

    return false;
}

// Testing the complex object.
bool testComplexObject(const JValue& complexObject) {
    return false;
}

// Attempting to test parseJSON for 'null' values.
int runTest05() {
    JValue emptyObject = parseJSON("{ }");
    JValue simpleObject = parseJSON("{ \"test\": true }");

    // TODO: Make the complex object a little less empty.
    JValue complexObject = parseJSON("{}");

    if (testEmptyObject(emptyObject))
        return 1;
    if (testSimpleObject(simpleObject))
        return 1;
    if (testComplexObject(complexObject))
        return 1;

    return 0;
}
<commit_msg>Started doing some complex JSON parsing stuff.<commit_after>#include "05.hpp"

//////////////
// Includes //
#include "../parser.hpp"
#include "../json.hpp"

//////////
// Code //

// Testing the empty object.
bool testEmptyObject(const JValue& emptyObject) {
    if (!emptyObject.isObject())
        return true;

    auto map = emptyObject.jObject();
    int n = 0;

    for (auto it = map.begin(); it != map.end(); it++)
        n++;

    if (n > 0)
        return true;

    return false;
}

// Testing the simple object.
bool testSimpleObject(const JValue& simpleObject) {
    if (!simpleObject.isObject())
        return true;

    std::map<std::string, JValue> map = simpleObject.jObject();

    if (map.count("test") == 0)
        return true;

    if (!map["test"].isBool())
        return true;

    if (map["test"].jBool() != true)
        return true;

    return false;
}

// Testing the complex object.
// Complex object structure:
// {
//   "_id": "549c7c943e7a710a50638c11",
//   "index": 0,
//   "guid": "a5847a1e-8897-4195-acd3-82d6270a68a1",
//   "isActive": false,
//   "balance": "$2,495.21",
//   "picture": "http://placehold.it/32x32",
//   "age": 25,
//   "eyeColor": "blue",
//   "name": "Kasey Bentely",
//   "gender": "female",
//   "company": "GINKOGENE",
//   "email": "kaseybentley@ginkogene.com",
//   "phone": "+1 (954) 503-2214",
//   "address": "806 Hull Street, Chalfant, California, 8749",
//   "about": "Sunt officia ex non tempor. Elit voluptate ea est aliquip laboris laborum in eu do elit reprehenderit ea. Irure proident dolore commodo tempor qui. Duis nostrud est reprehenderit ex est culpa commodo mollit excepteur sunt ex qui. Lorem id consectetur qui mollit. Anim commodo laborum eu do ea dolor laboris in proident Lorem sint anim.\r\n",
//   "registered": "2014-10-21T10:42:13 +04:00",
//   "latitude": 48.984494,
//   "longitude": -81.8415,
//   "tags": ["incididunt", "eu", "eiusmod", "nostrud", "tempor", "incididunt", "consectetur"],
//   "friends": [{ "id": 0, "name": "Snow Williamson" }, { "id": 1, "name": "Christy Mcfadden" }, { "id": 2, "name": "Cathy Stone" }],
//   "greeting": "Hello, Kasey Bentley! You have 6 unread messages.",
//   "favoriteFruit": "strawberry"
// }
bool testComplexObject(const JValue& complexObject) {
    if (!complexObject.isObject())
        return true;

    // TODO: Some actual testing here.

    return false;
}

// Attempting to test parseJSON for 'null' values.
int runTest05() {
    JValue emptyObject = parseJSON("{ }");
    JValue simpleObject = parseJSON("{ \"test\": true }");
    JValue complexObject = parseJSON("{ \"_id\": \"549c7c943e7a710a50638c11\", \"index\": 0, \"guid\": \"a5847a1e-8897-4195-acd3-82d6270a68a1\", \"isActive\": false, \"balance\": \"$2,495.21\", \"picture\": \"http://placehold.it/32x32\", \"age\": 25, \"eyeColor\": \"blue\", \"name\": \"Kasey Bentley\", \"gender\": \"female\", \"company\": \"GINKOGENE\", \"email\": \"kaseybentley@ginkogene.com\", \"phone\": \"+1 (954) 503-2214\", \"address\": \"806 Hull Street, Chalfant, California, 8749\", \"about\": \"Sunt officia ex non tempor. Elit voluptate ea est aliquip laboris laborum in eu do elit reprehenderit ea. Irure proident dolore commodo tempor qui. Duis nostrud est reprehenderit ex est culpa commodo mollit excepteur sunt ex qui. Lorem id consectetur qui mollit. Anim commodo laborum eu do ea dolor laboris in proident Lorem sint anim.\r\n\", \"registered\": \"2014-10-21T10:42:13 +04:00\", \"latitude\": 48.984494, \"longitude\": -81.8415, \"tags\": [ \"incididunt\", \"eu\", \"eiusmod\", \"nostrud\", \"tempor\", \"incididunt\", \"consectetur\" ], \"friends\": [ { \"id\": 0, \"name\": \"Snow Williamson\" }, { \"id\": 1, \"name\": \"Christy Mcfadden\" }, { \"id\": 2, \"name\": \"Cathy Stone\" } ], \"greeting\": \"Hello, Kasey Bentley! You have 6 unread messages.\", \"favoriteFruit\": \"strawberry\" }");

    if (testEmptyObject(emptyObject))
        return 1;
    if (testSimpleObject(simpleObject))
        return 1;
    if (testComplexObject(complexObject))
        return 1;

    return 0;
}
<|endoftext|>


<commit_before>// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
/*
 * Ceph - scalable distributed file system
 *
 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1, as published by the Free Software
 * Foundation.  See file COPYING.
 *
 */

#include "include/types.h"
#include "include/rados/librados.hpp"

using namespace librados;

#include <iostream>

#include <stdlib.h>
#include <time.h>

void buf_to_hex(const unsigned char *buf, int len, char *str)
{
  str[0] = '\0';
  for (int i = 0; i < len; i++) {
    sprintf(&str[i*2], "%02x", (int)buf[i]);
  }
}

class C_Watch : public WatchCtx {
public:
  C_Watch() {}
  void notify(uint8_t opcode, uint64_t ver, bufferlist& bl) {
    cout << "C_Watch::notify() opcode=" << (int)opcode << " ver=" << ver << std::endl;
  }
};

void testradospp_milestone(void)
{
  cout << "*** press enter to continue ***" << std::endl;
  getchar();
}

int main(int argc, const char **argv)
{
  Rados rados;
  if (rados.init(NULL) < 0) {
     cerr << "couldn't initialize rados!" << std::endl;
     exit(1);
  }

  if (rados.conf_read_file(NULL)) {
     cerr << "couldn't read configuration file." << std::endl;
     exit(1);
  }

  if (!rados.conf_set("config option that doesn't exist",
                     "some random value")) {
    printf("error: succeeded in setting nonexistent config option\n");
    exit(1);
  }
  if (rados.conf_set("log to stderr", "2")) {
    printf("error: error setting log_to_stderr\n");
    exit(1);
  }
  std::string tmp;
  if (rados.conf_get("log to stderr", tmp)) {
    printf("error: failed to read log_to_stderr from config\n");
    exit(1);
  }
  if (tmp[0] != '2') {
    printf("error: new setting for log_to_stderr failed to take effect.\n");
    exit(1);
  }

  if (rados.connect()) {
    printf("error connecting\n");
    exit(1);
  }

  cout << "rados_initialize completed" << std::endl;
  testradospp_milestone();

  time_t tm;
  bufferlist bl, bl2, blf;
  char buf[128];

  time(&tm);
  snprintf(buf, 128, "%s", ctime(&tm));
  bl.append(buf, strlen(buf));
  blf.append(buf, 16);

  const char *oid = "bar";

  IoCtx io_ctx;
  int r = rados.ioctx_create("data", io_ctx);
  cout << "ioctx_create result = " << r << std::endl;

  r = io_ctx.write(oid, bl, bl.length(), 0);
  uint64_t objver = io_ctx.get_last_version();
  cout << "io_ctx.write returned " << r << " last_ver=" << objver << std::endl;

  uint64_t stat_size;
  time_t stat_mtime;
  r = io_ctx.stat(oid, &stat_size, &stat_mtime);
  cout << "io_ctx.stat size = " << stat_size << " mtime = " << stat_mtime << std::endl;

  r = io_ctx.stat(oid, NULL, NULL);
  cout << "io_ctx.stat(does_not_exist) = " << r;

  uint64_t handle;
  C_Watch wc;
  r = io_ctx.watch(oid, objver, &handle, &wc);
  cout << "io_ctx.watch returned " << r << std::endl;

  testradospp_milestone();
  io_ctx.set_notify_timeout(7);
  bufferlist notify_bl;
  r = io_ctx.notify(oid, objver, notify_bl);
  cout << "io_ctx.notify returned " << r << std::endl;
  testradospp_milestone();

  r = io_ctx.notify(oid, objver, notify_bl);
  cout << "io_ctx.notify returned " << r << std::endl;
  testradospp_milestone();

  r = io_ctx.unwatch(oid, handle);
  cout << "io_ctx.unwatch returned " << r << std::endl;
  cout << "*** press enter to continue ***" << std::endl;
  testradospp_milestone();

  r = io_ctx.notify(oid, objver, notify_bl);
  cout << "io_ctx.notify returned " << r << std::endl;
  cout << "*** press enter to continue ***" << std::endl;
  testradospp_milestone();
  io_ctx.set_assert_version(objver);

  r = io_ctx.write(oid, bl, bl.length() - 1, 0);
  cout << "io_ctx.write returned " << r << std::endl;

  r = io_ctx.write(oid, bl, bl.length() - 2, 0);
  cout << "io_ctx.write returned " << r << std::endl;
  r = io_ctx.write(oid, bl, bl.length() - 3, 0);
  cout << "rados.write returned " << r << std::endl;
  r = io_ctx.append(oid, bl, bl.length());
  cout << "rados.write returned " << r << std::endl;
  r = io_ctx.write_full(oid, blf);
  cout << "rados.write_full returned " << r << std::endl;
  r = io_ctx.read(oid, bl, bl.length(), 0);
  cout << "rados.read returned " << r << std::endl;
  r = io_ctx.trunc(oid, 8);
  cout << "rados.trunc returned " << r << std::endl;
  r = io_ctx.read(oid, bl, bl.length(), 0);
  cout << "rados.read returned " << r << std::endl;
  r = io_ctx.exec(oid, "crypto", "md5", bl, bl2);
  cout << "exec returned " << r <<  " buf size=" << bl2.length() << std::endl;
  const unsigned char *md5 = (const unsigned char *)bl2.c_str();
  char md5_str[bl2.length()*2 + 1];
  buf_to_hex(md5, bl2.length(), md5_str);
  cout << "md5 result=" << md5_str << std::endl;

  r = io_ctx.exec(oid, "crypto", "sha1", bl, bl2);
  cout << "exec returned " << r << std::endl;
  const unsigned char *sha1 = (const unsigned char *)bl2.c_str();
  char sha1_str[bl2.length()*2 + 1];
  buf_to_hex(sha1, bl2.length(), sha1_str);
  cout << "sha1 result=" << sha1_str << std::endl;

  r = io_ctx.exec(oid, "acl", "set", bl, bl2);
  r = io_ctx.exec(oid, "acl", "get", bl, bl2);
  cout << "exec returned " << r << std::endl;
  if (bl2.length() > 0) {
    cout << "attr=" << bl2.c_str() << std::endl;
  }

  int size = io_ctx.read(oid, bl2, 128, 0);
  if (size <= 0) {
    cout << "failed to read oid " << oid << "." << std::endl;
    exit(1);
  }
  if (size > 4096) {
    cout << "read too many bytes from oid " << oid << "." << std::endl;
    exit(1);
  }
  char rbuf[size + 1];
  memcpy(rbuf, bl2.c_str(), size);
  rbuf[size] = '\0';
  cout << "read result='" << rbuf << "'" << std::endl;
  cout << "size=" << size << std::endl;

  const char *oid2 = "jjj10.rbd";
  r = io_ctx.exec(oid2, "rbd", "snap_list", bl, bl2);
  cout << "snap_list result=" << r << std::endl;
  r = io_ctx.exec(oid2, "rbd", "snap_add", bl, bl2);
  cout << "snap_add result=" << r << std::endl;

  if (r > 0) {
    char *s = bl2.c_str();
    for (int i=0; i<r; i++, s += strlen(s) + 1)
      cout << s << std::endl;
  }

  cout << "compound operation..." << std::endl;
  ObjectOperation o;
  o.write(0, bl);
  o.setxattr("foo", bl2);
  r = io_ctx.operate(oid, &o, &bl2);
  cout << "operate result=" << r << std::endl;

  cout << "iterating over objects..." << std::endl;
  int num_objs = 0;
  for (ObjectIterator iter = io_ctx.objects_begin();
       iter != io_ctx.objects_end(); ++iter) {
    num_objs++;
    cout << "'" << *iter << "'" << std::endl;
  }
  cout << "iterated over " << num_objs << " objects." << std::endl;
  map<string, bufferlist> attrset;
  io_ctx.getxattrs(oid, attrset);

  map<string, bufferlist>::iterator it;
  for (it = attrset.begin(); it != attrset.end(); ++it) {
    cout << "xattr: " << it->first << std::endl;
  }

  r = io_ctx.remove(oid);
  cout << "remove result=" << r << std::endl;
  rados.shutdown();

  return 0;
}

<commit_msg>testradospp: add version tests<commit_after>// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
/*
 * Ceph - scalable distributed file system
 *
 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1, as published by the Free Software
 * Foundation.  See file COPYING.
 *
 */

#include "include/types.h"
#include "include/rados/librados.hpp"

using namespace librados;

#include <iostream>

#include <errno.h>
#include <stdlib.h>
#include <time.h>

void buf_to_hex(const unsigned char *buf, int len, char *str)
{
  str[0] = '\0';
  for (int i = 0; i < len; i++) {
    sprintf(&str[i*2], "%02x", (int)buf[i]);
  }
}

class C_Watch : public WatchCtx {
public:
  C_Watch() {}
  void notify(uint8_t opcode, uint64_t ver, bufferlist& bl) {
    cout << "C_Watch::notify() opcode=" << (int)opcode << " ver=" << ver << std::endl;
  }
};

void testradospp_milestone(void)
{
  cout << "*** press enter to continue ***" << std::endl;
  getchar();
}

int main(int argc, const char **argv)
{
  Rados rados;
  if (rados.init(NULL) < 0) {
     cerr << "couldn't initialize rados!" << std::endl;
     exit(1);
  }

  if (rados.conf_read_file(NULL)) {
     cerr << "couldn't read configuration file." << std::endl;
     exit(1);
  }
  rados.conf_parse_argv(argc, argv);

  if (!rados.conf_set("config option that doesn't exist",
                     "some random value")) {
    printf("error: succeeded in setting nonexistent config option\n");
    exit(1);
  }
  if (rados.conf_set("log to stderr", "2")) {
    printf("error: error setting log_to_stderr\n");
    exit(1);
  }
  std::string tmp;
  if (rados.conf_get("log to stderr", tmp)) {
    printf("error: failed to read log_to_stderr from config\n");
    exit(1);
  }
  if (tmp[0] != '2') {
    printf("error: new setting for log_to_stderr failed to take effect.\n");
    exit(1);
  }

  if (rados.connect()) {
    printf("error connecting\n");
    exit(1);
  }

  cout << "rados_initialize completed" << std::endl;
  testradospp_milestone();

  time_t tm;
  bufferlist bl, bl2, blf;
  char buf[128];

  time(&tm);
  snprintf(buf, 128, "%s", ctime(&tm));
  bl.append(buf, strlen(buf));
  blf.append(buf, 16);

  const char *oid = "bar";

  IoCtx io_ctx;
  int r = rados.ioctx_create("data", io_ctx);
  cout << "ioctx_create result = " << r << std::endl;

  r = io_ctx.write(oid, bl, bl.length(), 0);
  uint64_t objver = io_ctx.get_last_version();
  assert(objver > 0);
  cout << "io_ctx.write returned " << r << " last_ver=" << objver << std::endl;

  uint64_t stat_size;
  time_t stat_mtime;
  r = io_ctx.stat(oid, &stat_size, &stat_mtime);
  cout << "io_ctx.stat size = " << stat_size << " mtime = " << stat_mtime << std::endl;

  r = io_ctx.stat(oid, NULL, NULL);
  cout << "io_ctx.stat(does_not_exist) = " << r;

  uint64_t handle;
  C_Watch wc;
  r = io_ctx.watch(oid, objver, &handle, &wc);
  cout << "io_ctx.watch returned " << r << std::endl;

  testradospp_milestone();
  io_ctx.set_notify_timeout(7);
  bufferlist notify_bl;
  r = io_ctx.notify(oid, objver, notify_bl);
  cout << "io_ctx.notify returned " << r << std::endl;
  testradospp_milestone();

  r = io_ctx.notify(oid, objver, notify_bl);
  cout << "io_ctx.notify returned " << r << std::endl;
  testradospp_milestone();

  r = io_ctx.unwatch(oid, handle);
  cout << "io_ctx.unwatch returned " << r << std::endl;
  cout << "*** press enter to continue ***" << std::endl;
  testradospp_milestone();

  r = io_ctx.notify(oid, objver, notify_bl);
  cout << "io_ctx.notify returned " << r << std::endl;
  cout << "*** press enter to continue ***" << std::endl;
  testradospp_milestone();
  io_ctx.set_assert_version(objver);

  r = io_ctx.write(oid, bl, bl.length() - 1, 0);
  cout << "io_ctx.write returned " << r << std::endl;

  r = io_ctx.write(oid, bl, bl.length() - 2, 0);
  cout << "io_ctx.write returned " << r << std::endl;
  r = io_ctx.write(oid, bl, bl.length() - 3, 0);
  cout << "rados.write returned " << r << std::endl;
  r = io_ctx.append(oid, bl, bl.length());
  cout << "rados.write returned " << r << std::endl;
  r = io_ctx.write_full(oid, blf);
  cout << "rados.write_full returned " << r << std::endl;
  r = io_ctx.read(oid, bl, bl.length(), 0);
  cout << "rados.read returned " << r << std::endl;
  r = io_ctx.trunc(oid, 8);
  cout << "rados.trunc returned " << r << std::endl;
  r = io_ctx.read(oid, bl, bl.length(), 0);
  cout << "rados.read returned " << r << std::endl;
  r = io_ctx.exec(oid, "crypto", "md5", bl, bl2);
  cout << "exec returned " << r <<  " buf size=" << bl2.length() << std::endl;
  const unsigned char *md5 = (const unsigned char *)bl2.c_str();
  char md5_str[bl2.length()*2 + 1];
  buf_to_hex(md5, bl2.length(), md5_str);
  cout << "md5 result=" << md5_str << std::endl;

  // test assert_version
  r = io_ctx.read(oid, bl, 0, 1);
  assert(r >= 0);
  uint64_t v = io_ctx.get_last_version();
  cout << oid << " version is " << v << std::endl;
  assert(v > 0);
  io_ctx.set_assert_version(v);
  r = io_ctx.read(oid, bl, 0, 1);
  assert(r >= 0);
  io_ctx.set_assert_version(v - 1);
  r = io_ctx.read(oid, bl, 0, 1);
  assert(r == -ERANGE);
  io_ctx.set_assert_version(v + 1);
  r = io_ctx.read(oid, bl, 0, 1);
  assert(r == -EOVERFLOW);

  // test assert_src_version
  const char *dest = "baz";
  r = io_ctx.read(oid, bl, 0, 1);
  assert(r >= 0);
  v = io_ctx.get_last_version();
  cout << oid << " version is " << v << std::endl;
  io_ctx.set_assert_src_version(oid, v);
  r = io_ctx.clone_range(dest, 0, oid, 0, 1);
  assert(r >= 0);
  io_ctx.set_assert_src_version(oid, v-1);
  r = io_ctx.clone_range(dest, 0, oid, 0, 1);
  assert(r == -ERANGE);
  io_ctx.set_assert_src_version(oid, v+1);
  r = io_ctx.clone_range(dest, 0, oid, 0, 1);
  assert(r == -EOVERFLOW);

  r = io_ctx.exec(oid, "crypto", "sha1", bl, bl2);
  cout << "exec returned " << r << std::endl;
  const unsigned char *sha1 = (const unsigned char *)bl2.c_str();
  char sha1_str[bl2.length()*2 + 1];
  buf_to_hex(sha1, bl2.length(), sha1_str);
  cout << "sha1 result=" << sha1_str << std::endl;

  r = io_ctx.exec(oid, "acl", "set", bl, bl2);
  r = io_ctx.exec(oid, "acl", "get", bl, bl2);
  cout << "exec returned " << r << std::endl;
  if (bl2.length() > 0) {
    cout << "attr=" << bl2.c_str() << std::endl;
  }

  int size = io_ctx.read(oid, bl2, 128, 0);
  if (size <= 0) {
    cout << "failed to read oid " << oid << "." << std::endl;
    exit(1);
  }
  if (size > 4096) {
    cout << "read too many bytes from oid " << oid << "." << std::endl;
    exit(1);
  }
  char rbuf[size + 1];
  memcpy(rbuf, bl2.c_str(), size);
  rbuf[size] = '\0';
  cout << "read result='" << rbuf << "'" << std::endl;
  cout << "size=" << size << std::endl;

  const char *oid2 = "jjj10.rbd";
  r = io_ctx.exec(oid2, "rbd", "snap_list", bl, bl2);
  cout << "snap_list result=" << r << std::endl;
  r = io_ctx.exec(oid2, "rbd", "snap_add", bl, bl2);
  cout << "snap_add result=" << r << std::endl;

  if (r > 0) {
    char *s = bl2.c_str();
    for (int i=0; i<r; i++, s += strlen(s) + 1)
      cout << s << std::endl;
  }

  cout << "compound operation..." << std::endl;
  ObjectOperation o;
  o.write(0, bl);
  o.setxattr("foo", bl2);
  r = io_ctx.operate(oid, &o, &bl2);
  cout << "operate result=" << r << std::endl;

  cout << "iterating over objects..." << std::endl;
  int num_objs = 0;
  for (ObjectIterator iter = io_ctx.objects_begin();
       iter != io_ctx.objects_end(); ++iter) {
    num_objs++;
    cout << "'" << *iter << "'" << std::endl;
  }
  cout << "iterated over " << num_objs << " objects." << std::endl;
  map<string, bufferlist> attrset;
  io_ctx.getxattrs(oid, attrset);

  map<string, bufferlist>::iterator it;
  for (it = attrset.begin(); it != attrset.end(); ++it) {
    cout << "xattr: " << it->first << std::endl;
  }

  r = io_ctx.remove(oid);
  cout << "remove result=" << r << std::endl;
  rados.shutdown();

  return 0;
}

<|endoftext|>


<commit_before>/*
 * Copyright 2016 Christian Lockley
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may obtain
 * a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

#include "watchdogd.hpp"
#include <semaphore.h>

#define MAX_WORKERS 16

static std::atomic_bool canceled = {false};

struct threadpool
{
	pthread_t thread;
	sem_t sem;
	void *(*func)(void*);
	void * arg;
	int active;
};

static struct threadpool threads[MAX_WORKERS] = {0};

static void *Worker(void *arg)
{
	struct threadpool * t = (struct threadpool *)arg;

	while (true) {
		sem_wait(&t->sem);
		__sync_synchronize();
		t->func(t->arg);

		__atomic_store_n(&t->active, 0, __ATOMIC_SEQ_CST);

		__sync_synchronize();
	}
	return NULL;
}

bool ThreadPoolNew(void)
{
	pthread_attr_t attr;
	pthread_attr_init(&attr);
	pthread_attr_setguardsize(&attr, 0);
	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
	pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN*2);
	for (size_t i = 0; i < MAX_WORKERS; i++) {
		sem_init(&threads[i].sem, 0, 0);
		pthread_create(&threads[i].thread, &attr, Worker, &threads[i]);
	}
	pthread_attr_destroy(&attr);
	return true;
}

bool ThreadPoolCancel(void)
{
	canceled = true;
	for (size_t i = 0; i < MAX_WORKERS; i++) {
		pthread_cancel(threads[i].thread);
	}
	return true;
}

bool ThreadPoolAddTask(void *(*entry)(void*), void * arg, bool retry)
{

	if (entry == NULL) {
		return false;
	}

	if (canceled == true) {
		return false;
	}

	do {
		for (size_t i = 0; i < MAX_WORKERS; i++) {
			if (__sync_val_compare_and_swap(&threads[i].active, 0, 1) == 0) {
				threads[i].func = entry;
				threads[i].arg = arg;
				__sync_synchronize();
				sem_post(&threads[i].sem);
				return true;
			}
		}
	} while (retry);

	return false;
}
<commit_msg>use union in thread pool struct<commit_after>/*
 * Copyright 2016 Christian Lockley
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may obtain
 * a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

#include "watchdogd.hpp"
#include <semaphore.h>

#define MAX_WORKERS 16

static std::atomic_bool canceled = {false};

struct threadpool
{
	pthread_t thread;
	sem_t sem;
	void *(*func)(void*);
	union {
		void * arg;
		int active;
	};
};

static struct threadpool threads[MAX_WORKERS] = {0};

static void *Worker(void *arg)
{
	struct threadpool * t = (struct threadpool *)arg;

	while (true) {
		sem_wait(&t->sem);
		__sync_synchronize();
		t->func(t->arg);

		__atomic_store_n(&t->active, 0, __ATOMIC_SEQ_CST);

		__sync_synchronize();
	}
	return NULL;
}

bool ThreadPoolNew(void)
{
	pthread_attr_t attr;
	pthread_attr_init(&attr);
	pthread_attr_setguardsize(&attr, 0);
	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
	pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN*2);
	for (size_t i = 0; i < MAX_WORKERS; i++) {
		sem_init(&threads[i].sem, 0, 0);
		pthread_create(&threads[i].thread, &attr, Worker, &threads[i]);
	}
	pthread_attr_destroy(&attr);
	return true;
}

bool ThreadPoolCancel(void)
{
	canceled = true;
	for (size_t i = 0; i < MAX_WORKERS; i++) {
		pthread_cancel(threads[i].thread);
	}
	return true;
}

bool ThreadPoolAddTask(void *(*entry)(void*), void * arg, bool retry)
{

	if (entry == NULL) {
		return false;
	}

	if (canceled == true) {
		return false;
	}

	do {
		for (size_t i = 0; i < MAX_WORKERS; i++) {
			if (__sync_val_compare_and_swap(&threads[i].active, 0, 1) == 0) {
				threads[i].func = entry;
				threads[i].arg = arg;
				__sync_synchronize();
				sem_post(&threads[i].sem);
				return true;
			}
		}
	} while (retry);

	return false;
}
<|endoftext|>


<commit_before>
#include <string>
#include <iostream>
#include <physfs.h>
#include <SDL.h>
#include <SDL_rwops.h>
#include <SDL_net.h>
#include <SDL_thread.h>
#include <scheme-private.h>
#include "res_path.h"

extern "C"
{
	pointer run_test(scheme* sc, pointer args);
	SDL_atomic_t schemeQuitAtomic;
}

namespace venk {

void load_scheme(scheme* sc, const std::string& fname) {
	std::string realfname = std::string("scheme/") +fname + std::string(".scm");
	Uint64 length = 0;
	char* source = file_contents(realfname.c_str(), &length);
	SDL_assert_always((source != nullptr) && (length > 0));
	if((source == nullptr) || (length == 0)) {
		std::cerr << "Could not load " << fname << std::endl;
		return;
	}
	SDL_RWops *rwop = SDL_RWFromMem((void*) source, length);
	int i = ++(sc->file_i);
	SDL_assert(i < MAXFIL);
	sc->load_stack[i].kind = port_sdl|port_input;
	sc->load_stack[i].rep.sdl.rwop = rwop;
	char *buffer =  (char*) sc->malloc(SOCKET_BUFFER_SIZE);
	if (buffer == NULL) {
		return;
	}
	sc->load_stack[i].rep.sdl.buffer = buffer;
	sc->load_stack[i].rep.sdl.start = buffer;
	sc->load_stack[i].rep.sdl.end = buffer;
	sc->nesting_stack[sc->file_i]=0;
	sc->loadport->_object._port=sc->load_stack+sc->file_i;
	pointer args = mk_integer(sc, sc->file_i);
	pointer proc = mk_proc(sc, OP_T0LVL);
	scheme_call(sc, proc, args);
	return;
}

int server(void *data) {

	scheme *sc = (scheme *) data;
	TCPsocket sd, csd; /* Socket descriptor, Client socket descriptor */
	IPaddress ip, *remoteIP;

	scheme_define(sc,sc->global_env,mk_symbol(sc,"run-test"),mk_foreign_func(sc, run_test));

	/* Resolving the host using NULL make network interface to listen */
	if(SDLNet_ResolveHost(&ip, nullptr, 2956) < 0) {
		std::cerr << "SDLNet_ResolveHost: " << SDLNet_GetError() << std::endl;
		return -1;
	}
	/* Open a connection with the IP provided (listen on the host's port) */
	if(!(sd = SDLNet_TCP_Open(&ip))) {
		std::cerr << "SDLNet_TCP_Open:" << SDLNet_GetError() << std::endl;
		return -1;
	}
	/* This check the sd if there is a pending connection.
	 * If there is one, accept that, and open a new socket for communicating */
	while((csd = SDLNet_TCP_Accept(sd)) == nullptr) {
		SDL_Delay(1);
	}
	/* Now we can communicate with the client using csd socket
	 * sd will remain opened waiting other connections */
	/* Get the remote address */
	if((remoteIP = SDLNet_TCP_GetPeerAddress(csd))) {
		/* Print the address, converting in the host format */
		std::cout << "Host connected: " << std::hex << SDLNet_Read32(&remoteIP->host) << ":" << std::dec <<  SDLNet_Read16(&remoteIP->port) << std::endl;
	} else {
		std::cerr << "SDLNet_TCP_GetPeerAddress: " << SDLNet_GetError() << std::endl;
	}
	scheme_load_string(sc, "(write \"SDL Scheme\") (newline)");
	scheme_set_port_net(sc, csd);
	scheme_load_socket(sc, csd);
	std::cout << "Terminate connection" << std::endl;
	/* Close the client socket */
	SDLNet_TCP_Close(csd);
	csd = nullptr;

	/* Close the server socket */
	SDLNet_TCP_Close(sd);
	return 0;
}

SDL_Thread* launch_server(scheme* sc) {
	SDL_AtomicSet(&schemeQuitAtomic, 0);
	SDL_Thread* threadID = SDL_CreateThread( server, "SchemePort", (void*) sc );
	return threadID;
}

void kill_server() {
	// signal that it's time to end
	SDL_AtomicSet(&schemeQuitAtomic, 1);
}

} // namespace venk

extern SDL_sem* global_lock;

extern "C"
{
	static int lock_held = 0;

	void hold_global_lock() {
		if (!lock_held) {
			SDL_SemWait(global_lock);
		}
		lock_held++;
	}

	void release_global_lock() {
		SDL_assert(lock_held != 0);
		lock_held--;
		if (!lock_held) {
			SDL_SemPost(global_lock);
		}
	}

	pointer run_test(scheme* sc, pointer args) {
		hold_global_lock();
		if (args == sc->NIL)
			return sc->F;
		if (is_string(pair_car(args))) {
			venk::load_scheme(sc, string_value(pair_car(args)));
		}
		release_global_lock();
		return sc->T;
	}
}
<commit_msg>Program shuts down properly if you dont connect to the server<commit_after>
#include <string>
#include <iostream>
#include <physfs.h>
#include <SDL.h>
#include <SDL_rwops.h>
#include <SDL_net.h>
#include <SDL_thread.h>
#include <scheme-private.h>
#include "res_path.h"

extern "C"
{
	pointer run_test(scheme* sc, pointer args);
	SDL_atomic_t schemeQuitAtomic;
}

namespace venk {

void load_scheme(scheme* sc, const std::string& fname) {
	std::string realfname = std::string("scheme/") +fname + std::string(".scm");
	Uint64 length = 0;
	char* source = file_contents(realfname.c_str(), &length);
	SDL_assert_always((source != nullptr) && (length > 0));
	if((source == nullptr) || (length == 0)) {
		std::cerr << "Could not load " << fname << std::endl;
		return;
	}
	SDL_RWops *rwop = SDL_RWFromMem((void*) source, length);
	int i = ++(sc->file_i);
	SDL_assert(i < MAXFIL);
	sc->load_stack[i].kind = port_sdl|port_input;
	sc->load_stack[i].rep.sdl.rwop = rwop;
	char *buffer =  (char*) sc->malloc(SOCKET_BUFFER_SIZE);
	if (buffer == NULL) {
		return;
	}
	sc->load_stack[i].rep.sdl.buffer = buffer;
	sc->load_stack[i].rep.sdl.start = buffer;
	sc->load_stack[i].rep.sdl.end = buffer;
	sc->nesting_stack[sc->file_i]=0;
	sc->loadport->_object._port=sc->load_stack+sc->file_i;
	pointer args = mk_integer(sc, sc->file_i);
	pointer proc = mk_proc(sc, OP_T0LVL);
	scheme_call(sc, proc, args);
	return;
}

int server(void *data) {

	scheme *sc = (scheme *) data;
	TCPsocket sd, csd; /* Socket descriptor, Client socket descriptor */
	IPaddress ip, *remoteIP;

	scheme_define(sc,sc->global_env,mk_symbol(sc,"run-test"),mk_foreign_func(sc, run_test));

	/* Resolving the host using NULL make network interface to listen */
	if(SDLNet_ResolveHost(&ip, nullptr, 2956) < 0) {
		std::cerr << "SDLNet_ResolveHost: " << SDLNet_GetError() << std::endl;
		return -1;
	}
	/* Open a connection with the IP provided (listen on the host's port) */
	if(!(sd = SDLNet_TCP_Open(&ip))) {
		std::cerr << "SDLNet_TCP_Open:" << SDLNet_GetError() << std::endl;
		return -1;
	}
	/* This check the sd if there is a pending connection.
	 * If there is one, accept that, and open a new socket for communicating */
	while(((csd = SDLNet_TCP_Accept(sd)) == nullptr) && (SDL_AtomicGet(&schemeQuitAtomic) == 0)) {
		SDL_Delay(1);
	}
	if (SDL_AtomicGet(&schemeQuitAtomic) != 0) {
		goto forget_it;
	}
	/* Now we can communicate with the client using csd socket
	 * sd will remain opened waiting other connections */
	/* Get the remote address */
	if((remoteIP = SDLNet_TCP_GetPeerAddress(csd))) {
		/* Print the address, converting in the host format */
		std::cout << "Host connected: " << std::hex << SDLNet_Read32(&remoteIP->host) << ":" << std::dec <<  SDLNet_Read16(&remoteIP->port) << std::endl;
	} else {
		std::cerr << "SDLNet_TCP_GetPeerAddress: " << SDLNet_GetError() << std::endl;
	}
	scheme_load_string(sc, "(write \"SDL Scheme\") (newline)");
	scheme_set_port_net(sc, csd);
	scheme_load_socket(sc, csd);
forget_it:
	std::cout << "Terminate connection" << std::endl;
	/* Close the client socket */
	SDLNet_TCP_Close(csd);
	csd = nullptr;

	/* Close the server socket */
	SDLNet_TCP_Close(sd);
	return 0;
}

SDL_Thread* launch_server(scheme* sc) {
	SDL_AtomicSet(&schemeQuitAtomic, 0);
	SDL_Thread* threadID = SDL_CreateThread( server, "SchemePort", (void*) sc );
	return threadID;
}

void kill_server() {
	// signal that it's time to end
	SDL_AtomicSet(&schemeQuitAtomic, 1);
}

} // namespace venk

extern SDL_sem* global_lock;

extern "C"
{
	static int lock_held = 0;

	void hold_global_lock() {
		if (!lock_held) {
			SDL_SemWait(global_lock);
		}
		lock_held++;
	}

	void release_global_lock() {
		SDL_assert(lock_held != 0);
		lock_held--;
		if (!lock_held) {
			SDL_SemPost(global_lock);
		}
	}

	pointer run_test(scheme* sc, pointer args) {
		hold_global_lock();
		if (args == sc->NIL)
			return sc->F;
		if (is_string(pair_car(args))) {
			venk::load_scheme(sc, string_value(pair_car(args)));
		}
		release_global_lock();
		return sc->T;
	}
}
<|endoftext|>