<commit_before>#include <QtCore/QDebug>
#include <QtCore/QTimer>

#include <QtDBus/QtDBus>

#include <QtTest/QtTest>

#include <TelepathyQt4/ConnectionCapabilities>
#include <TelepathyQt4/ConnectionManager>
#include <TelepathyQt4/Debug>
#include <TelepathyQt4/PendingReady>
#include <TelepathyQt4/PendingStringList>
#include <TelepathyQt4/PresenceSpec>

#include <telepathy-glib/dbus.h>
#include <telepathy-glib/debug.h>

#include <tests/lib/glib/simple-manager.h>
#include <tests/lib/glib/echo2/connection-manager.h>
#include <tests/lib/test.h>

using namespace Tp;

namespace
{

PresenceSpec getPresenceSpec(const PresenceSpecList &specs, const QString &status)
{
    Q_FOREACH (const PresenceSpec &spec, specs) {
        if (spec.presence().status() == status) {
            return spec;
        }
    }
    return PresenceSpec();
}

}

class TestCmBasics : public Test
{
    Q_OBJECT

public:
    TestCmBasics(QObject *parent = 0)
        : Test(parent), mCMService(0)
    { }

private Q_SLOTS:
    void initTestCase();
    void init();

    void testBasics();
    void testLegacy();

    void cleanup();
    void cleanupTestCase();

private:
    TpBaseConnectionManager *mCMService;
    Tp::ConnectionManagerPtr mCM;

    TpBaseConnectionManager *mCMServiceLegacy;
    Tp::ConnectionManagerPtr mCMLegacy;
};

void TestCmBasics::initTestCase()
{
    initTestCaseImpl();

    g_type_init();
    g_set_prgname("cm-basics");
    tp_debug_set_flags("all");
    dbus_g_bus_get(DBUS_BUS_STARTER, 0);

    mCMService = TP_BASE_CONNECTION_MANAGER(g_object_new(
        EXAMPLE_TYPE_ECHO_2_CONNECTION_MANAGER,
        NULL));
    QVERIFY(mCMService != 0);

    mCMServiceLegacy = TP_BASE_CONNECTION_MANAGER(g_object_new(
        TP_TESTS_TYPE_SIMPLE_CONNECTION_MANAGER,
        NULL));
    QVERIFY(mCMServiceLegacy != 0);

    QVERIFY(tp_base_connection_manager_register(mCMService));
    QVERIFY(tp_base_connection_manager_register(mCMServiceLegacy));
}

void TestCmBasics::init()
{
    initImpl();
}

void TestCmBasics::testBasics()
{
    mCM = ConnectionManager::create(QLatin1String("example_echo_2"));
    QCOMPARE(mCM->isReady(), false);

    QVERIFY(connect(mCM->becomeReady(),
                    SIGNAL(finished(Tp::PendingOperation *)),
                    SLOT(expectSuccessfulCall(Tp::PendingOperation *))));
    QCOMPARE(mLoop->exec(), 0);
    QCOMPARE(mCM->isReady(), true);

    // calling becomeReady() twice is a no-op
    QVERIFY(connect(mCM->becomeReady(),
                    SIGNAL(finished(Tp::PendingOperation *)),
                    SLOT(expectSuccessfulCall(Tp::PendingOperation *))));
    QCOMPARE(mLoop->exec(), 0);
    QCOMPARE(mCM->isReady(), true);

    QCOMPARE(mCM->interfaces(), QStringList());
    QCOMPARE(mCM->supportedProtocols(), QStringList() << QLatin1String("example"));

    QVERIFY(mCM->hasProtocol(QLatin1String("example")));
    QVERIFY(!mCM->hasProtocol(QLatin1String("not-there")));

    ProtocolInfo info = mCM->protocol(QLatin1String("example"));
    QVERIFY(info.isValid());

    QCOMPARE(info.cmName(), QLatin1String("example_echo_2"));
    QCOMPARE(info.name(), QLatin1String("example"));

    QCOMPARE(info.hasParameter(QLatin1String("account")), true);
    QCOMPARE(info.hasParameter(QLatin1String("not-there")), false);

    QCOMPARE(info.parameters().size(), 1);

    ProtocolParameter param = info.parameters().at(0);
    QCOMPARE(param.name(), QLatin1String("account"));
    QCOMPARE(static_cast<uint>(param.type()), static_cast<uint>(QVariant::String));
    QCOMPARE(param.defaultValue().isNull(), true);
    QCOMPARE(param.dbusSignature().signature(), QLatin1String("s"));
    QCOMPARE(param.isRequired(), true);
    QCOMPARE(param.isRequiredForRegistration(), true); // though it can't register!
    QCOMPARE(param.isSecret(), false);

    QVERIFY(param == QLatin1String("account"));

    QCOMPARE(info.canRegister(), false);

    QCOMPARE(info.capabilities().isSpecificToContact(), false);
    QCOMPARE(info.capabilities().textChatrooms(), false);
    QCOMPARE(info.capabilities().textChats(), true);
    QCOMPARE(info.capabilities().streamedMediaCalls(), false);
    QCOMPARE(info.capabilities().streamedMediaAudioCalls(), false);
    QCOMPARE(info.capabilities().streamedMediaVideoCalls(), false);
    QCOMPARE(info.capabilities().streamedMediaVideoCallsWithAudio(), false);
    QCOMPARE(info.capabilities().upgradingStreamedMediaCalls(), false);

    QCOMPARE(info.vcardField(), QLatin1String("x-telepathy-example"));
    QCOMPARE(info.englishName(), QLatin1String("Echo II example"));
    QCOMPARE(info.iconName(), QLatin1String("im-icq"));

    PresenceSpecList statuses = info.allowedPresenceStatuses();
    QCOMPARE(statuses.size(), 3);
    PresenceSpec spec;
    spec = getPresenceSpec(statuses, QLatin1String("offline"));
    QCOMPARE(spec.isValid(), true);
    QVERIFY(spec.presence().type() == ConnectionPresenceTypeOffline);
    QCOMPARE(spec.maySetOnSelf(), false);
    QCOMPARE(spec.canHaveStatusMessage(), false);
    spec = getPresenceSpec(statuses, QLatin1String("dnd"));
    QCOMPARE(spec.isValid(), true);
    QVERIFY(spec.presence().type() == ConnectionPresenceTypeBusy);
    QCOMPARE(spec.maySetOnSelf(), true);
    QCOMPARE(spec.canHaveStatusMessage(), false);
    spec = getPresenceSpec(statuses, QLatin1String("available"));
    QCOMPARE(spec.isValid(), true);
    QVERIFY(spec.presence().type() == ConnectionPresenceTypeAvailable);
    QCOMPARE(spec.maySetOnSelf(), true);
    QCOMPARE(spec.canHaveStatusMessage(), true);

    AvatarSpec avatarReqs = info.avatarRequirements();
    QStringList supportedMimeTypes = avatarReqs.supportedMimeTypes();
    supportedMimeTypes.sort();
    QCOMPARE(supportedMimeTypes,
             QStringList() << QLatin1String("image/gif") << QLatin1String("image/jpeg") <<
                              QLatin1String("image/png"));
    QCOMPARE(avatarReqs.minimumHeight(), (uint) 32);
    QCOMPARE(avatarReqs.maximumHeight(), (uint) 96);
    QCOMPARE(avatarReqs.recommendedHeight(), (uint) 64);
    QCOMPARE(avatarReqs.minimumWidth(), (uint) 32);
    QCOMPARE(avatarReqs.maximumWidth(), (uint) 96);
    QCOMPARE(avatarReqs.recommendedWidth(), (uint) 64);
    QCOMPARE(avatarReqs.maximumBytes(), (uint) 37748736);

    QCOMPARE(mCM->supportedProtocols(), QStringList() << QLatin1String("example"));
}

// Test for a CM which doesn't implement Protocol objects
void TestCmBasics::testLegacy()
{
    mCMLegacy = ConnectionManager::create(QLatin1String("simple"));
    QCOMPARE(mCMLegacy->isReady(), false);

    QVERIFY(connect(mCMLegacy->becomeReady(),
                    SIGNAL(finished(Tp::PendingOperation *)),
                    SLOT(expectSuccessfulCall(Tp::PendingOperation *))));
    QCOMPARE(mLoop->exec(), 0);
    QCOMPARE(mCMLegacy->isReady(), true);

    QCOMPARE(mCMLegacy->interfaces(), QStringList());
    QCOMPARE(mCMLegacy->supportedProtocols(), QStringList() << QLatin1String("simple"));

    QVERIFY(mCMLegacy->hasProtocol(QLatin1String("simple")));
    QVERIFY(!mCMLegacy->hasProtocol(QLatin1String("not-there")));

    ProtocolInfo info = mCMLegacy->protocol(QLatin1String("simple"));
    QVERIFY(info.isValid());

    QCOMPARE(info.cmName(), QLatin1String("simple"));
    QCOMPARE(info.name(), QLatin1String("simple"));

    QCOMPARE(info.hasParameter(QLatin1String("account")), true);
    QCOMPARE(info.hasParameter(QLatin1String("not-there")), false);

    QCOMPARE(info.parameters().size(), 1);

    ProtocolParameter param = info.parameters().at(0);
    QCOMPARE(param.name(), QLatin1String("account"));
    QCOMPARE(static_cast<uint>(param.type()), static_cast<uint>(QVariant::String));
    QCOMPARE(param.defaultValue().isNull(), true);
    QCOMPARE(param.dbusSignature().signature(), QLatin1String("s"));
    QCOMPARE(param.isRequired(), true);
    QCOMPARE(param.isRequiredForRegistration(), true);
    QCOMPARE(param.isSecret(), false);

    QVERIFY(param == QLatin1String("account"));

    QCOMPARE(info.canRegister(), false);

    // Protocol capabilities semantics is "an actual connection supports whatever I claim, or
    // less", so for a service with no actual Protocol implementation everything should be
    // assumed to be possible at this point
    QCOMPARE(info.capabilities().isSpecificToContact(), false);
    QCOMPARE(info.capabilities().textChatrooms(), true);
    QCOMPARE(info.capabilities().textChats(), true);
    QCOMPARE(info.capabilities().streamedMediaCalls(), true);
    QCOMPARE(info.capabilities().streamedMediaAudioCalls(), true);
    QCOMPARE(info.capabilities().streamedMediaVideoCalls(), true);
    QCOMPARE(info.capabilities().streamedMediaVideoCallsWithAudio(), true);
    QCOMPARE(info.capabilities().upgradingStreamedMediaCalls(), true);

    QCOMPARE(info.vcardField(), QLatin1String(""));
    QCOMPARE(info.englishName(), QLatin1String("Simple"));
    QCOMPARE(info.iconName(), QLatin1String("im-simple"));

    QCOMPARE(mCMLegacy->supportedProtocols(), QStringList() << QLatin1String("simple"));
}

// TODO add a test for the case of getting the information from a .manager file, and if possible,
// also for using the fallbacks for the CM::Protocols property not being present.

// TODO also one for CM::listNames()

void TestCmBasics::cleanup()
{
    mCM.reset();
    mCMLegacy.reset();

    cleanupImpl();
}

void TestCmBasics::cleanupTestCase()
{
    if (mCMService) {
        g_object_unref(mCMService);
        mCMService = 0;
    }

    if (mCMServiceLegacy) {
        g_object_unref(mCMServiceLegacy);
        mCMServiceLegacy = 0;
    }

    cleanupTestCaseImpl();
}

QTEST_MAIN(TestCmBasics)
#include "_gen/cm-basics.cpp.moc.hpp"
<commit_msg>cm-basics test: Add test for ConnectionManager::listNames().<commit_after>#include <tests/lib/test.h>

#include <tests/lib/glib/simple-manager.h>
#include <tests/lib/glib/echo2/connection-manager.h>

#include <TelepathyQt4/ConnectionCapabilities>
#include <TelepathyQt4/ConnectionManager>
#include <TelepathyQt4/PendingReady>
#include <TelepathyQt4/PendingStringList>
#include <TelepathyQt4/PresenceSpec>

#include <telepathy-glib/debug.h>

using namespace Tp;

namespace
{

PresenceSpec getPresenceSpec(const PresenceSpecList &specs, const QString &status)
{
    Q_FOREACH (const PresenceSpec &spec, specs) {
        if (spec.presence().status() == status) {
            return spec;
        }
    }
    return PresenceSpec();
}

}

class TestCmBasics : public Test
{
    Q_OBJECT

public:
    TestCmBasics(QObject *parent = 0)
        : Test(parent), mCMService(0)
    { }

protected Q_SLOTS:
    void expectListNamesFinished(Tp::PendingOperation *);

private Q_SLOTS:
    void initTestCase();
    void init();

    void testBasics();
    void testLegacy();
    void testListNames();

    void cleanup();
    void cleanupTestCase();

private:
    TpBaseConnectionManager *mCMService;
    Tp::ConnectionManagerPtr mCM;

    TpBaseConnectionManager *mCMServiceLegacy;
    Tp::ConnectionManagerPtr mCMLegacy;

    QStringList mCMNames;
};

void TestCmBasics::expectListNamesFinished(PendingOperation *op)
{
    if (!op->isFinished()) {
        qWarning() << "unfinished";
        mLoop->exit(1);
        return;
    }

    if (op->isError()) {
        qWarning().nospace() << op->errorName()
            << ": " << op->errorMessage();
        mLoop->exit(2);
        return;
    }

    if (!op->isValid()) {
        qWarning() << "inconsistent results";
        mLoop->exit(3);
        return;
    }

    PendingStringList *ps = qobject_cast<PendingStringList*>(op);
    mCMNames = ps->result();
    mLoop->exit(0);
}

void TestCmBasics::initTestCase()
{
    initTestCaseImpl();

    g_type_init();
    g_set_prgname("cm-basics");
    tp_debug_set_flags("all");
    dbus_g_bus_get(DBUS_BUS_STARTER, 0);

    mCMService = TP_BASE_CONNECTION_MANAGER(g_object_new(
        EXAMPLE_TYPE_ECHO_2_CONNECTION_MANAGER,
        NULL));
    QVERIFY(mCMService != 0);

    mCMServiceLegacy = TP_BASE_CONNECTION_MANAGER(g_object_new(
        TP_TESTS_TYPE_SIMPLE_CONNECTION_MANAGER,
        NULL));
    QVERIFY(mCMServiceLegacy != 0);

    QVERIFY(tp_base_connection_manager_register(mCMService));
    QVERIFY(tp_base_connection_manager_register(mCMServiceLegacy));
}

void TestCmBasics::init()
{
    initImpl();
}

void TestCmBasics::testBasics()
{
    mCM = ConnectionManager::create(QLatin1String("example_echo_2"));
    QCOMPARE(mCM->isReady(), false);

    QVERIFY(connect(mCM->becomeReady(),
                    SIGNAL(finished(Tp::PendingOperation *)),
                    SLOT(expectSuccessfulCall(Tp::PendingOperation *))));
    QCOMPARE(mLoop->exec(), 0);
    QCOMPARE(mCM->isReady(), true);

    // calling becomeReady() twice is a no-op
    QVERIFY(connect(mCM->becomeReady(),
                    SIGNAL(finished(Tp::PendingOperation *)),
                    SLOT(expectSuccessfulCall(Tp::PendingOperation *))));
    QCOMPARE(mLoop->exec(), 0);
    QCOMPARE(mCM->isReady(), true);

    QCOMPARE(mCM->interfaces(), QStringList());
    QCOMPARE(mCM->supportedProtocols(), QStringList() << QLatin1String("example"));

    QVERIFY(mCM->hasProtocol(QLatin1String("example")));
    QVERIFY(!mCM->hasProtocol(QLatin1String("not-there")));

    ProtocolInfo info = mCM->protocol(QLatin1String("example"));
    QVERIFY(info.isValid());

    QCOMPARE(info.cmName(), QLatin1String("example_echo_2"));
    QCOMPARE(info.name(), QLatin1String("example"));

    QCOMPARE(info.hasParameter(QLatin1String("account")), true);
    QCOMPARE(info.hasParameter(QLatin1String("not-there")), false);

    QCOMPARE(info.parameters().size(), 1);

    ProtocolParameter param = info.parameters().at(0);
    QCOMPARE(param.name(), QLatin1String("account"));
    QCOMPARE(static_cast<uint>(param.type()), static_cast<uint>(QVariant::String));
    QCOMPARE(param.defaultValue().isNull(), true);
    QCOMPARE(param.dbusSignature().signature(), QLatin1String("s"));
    QCOMPARE(param.isRequired(), true);
    QCOMPARE(param.isRequiredForRegistration(), true); // though it can't register!
    QCOMPARE(param.isSecret(), false);

    QVERIFY(param == QLatin1String("account"));

    QCOMPARE(info.canRegister(), false);

    QCOMPARE(info.capabilities().isSpecificToContact(), false);
    QCOMPARE(info.capabilities().textChatrooms(), false);
    QCOMPARE(info.capabilities().textChats(), true);
    QCOMPARE(info.capabilities().streamedMediaCalls(), false);
    QCOMPARE(info.capabilities().streamedMediaAudioCalls(), false);
    QCOMPARE(info.capabilities().streamedMediaVideoCalls(), false);
    QCOMPARE(info.capabilities().streamedMediaVideoCallsWithAudio(), false);
    QCOMPARE(info.capabilities().upgradingStreamedMediaCalls(), false);

    QCOMPARE(info.vcardField(), QLatin1String("x-telepathy-example"));
    QCOMPARE(info.englishName(), QLatin1String("Echo II example"));
    QCOMPARE(info.iconName(), QLatin1String("im-icq"));

    PresenceSpecList statuses = info.allowedPresenceStatuses();
    QCOMPARE(statuses.size(), 3);
    PresenceSpec spec;
    spec = getPresenceSpec(statuses, QLatin1String("offline"));
    QCOMPARE(spec.isValid(), true);
    QVERIFY(spec.presence().type() == ConnectionPresenceTypeOffline);
    QCOMPARE(spec.maySetOnSelf(), false);
    QCOMPARE(spec.canHaveStatusMessage(), false);
    spec = getPresenceSpec(statuses, QLatin1String("dnd"));
    QCOMPARE(spec.isValid(), true);
    QVERIFY(spec.presence().type() == ConnectionPresenceTypeBusy);
    QCOMPARE(spec.maySetOnSelf(), true);
    QCOMPARE(spec.canHaveStatusMessage(), false);
    spec = getPresenceSpec(statuses, QLatin1String("available"));
    QCOMPARE(spec.isValid(), true);
    QVERIFY(spec.presence().type() == ConnectionPresenceTypeAvailable);
    QCOMPARE(spec.maySetOnSelf(), true);
    QCOMPARE(spec.canHaveStatusMessage(), true);

    AvatarSpec avatarReqs = info.avatarRequirements();
    QStringList supportedMimeTypes = avatarReqs.supportedMimeTypes();
    supportedMimeTypes.sort();
    QCOMPARE(supportedMimeTypes,
             QStringList() << QLatin1String("image/gif") << QLatin1String("image/jpeg") <<
                              QLatin1String("image/png"));
    QCOMPARE(avatarReqs.minimumHeight(), (uint) 32);
    QCOMPARE(avatarReqs.maximumHeight(), (uint) 96);
    QCOMPARE(avatarReqs.recommendedHeight(), (uint) 64);
    QCOMPARE(avatarReqs.minimumWidth(), (uint) 32);
    QCOMPARE(avatarReqs.maximumWidth(), (uint) 96);
    QCOMPARE(avatarReqs.recommendedWidth(), (uint) 64);
    QCOMPARE(avatarReqs.maximumBytes(), (uint) 37748736);

    QCOMPARE(mCM->supportedProtocols(), QStringList() << QLatin1String("example"));
}

// Test for a CM which doesn't implement Protocol objects
void TestCmBasics::testLegacy()
{
    mCMLegacy = ConnectionManager::create(QLatin1String("simple"));
    QCOMPARE(mCMLegacy->isReady(), false);

    QVERIFY(connect(mCMLegacy->becomeReady(),
                    SIGNAL(finished(Tp::PendingOperation *)),
                    SLOT(expectSuccessfulCall(Tp::PendingOperation *))));
    QCOMPARE(mLoop->exec(), 0);
    QCOMPARE(mCMLegacy->isReady(), true);

    QCOMPARE(mCMLegacy->interfaces(), QStringList());
    QCOMPARE(mCMLegacy->supportedProtocols(), QStringList() << QLatin1String("simple"));

    QVERIFY(mCMLegacy->hasProtocol(QLatin1String("simple")));
    QVERIFY(!mCMLegacy->hasProtocol(QLatin1String("not-there")));

    ProtocolInfo info = mCMLegacy->protocol(QLatin1String("simple"));
    QVERIFY(info.isValid());

    QCOMPARE(info.cmName(), QLatin1String("simple"));
    QCOMPARE(info.name(), QLatin1String("simple"));

    QCOMPARE(info.hasParameter(QLatin1String("account")), true);
    QCOMPARE(info.hasParameter(QLatin1String("not-there")), false);

    QCOMPARE(info.parameters().size(), 1);

    ProtocolParameter param = info.parameters().at(0);
    QCOMPARE(param.name(), QLatin1String("account"));
    QCOMPARE(static_cast<uint>(param.type()), static_cast<uint>(QVariant::String));
    QCOMPARE(param.defaultValue().isNull(), true);
    QCOMPARE(param.dbusSignature().signature(), QLatin1String("s"));
    QCOMPARE(param.isRequired(), true);
    QCOMPARE(param.isRequiredForRegistration(), true);
    QCOMPARE(param.isSecret(), false);

    QVERIFY(param == QLatin1String("account"));

    QCOMPARE(info.canRegister(), false);

    // Protocol capabilities semantics is "an actual connection supports whatever I claim, or
    // less", so for a service with no actual Protocol implementation everything should be
    // assumed to be possible at this point
    QCOMPARE(info.capabilities().isSpecificToContact(), false);
    QCOMPARE(info.capabilities().textChatrooms(), true);
    QCOMPARE(info.capabilities().textChats(), true);
    QCOMPARE(info.capabilities().streamedMediaCalls(), true);
    QCOMPARE(info.capabilities().streamedMediaAudioCalls(), true);
    QCOMPARE(info.capabilities().streamedMediaVideoCalls(), true);
    QCOMPARE(info.capabilities().streamedMediaVideoCallsWithAudio(), true);
    QCOMPARE(info.capabilities().upgradingStreamedMediaCalls(), true);

    QCOMPARE(info.vcardField(), QLatin1String(""));
    QCOMPARE(info.englishName(), QLatin1String("Simple"));
    QCOMPARE(info.iconName(), QLatin1String("im-simple"));

    QCOMPARE(mCMLegacy->supportedProtocols(), QStringList() << QLatin1String("simple"));
}

// TODO add a test for the case of getting the information from a .manager file, and if possible,
// also for using the fallbacks for the CM::Protocols property not being present.

void TestCmBasics::testListNames()
{
    QVERIFY(connect(ConnectionManager::listNames(),
                SIGNAL(finished(Tp::PendingOperation*)),
                SLOT(expectListNamesFinished(Tp::PendingOperation*))));
    QCOMPARE(mLoop->exec(), 0);
    QCOMPARE(mCMNames.size(), 3);
    QVERIFY(mCMNames.contains(QLatin1String("simple")));
    QVERIFY(mCMNames.contains(QLatin1String("example_echo_2")));
    QVERIFY(mCMNames.contains(QLatin1String("spurious")));
}

void TestCmBasics::cleanup()
{
    mCM.reset();
    mCMLegacy.reset();

    cleanupImpl();
}

void TestCmBasics::cleanupTestCase()
{
    if (mCMService) {
        g_object_unref(mCMService);
        mCMService = 0;
    }

    if (mCMServiceLegacy) {
        g_object_unref(mCMServiceLegacy);
        mCMServiceLegacy = 0;
    }

    cleanupTestCaseImpl();
}

QTEST_MAIN(TestCmBasics)
#include "_gen/cm-basics.cpp.moc.hpp"
<|endoftext|>


<commit_before>/** \brief Utility for updating SQL schemata etc.
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *
 *  \copyright 2019 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <algorithm>
#include <iostream>
#include <utility>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include "Compiler.h"
#include "FileUtil.h"
#include "DbConnection.h"
#include "StringUtil.h"
#include "util.h"

namespace {

[[noreturn]] void Usage() {
    ::Usage("[--test] update_directory_path");
}

void SplitIntoDatabaseAndVersion(const std::string &update_filename, std::string * const database, unsigned * const version) {
    const auto first_dot_pos(update_filename.find('.'));
    if (first_dot_pos == std::string::npos or first_dot_pos == 0 or first_dot_pos == update_filename.length() - 1)
        LOG_ERROR("invalid update filename \"" + update_filename + "\"!");

    if (not StringUtil::ToUnsigned(update_filename.substr(first_dot_pos + 1), version))
        LOG_ERROR("bad or missing version in update filename \"" + update_filename + "\"!");

    *database = update_filename.substr(0, first_dot_pos);
}

// The filenames being compared are assumed to have the "structure database.version]*"
bool FileNameCompare(const std::string &filename1, const std::string &filename2) {
    std::string database1;
    unsigned version1;
    SplitIntoDatabaseAndVersion(filename1, &database1, &version1);

    std::string database2;
    unsigned version2;
    SplitIntoDatabaseAndVersion(filename2, &database2, &version2);

    // Compare database names:
    if (database1 < database2)
        return true;
    if (database1 > database2)
        return false;

    return version1 < version2;
}

void LoadAndSortUpdateFilenames(const bool test, const std::string &directory_path, std::vector<std::string> * const update_filenames) {
    FileUtil::Directory directory(directory_path, "[^.]+\\.\\d+");
    for (const auto &entry : directory)
        update_filenames->emplace_back(entry.getName());

    std::sort(update_filenames->begin(), update_filenames->end(), FileNameCompare);

    if (test) {
        std::cerr << "Sorted filenames:\n";
        for (const auto &filename : *update_filenames)
            std::cerr << filename << '\n';
        std::exit(0);
    }
}

void ApplyUpdate(DbConnection * const db_connection, const std::string &update_directory_path, const std::string &update_filename) {
    std::string database;
    unsigned update_version;
    SplitIntoDatabaseAndVersion(update_filename, &database, &update_version);

    if (not db_connection->mySQLDatabaseExists(database)) {
        LOG_INFO("database \"" + database + "\" does not exist, skipping file " + update_filename);
        return;
    }

    db_connection->queryOrDie("START TRANSACTION");

    unsigned current_version(0);
    db_connection->queryOrDie("SELECT version FROM ub_tools.database_versions WHERE database_name='"
                              + db_connection->escapeString(database) + "'");
    DbResultSet result_set(db_connection->getLastResultSet());
    if (result_set.empty()) {
        db_connection->queryOrDie("INSERT INTO ub_tools.database_versions (database_name,version) VALUES ('"
                                  + db_connection->escapeString(database) + "',0)");
            LOG_INFO("Created a new entry for database \"" + database + " in ub_tools.database_versions.");
    } else
        current_version = StringUtil::ToUnsigned(result_set.getNextRow()["version"]);
    if (update_version <= current_version)
        return;

    // Sanity check:
    if (unlikely(update_version != current_version + 1))
        LOG_ERROR("update version is " + std::to_string(update_version) + ", current version is "
                  + std::to_string(current_version) + " for database \"" + database + "\"!");

    db_connection->queryOrDie("UPDATE ub_tools.database_versions SET version=" + std::to_string(update_version)
                              + " WHERE database_name='" + db_connection->escapeString(database) + "'");

    LOG_INFO("applying update " + std::to_string(update_version) + " to database \"" + database + "\".");
    db_connection->queryFileOrDie(update_directory_path + "/" + update_filename);

    db_connection->queryOrDie("COMMIT");
}

} // unnamed namespace

int Main(int argc, char *argv[]) {
    if (argc != 2 and argc != 3)
        Usage();

    bool test(false);
    if (argc == 3) {
        if (std::strcmp(argv[1], "--test") != 0)
            Usage();
        test = true;
        --argc, ++argv;
    }

    std::vector<std::string> update_filenames;
    const std::string update_directory_path(argv[1]);
    LoadAndSortUpdateFilenames(test, update_directory_path, &update_filenames);

    DbConnection db_connection;
    const std::string system_table_name("database_versions");
    if (not db_connection.tableExists("ub_tools", system_table_name)) {
        db_connection.queryOrDie("CREATE TABLE ub_tools." + system_table_name + " (version INT UNSIGNED NOT NULL,"
                                 "database_name VARCHAR(64) NOT NULL,UNIQUE (database_name)) "
                                 "CHARACTER SET utf8mb4 COLLATE utf8mb4_bin");
        LOG_INFO("Created the ub_tools." + system_table_name + " table.");
    }

    for (const auto &update_filename : update_filenames)
        ApplyUpdate(&db_connection, update_directory_path, update_filename);

    return EXIT_SUCCESS;
}
<commit_msg>mysql_database_patcher: disable autocommit for transaction & optimize failure behaviour<commit_after>/** \brief Utility for updating SQL schemata etc.
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *
 *  \copyright 2019 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <algorithm>
#include <iostream>
#include <utility>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include "Compiler.h"
#include "FileUtil.h"
#include "DbConnection.h"
#include "StringUtil.h"
#include "util.h"

namespace {

[[noreturn]] void Usage() {
    ::Usage("[--test] update_directory_path");
}

void SplitIntoDatabaseAndVersion(const std::string &update_filename, std::string * const database, unsigned * const version) {
    const auto first_dot_pos(update_filename.find('.'));
    if (first_dot_pos == std::string::npos or first_dot_pos == 0 or first_dot_pos == update_filename.length() - 1)
        LOG_ERROR("invalid update filename \"" + update_filename + "\"!");

    if (not StringUtil::ToUnsigned(update_filename.substr(first_dot_pos + 1), version))
        LOG_ERROR("bad or missing version in update filename \"" + update_filename + "\"!");

    *database = update_filename.substr(0, first_dot_pos);
}

// The filenames being compared are assumed to have the "structure database.version]*"
bool FileNameCompare(const std::string &filename1, const std::string &filename2) {
    std::string database1;
    unsigned version1;
    SplitIntoDatabaseAndVersion(filename1, &database1, &version1);

    std::string database2;
    unsigned version2;
    SplitIntoDatabaseAndVersion(filename2, &database2, &version2);

    // Compare database names:
    if (database1 < database2)
        return true;
    if (database1 > database2)
        return false;

    return version1 < version2;
}

void LoadAndSortUpdateFilenames(const bool test, const std::string &directory_path, std::vector<std::string> * const update_filenames) {
    FileUtil::Directory directory(directory_path, "[^.]+\\.\\d+");
    for (const auto &entry : directory)
        update_filenames->emplace_back(entry.getName());

    std::sort(update_filenames->begin(), update_filenames->end(), FileNameCompare);

    if (test) {
        std::cerr << "Sorted filenames:\n";
        for (const auto &filename : *update_filenames)
            std::cerr << filename << '\n';
        std::exit(0);
    }
}

void ApplyUpdate(DbConnection * const db_connection, const std::string &update_directory_path, const std::string &update_filename) {
    std::string database;
    unsigned update_version;
    SplitIntoDatabaseAndVersion(update_filename, &database, &update_version);

    if (not db_connection->mySQLDatabaseExists(database)) {
        LOG_INFO("database \"" + database + "\" does not exist, skipping file " + update_filename);
        return;
    }

    db_connection->queryOrDie("SET autocommit=0");
    db_connection->queryOrDie("START TRANSACTION");

    unsigned current_version(0);
    db_connection->queryOrDie("SELECT version FROM ub_tools.database_versions WHERE database_name='"
                              + db_connection->escapeString(database) + "'");
    DbResultSet result_set(db_connection->getLastResultSet());
    if (result_set.empty()) {
        db_connection->queryOrDie("INSERT INTO ub_tools.database_versions (database_name,version) VALUES ('"
                                  + db_connection->escapeString(database) + "',0)");
            LOG_INFO("Created a new entry for database \"" + database + " in ub_tools.database_versions.");
    } else
        current_version = StringUtil::ToUnsigned(result_set.getNextRow()["version"]);
    if (update_version <= current_version)
        return;

    // Sanity check:
    if (unlikely(update_version != current_version + 1))
        LOG_ERROR("update version is " + std::to_string(update_version) + ", current version is "
                  + std::to_string(current_version) + " for database \"" + database + "\"!");

    LOG_INFO("applying update " + std::to_string(update_version) + " to database \"" + database + "\".");
    db_connection->queryFileOrDie(update_directory_path + "/" + update_filename);
    db_connection->queryOrDie("UPDATE ub_tools.database_versions SET version=" + std::to_string(update_version)
                              + " WHERE database_name='" + db_connection->escapeString(database) + "'");

    db_connection->queryOrDie("COMMIT");
    db_connection->queryOrDie("SET autocommit=1");
}

} // unnamed namespace

int Main(int argc, char *argv[]) {
    if (argc != 2 and argc != 3)
        Usage();

    bool test(false);
    if (argc == 3) {
        if (std::strcmp(argv[1], "--test") != 0)
            Usage();
        test = true;
        --argc, ++argv;
    }

    std::vector<std::string> update_filenames;
    const std::string update_directory_path(argv[1]);
    LoadAndSortUpdateFilenames(test, update_directory_path, &update_filenames);

    DbConnection db_connection;
    const std::string system_table_name("database_versions");
    if (not db_connection.tableExists("ub_tools", system_table_name)) {
        db_connection.queryOrDie("CREATE TABLE ub_tools." + system_table_name + " (version INT UNSIGNED NOT NULL,"
                                 "database_name VARCHAR(64) NOT NULL,UNIQUE (database_name)) "
                                 "CHARACTER SET utf8mb4 COLLATE utf8mb4_bin");
        LOG_INFO("Created the ub_tools." + system_table_name + " table.");
    }

    for (const auto &update_filename : update_filenames)
        ApplyUpdate(&db_connection, update_directory_path, update_filename);

    return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>/* $Id$ */
# ifndef CPPAD_CHECK_SIMPLE_VECTOR_INCLUDED
# define CPPAD_CHECK_SIMPLE_VECTOR_INCLUDED

/* --------------------------------------------------------------------------
CppAD: C++ Algorithmic Differentiation: Copyright (C) 2003-12 Bradley M. Bell

CppAD is distributed under multiple licenses. This distribution is under
the terms of the
                    Common Public License Version 1.0.

A copy of this license is included in the COPYING file of this distribution.
Please visit http://www.coin-or.org/CppAD/ for information on other licenses.
-------------------------------------------------------------------------- */
/*
$begin CheckSimpleVector$$
$spell
	alloc
	const
	cppad.hpp
	CppAD
$$

$section Check Simple Vector Concept$$

$index simple, vector check$$
$index vector, simple check$$
$index check, simple vector$$
$index concept, check simple vector$$

$head Syntax$$
$code # include <cppad/check_simple_vector.hpp>$$
$pre
$$
$codei%CheckSimpleVector<%Scalar%, %Vector%>()%$$
$pre
$$
$codei%CheckSimpleVector<%Scalar%, %Vector%>(%x%, %y%)%$$

$head Purpose$$
Preforms compile and run time checks that the type specified
by $icode Vector$$ satisfies all the requirements for
a $xref/SimpleVector/$$ class with
$xref/SimpleVector/Elements of Specified Type/elements of type/$$
$icode Scalar$$.
If a requirement is not satisfied,
a an error message makes it clear what condition is not satisfied.

$head x, y$$
If the arguments $icode x$$ and $icode y$$ are present,
they have prototype
$codei%
	const %Scalar%& %x%
	const %Scalar%& %y%
%$$
In addition, the check
$code%
	%x% == %x%
%$$
will return the boolean value $code true$$, and
$code%
	%x% == %y%
%$$
will return $code false$$.

$head Restrictions$$
If the arguments $icode x$$ and $icode y$$ are not present,
the following extra assumption is made by $code CheckSimpleVector$$:
If $icode x$$ is a $icode Scalar$$ object
$codei%
	%x% = 0
	%y% = 1
%$$
assigns values to the objects $icode x$$ and $icode y$$.
In addition,
$icode%x% == %x%$$ would return the boolean value $code true$$ and
$icode%x% == %y%$$ would return $code false$$.

$head Include$$
The file $code cppad/check_simple_vector.hpp$$ is included by $code cppad/cppad.hpp$$
but it can also be included separately with out the rest
if the CppAD include files.

$head Parallel Mode$$
$index parallel, CheckSimpleVector$$
$index CheckSimpleVector, parallel$$
The routine $cref/thread_alloc::parallel_setup/ta_parallel_setup/$$
must be called before it
can be used in $cref/parallel/ta_in_parallel/$$ mode.

$head Example$$
$children%
	example/check_simple_vector.cpp
%$$
The file $xref/CheckSimpleVector.cpp/$$
contains an example and test of this function where $icode S$$
is the same as $icode T$$.
It returns true, if it succeeds an false otherwise.
The comments in this example suggest a way to change the example
so $icode S$$ is not the same as $icode T$$.

$end
---------------------------------------------------------------------------
*/

# include <cstddef>
# include <cppad/local/cppad_assert.hpp>
# include <cppad/local/define.hpp>
# include <cppad/thread_alloc.hpp>

namespace CppAD {

# ifdef NDEBUG
	template <class Scalar, class Vector>
	inline void CheckSimpleVector(const Scalar& x, const Scalar& y)
	{ }
	template <class Scalar, class Vector>
	inline void CheckSimpleVector(void)
	{ }
# else
	template <class S, class T>
	struct ok_if_S_same_as_T { };

	template <class T>
	struct ok_if_S_same_as_T<T,T> { typedef T ok; };

	template <class Scalar, class Vector>
	void CheckSimpleVector(const Scalar& x, const Scalar& y)
	{	CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL
		// Section 3.6.2 of ISO/IEC 14882:1998(E) states: "The storage for
		// objects with static storage duration (3.7.1) shall be zero-
		// initialized (8.5) before any other initialization takes place."
		static size_t count[CPPAD_MAX_NUM_THREADS];
		size_t thread = thread_alloc::thread_num();
		if( count[thread] > 0  )
			return;
		count[thread]++;

		// value_type must be type of elements of Vector
		typedef typename Vector::value_type value_type;

		// check that elements of Vector have type Scalar
		typedef typename ok_if_S_same_as_T<Scalar, value_type>::ok ok;

		// check default constructor
		Vector d;

		// size member function
		CPPAD_ASSERT_KNOWN(
			d.size() == 0,
			"default construtor result does not have size zero"
		);

		// resize to same size as other vectors in test
		d.resize(1);

		// check sizing constructor
		Vector s(1);

		// check element assignment
		s[0] = y;
		CPPAD_ASSERT_KNOWN(
			s[0] == y,
			"element assignment failed"
		);

		// check copy constructor
		s[0] = x;
		const Vector c(s);
		s[0] = y;
		CPPAD_ASSERT_KNOWN(
			c[0] == x,
			"copy constructor is shallow"
		);

		// vector assignment operator
		d[0] = x;
		s    = d;
		s[0] = y;
		CPPAD_ASSERT_KNOWN(
			d[0] == x,
			"assignment operator is shallow"
		);

		// element access, right side const
		// element assignment, left side not const
		d[0] = c[0];
		CPPAD_ASSERT_KNOWN(
			d[0] == x,
			"element assignment from const failed"
		);
	}
	template <class Scalar, class Vector>
	void CheckSimpleVector(void)
	{	Scalar x;
		Scalar y;

		// use assignment and not constructor
		x = 0;
		y = 1;

		CheckSimpleVector<Scalar, Vector>(x, y);
	}

# endif

} // end namespace CppAD

# endif
<commit_msg>check_simple_vector.hpp: Only need check once.<commit_after>/* $Id$ */
# ifndef CPPAD_CHECK_SIMPLE_VECTOR_INCLUDED
# define CPPAD_CHECK_SIMPLE_VECTOR_INCLUDED

/* --------------------------------------------------------------------------
CppAD: C++ Algorithmic Differentiation: Copyright (C) 2003-12 Bradley M. Bell

CppAD is distributed under multiple licenses. This distribution is under
the terms of the
                    Common Public License Version 1.0.

A copy of this license is included in the COPYING file of this distribution.
Please visit http://www.coin-or.org/CppAD/ for information on other licenses.
-------------------------------------------------------------------------- */
/*
$begin CheckSimpleVector$$
$spell
	alloc
	const
	cppad.hpp
	CppAD
$$

$section Check Simple Vector Concept$$

$index simple, vector check$$
$index vector, simple check$$
$index check, simple vector$$
$index concept, check simple vector$$

$head Syntax$$
$code # include <cppad/check_simple_vector.hpp>$$
$pre
$$
$codei%CheckSimpleVector<%Scalar%, %Vector%>()%$$
$pre
$$
$codei%CheckSimpleVector<%Scalar%, %Vector%>(%x%, %y%)%$$

$head Purpose$$
Preforms compile and run time checks that the type specified
by $icode Vector$$ satisfies all the requirements for
a $xref/SimpleVector/$$ class with
$xref/SimpleVector/Elements of Specified Type/elements of type/$$
$icode Scalar$$.
If a requirement is not satisfied,
a an error message makes it clear what condition is not satisfied.

$head x, y$$
If the arguments $icode x$$ and $icode y$$ are present,
they have prototype
$codei%
	const %Scalar%& %x%
	const %Scalar%& %y%
%$$
In addition, the check
$code%
	%x% == %x%
%$$
will return the boolean value $code true$$, and
$code%
	%x% == %y%
%$$
will return $code false$$.

$head Restrictions$$
If the arguments $icode x$$ and $icode y$$ are not present,
the following extra assumption is made by $code CheckSimpleVector$$:
If $icode x$$ is a $icode Scalar$$ object
$codei%
	%x% = 0
	%y% = 1
%$$
assigns values to the objects $icode x$$ and $icode y$$.
In addition,
$icode%x% == %x%$$ would return the boolean value $code true$$ and
$icode%x% == %y%$$ would return $code false$$.

$head Include$$
The file $code cppad/check_simple_vector.hpp$$ is included by $code cppad/cppad.hpp$$
but it can also be included separately with out the rest
if the CppAD include files.

$head Parallel Mode$$
$index parallel, CheckSimpleVector$$
$index CheckSimpleVector, parallel$$
The routine $cref/thread_alloc::parallel_setup/ta_parallel_setup/$$
must be called before it
can be used in $cref/parallel/ta_in_parallel/$$ mode.

$head Example$$
$children%
	example/check_simple_vector.cpp
%$$
The file $xref/CheckSimpleVector.cpp/$$
contains an example and test of this function where $icode S$$
is the same as $icode T$$.
It returns true, if it succeeds an false otherwise.
The comments in this example suggest a way to change the example
so $icode S$$ is not the same as $icode T$$.

$end
---------------------------------------------------------------------------
*/

# include <cstddef>
# include <cppad/local/cppad_assert.hpp>
# include <cppad/local/define.hpp>
# include <cppad/thread_alloc.hpp>

namespace CppAD {

# ifdef NDEBUG
	template <class Scalar, class Vector>
	inline void CheckSimpleVector(const Scalar& x, const Scalar& y)
	{ }
	template <class Scalar, class Vector>
	inline void CheckSimpleVector(void)
	{ }
# else
	template <class S, class T>
	struct ok_if_S_same_as_T { };

	template <class T>
	struct ok_if_S_same_as_T<T,T> { typedef T ok; };

	template <class Scalar, class Vector>
	void CheckSimpleVector(const Scalar& x, const Scalar& y)
	{	CPPAD_ASSERT_FIRST_CALL_NOT_PARALLEL
		static size_t count;
		if( count > 0  )
			return;
		count++;

		// value_type must be type of elements of Vector
		typedef typename Vector::value_type value_type;

		// check that elements of Vector have type Scalar
		typedef typename ok_if_S_same_as_T<Scalar, value_type>::ok ok;

		// check default constructor
		Vector d;

		// size member function
		CPPAD_ASSERT_KNOWN(
			d.size() == 0,
			"default construtor result does not have size zero"
		);

		// resize to same size as other vectors in test
		d.resize(1);

		// check sizing constructor
		Vector s(1);

		// check element assignment
		s[0] = y;
		CPPAD_ASSERT_KNOWN(
			s[0] == y,
			"element assignment failed"
		);

		// check copy constructor
		s[0] = x;
		const Vector c(s);
		s[0] = y;
		CPPAD_ASSERT_KNOWN(
			c[0] == x,
			"copy constructor is shallow"
		);

		// vector assignment operator
		d[0] = x;
		s    = d;
		s[0] = y;
		CPPAD_ASSERT_KNOWN(
			d[0] == x,
			"assignment operator is shallow"
		);

		// element access, right side const
		// element assignment, left side not const
		d[0] = c[0];
		CPPAD_ASSERT_KNOWN(
			d[0] == x,
			"element assignment from const failed"
		);
	}
	template <class Scalar, class Vector>
	void CheckSimpleVector(void)
	{	Scalar x;
		Scalar y;

		// use assignment and not constructor
		x = 0;
		y = 1;

		CheckSimpleVector<Scalar, Vector>(x, y);
	}

# endif

} // end namespace CppAD

# endif
<|endoftext|>


<commit_before>#include <ecto/plasm.hpp>
#include <ecto/tendril.hpp>
#include <ecto/module.hpp>

#include <string>
#include <map>
#include <set>
#include <utility>
#include <deque>

//this quiets a deprecated warning
#define BOOST_NO_HASH
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/topological_sort.hpp>
#include <boost/graph/graphviz.hpp>
#include <boost/foreach.hpp>
#include <boost/unordered_map.hpp>

#include <boost/timer.hpp>

namespace ecto
{

namespace
{
using boost::adjacency_list;
using boost::vecS;
using boost::bidirectionalS;
using boost::graph_traits;
using boost::tie;
using boost::add_vertex;

struct edge
{
  edge(const std::string& fp, const std::string& tp) :
    from_port(fp), to_port(tp)
  {
  }

  std::string from_port, to_port;
  std::deque<ecto::tendril> deque;
  typedef boost::shared_ptr<edge> ptr;
  typedef boost::shared_ptr<const edge> const_ptr;
};

// if the first argument is a sequence type (vecS, etc) then parallel edges are allowed
typedef adjacency_list<vecS, // OutEdgeList...
    vecS, // VertexList
    bidirectionalS, // Directed
    module::ptr, // vertex property
    edge::ptr> // edge property
graph_t;

struct vertex_writer
{
  graph_t* g;

  vertex_writer(graph_t* g_) :
    g(g_)
  {
  }

  void operator()(std::ostream& out, graph_t::vertex_descriptor vd)
  {
    out << "[label=\"" << (*g)[vd]->name() << "\"]";
  }
};

struct edge_writer
{
  graph_t* g;

  edge_writer(graph_t* g_) :
    g(g_)
  {
  }

  void operator()(std::ostream& out, graph_t::edge_descriptor ed)
  {
    out << "[headlabel=\"" << (*g)[ed]->to_port << "\" taillabel=\"" << (*g)[ed]->from_port << "\"]";
  }
};

struct graph_writer
{
  void operator()(std::ostream& out) const
  {
    out << "graph [rankdir=TB, ranksep=1]" << std::endl;
    out << "edge [labelfontsize=8]" << std::endl;
  }
};

edge::ptr make_edge(const std::string& fromport, const std::string& toport)
{
  edge::ptr eptr(new edge(fromport, toport));
  return eptr;
}

} // namespace

struct plasm::impl
{
  impl()
  {
  }

  //insert a module into the graph, will retrieve the
  //vertex descriptor if its already in the graph...
  graph_t::vertex_descriptor insert_module(module::ptr m)
  {
    //use the vertex map to look up the graphviz descriptor (reverse lookup)
    ModuleVertexMap::iterator it = mv_map.find(m);
    if (it != mv_map.end())
      return it->second;
    graph_t::vertex_descriptor d = add_vertex(m, graph);
    mv_map.insert(std::make_pair(m, d));
    return d;
  }

  void connect(module::ptr from, std::string output, module::ptr to, std::string input)
  {
    //throw if the types are bad...
    to->inputs[input].enforce_compatible_type(from->outputs[output]);

    graph_t::vertex_descriptor fromv = insert_module(from), tov = insert_module(to);
    edge::ptr new_edge = make_edge(output, input);

    //assert that the new edge does not violate inputs that are already connected.
    //RULE an input may only have one source.
    graph_t::in_edge_iterator inbegin, inend;
    tie(inbegin, inend) = boost::in_edges(tov, graph);
    while (inbegin != inend)
      {
        edge::ptr e = graph[*inbegin];
        if (e->to_port == new_edge->to_port)
          {
            throw std::runtime_error(new_edge->to_port + " is already connected, this is considered an error");
          }
        ++inbegin;
      }

    bool added;
    graph_t::edge_descriptor ed;
    tie(ed, added) = boost::add_edge(fromv, tov, new_edge, graph);
    if (!added)
      {
        throw std::runtime_error(
                                 "failed to connect " + from->name() + ":" + output + " with " + to->name() + ":"
                                     + input);
      }
    //clear stack to mark the ordering dirty...
    stack.clear();
  }

  void disconnect(module::ptr from, std::string output, module::ptr to, std::string input)
  {
    graph_t::vertex_descriptor fromv = insert_module(from), tov = insert_module(to);
    boost::remove_edge(fromv, tov, graph);
  }

  int invoke_process(graph_t::vertex_descriptor vd)
  {
    //boost::timer t;
    module::ptr m = graph[vd];

    graph_t::in_edge_iterator inbegin, inend;
    tie(inbegin, inend) = boost::in_edges(vd, graph);
    while (inbegin != inend)
      {
        edge::ptr e = graph[*inbegin];
        m->inputs.at(e->to_port).copy_value(e->deque.front());
        e->deque.pop_front();
        ++inbegin;
      }
    int val = m->process();

    graph_t::out_edge_iterator outbegin, outend;
    tie(outbegin, outend) = boost::out_edges(vd, graph);
    while (outbegin != outend)
      {
        edge::ptr e = graph[*outbegin];
        e->deque.push_back(m->outputs.at(e->from_port));
        ++outbegin;
      }
    //std::cout << m->name() << " time: " << t.elapsed() * 1000.0 << "\n";
    return val;
  }

  void compute_stack()
  {
    if (!stack.empty()) //will be empty if this needs to be computed.
      return;
    boost::topological_sort(graph, std::back_inserter(stack));
    std::reverse(stack.begin(), stack.end());
  }

  int execute()
  {
    //compute ordering
    compute_stack();
    for (size_t k = 0; k < stack.size(); ++k)
      {
        //need to check the return val of a process here, non zero means exit...
        size_t retval = invoke_process(stack[k]);
        if (retval)
          return retval;
      }
    return 0;
  }
  //the module to vertex mapping
  //unordered_map so that module ptr works as a key...
  typedef boost::unordered_map<ecto::module::ptr, graph_t::vertex_descriptor> ModuleVertexMap;
  ModuleVertexMap mv_map;
  graph_t graph;
  std::vector<graph_t::vertex_descriptor> stack;
};

plasm::plasm() :
  impl_(new impl)
{
}

void plasm::insert(module::ptr mod)
{
  impl_->insert_module(mod);
}

void plasm::connect(module::ptr from, const std::string& output, module::ptr to, const std::string& input)
{
  impl_->connect(from, output, to, input);
}

void plasm::viz(std::ostream& out) const
{
  boost::write_graphviz(out, impl_->graph, vertex_writer(&impl_->graph), edge_writer(&impl_->graph), graph_writer());
}

std::string plasm::viz() const
{
  std::stringstream ss;
  viz(ss);
  return ss.str();
}

int plasm::execute()
{
  return impl_->execute();
}

void plasm::spin()
{
  for (;;)
    {
      if (execute())
        return;
    }
}

void plasm::disconnect(module_ptr from, const std::string& output, module_ptr to, const std::string& input)
{
  impl_->disconnect(from, output, to, input);
}
}
<commit_msg>Catch and release exceptions for debugging the graph.<commit_after>#include <ecto/plasm.hpp>
#include <ecto/tendril.hpp>
#include <ecto/module.hpp>

#include <string>
#include <map>
#include <set>
#include <utility>
#include <deque>

//this quiets a deprecated warning
#define BOOST_NO_HASH
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/topological_sort.hpp>
#include <boost/graph/graphviz.hpp>
#include <boost/foreach.hpp>
#include <boost/unordered_map.hpp>

#include <boost/timer.hpp>

namespace ecto
{

namespace
{
using boost::adjacency_list;
using boost::vecS;
using boost::bidirectionalS;
using boost::graph_traits;
using boost::tie;
using boost::add_vertex;

struct edge
{
  edge(const std::string& fp, const std::string& tp) :
    from_port(fp), to_port(tp)
  {
  }

  std::string from_port, to_port;
  std::deque<ecto::tendril> deque;
  typedef boost::shared_ptr<edge> ptr;
  typedef boost::shared_ptr<const edge> const_ptr;
};

// if the first argument is a sequence type (vecS, etc)
// then parallel edges are allowed
typedef adjacency_list<vecS, // OutEdgeList...
    vecS, // VertexList
    bidirectionalS, // Directed
    module::ptr, // vertex property
    edge::ptr> // edge property
graph_t;

struct vertex_writer
{
  graph_t* g;

  vertex_writer(graph_t* g_) :
    g(g_)
  {
  }

  void operator()(std::ostream& out, graph_t::vertex_descriptor vd)
  {
    out << "[label=\"" << (*g)[vd]->name() << "\"]";
  }
};

struct edge_writer
{
  graph_t* g;

  edge_writer(graph_t* g_) :
    g(g_)
  {
  }

  void operator()(std::ostream& out, graph_t::edge_descriptor ed)
  {
    out << "[headlabel=\"" << (*g)[ed]->to_port << "\" taillabel=\""
        << (*g)[ed]->from_port << "\"]";
  }
};

struct graph_writer
{
  void operator()(std::ostream& out) const
  {
    out << "graph [rankdir=TB, ranksep=1]" << std::endl;
    out << "edge [labelfontsize=8]" << std::endl;
  }
};

edge::ptr make_edge(const std::string& fromport, const std::string& toport)
{
  edge::ptr eptr(new edge(fromport, toport));
  return eptr;
}

} // namespace

struct plasm::impl
{
  impl()
  {
  }

  //insert a module into the graph, will retrieve the
  //vertex descriptor if its already in the graph...
  graph_t::vertex_descriptor insert_module(module::ptr m)
  {
    //use the vertex map to look up the graphviz descriptor (reverse lookup)
    ModuleVertexMap::iterator it = mv_map.find(m);
    if (it != mv_map.end())
      return it->second;
    graph_t::vertex_descriptor d = add_vertex(m, graph);
    mv_map.insert(std::make_pair(m, d));
    return d;
  }

  void connect(module::ptr from, std::string output, module::ptr to,
               std::string input)
  {
    //throw if the types are bad...
    to->inputs[input].enforce_compatible_type(from->outputs[output]);

    graph_t::vertex_descriptor fromv = insert_module(from), tov =
        insert_module(to);
    edge::ptr new_edge = make_edge(output, input);

    //assert that the new edge does not violate inputs that are
    //already connected.
    //RULE an input may only have one source.
    graph_t::in_edge_iterator inbegin, inend;
    tie(inbegin, inend) = boost::in_edges(tov, graph);
    while (inbegin != inend)
      {
        edge::ptr e = graph[*inbegin];
        if (e->to_port == new_edge->to_port)
          {
            throw std::runtime_error(
                                     new_edge->to_port
                                         + " is already connected, this is considered an error");
          }
        ++inbegin;
      }

    bool added;
    graph_t::edge_descriptor ed;
    tie(ed, added) = boost::add_edge(fromv, tov, new_edge, graph);
    if (!added)
      {
        throw std::runtime_error(
                                 "failed to connect " + from->name() + ":"
                                     + output + " with " + to->name() + ":"
                                     + input);
      }
    //clear stack to mark the ordering dirty...
    stack.clear();
  }

  void disconnect(module::ptr from, std::string output, module::ptr to,
                  std::string input)
  {
    graph_t::vertex_descriptor fromv = insert_module(from), tov =
        insert_module(to);
    boost::remove_edge(fromv, tov, graph);
  }

  int invoke_process(graph_t::vertex_descriptor vd)
  {
    boost::timer t;
    module::ptr m = graph[vd];

    graph_t::in_edge_iterator inbegin, inend;
    tie(inbegin, inend) = boost::in_edges(vd, graph);
    while (inbegin != inend)
      {
        edge::ptr e = graph[*inbegin];
        m->inputs.at(e->to_port).copy_value(e->deque.front());
        e->deque.pop_front();
        ++inbegin;
      }
    int val = 1;
    try
      {
        val = m->process();
      } catch (std::exception& e)
      {
        throw std::runtime_error(
                                 m->name() + " threw an exception :\n"
                                     + e.what());
      }
    graph_t::out_edge_iterator outbegin, outend;
    tie(outbegin, outend) = boost::out_edges(vd, graph);
    while (outbegin != outend)
      {
        edge::ptr e = graph[*outbegin];
        e->deque.push_back(m->outputs.at(e->from_port));
        ++outbegin;
      }
    timings_total[vd] += t.elapsed();
    //std::cout << m->name() << " time: " << t.elapsed() * 1000.0 << "\n";
    return val;
  }

  void compute_stack()
  {
    if (!stack.empty()) //will be empty if this needs to be computed.
      return;
    boost::topological_sort(graph, std::back_inserter(stack));
    std::reverse(stack.begin(), stack.end());
  }

  int execute()
  {
    //compute ordering
    compute_stack();
    for (size_t k = 0; k < stack.size(); ++k)
      {
        //need to check the return val of a process here, non zero means exit...
        size_t retval = invoke_process(stack[k]);
        if (retval)
          return retval;
      }
    return 0;
  }
  //the module to vertex mapping
  //unordered_map so that module ptr works as a key...
  typedef boost::unordered_map<ecto::module::ptr, graph_t::vertex_descriptor>
      ModuleVertexMap;
  ModuleVertexMap mv_map;
  graph_t graph;
  std::vector<graph_t::vertex_descriptor> stack;
  std::map<graph_t::vertex_descriptor, double> timings_total;

};

plasm::plasm() :
  impl_(new impl)
{
}

void plasm::insert(module::ptr mod)
{
  impl_->insert_module(mod);
}

void plasm::connect(module::ptr from, const std::string& output,
                    module::ptr to, const std::string& input)
{
  impl_->connect(from, output, to, input);
}

void plasm::viz(std::ostream& out) const
{
  boost::write_graphviz(out, impl_->graph, vertex_writer(&impl_->graph),
                        edge_writer(&impl_->graph), graph_writer());
}

std::string plasm::viz() const
{
  std::stringstream ss;
  viz(ss);
  return ss.str();
}

int plasm::execute()
{
  return impl_->execute();
}

void plasm::spin()
{
  for (;;)
    {
      if (execute())
        return;
    }
}

void plasm::disconnect(module_ptr from, const std::string& output,
                       module_ptr to, const std::string& input)
{
  impl_->disconnect(from, output, to, input);
}
}
<|endoftext|>


<commit_before>#ifndef __FUTURE_HPP__
#define __FUTURE_HPP__

#include "SoftXMT.hpp"
#include "Delegate.hpp"
#include "Cache.hpp"

//TODO: special ArgsCache acquire, which
//uses args pointer field as a global address
//to the future and either
//1. flip the started bit
//2. or simply acquire RW instead of RO (and Cached not Incoherent)

// idea: if we had a need for futures that
// only mutate their arguments (essentially functional but could change the inputs as well)
// then we could allow multiple copies of
// the future to execute RW-shared mode, where only the first release writeback wins

// TODO: do something like this to force users to have
// the future fields but make them invisible
/*
class FutureArgs {
    private:
    GlobalAddress<Future> futurePtr;
    void (* user_fn_p)(A *);
};
class MyArgs : public FutureArgs {
    int i;
    int j;
}
// Additional possibility is method #2 above
    */

//////////////////////////////////////////
#if DEBUG
static int64_t count_ = 0;
#endif

template < typename ArgsStruct >
class Future {
    private:
        int64_t started;
        Thread * waiter;
        bool done;
        ArgsStruct * userArgs_lp;

        struct future_args {
            GlobalAddress<Future> futureAddr;
            void (* user_fn_p)(ArgsStruct *);
            GlobalAddress< ArgsStruct > userArgs;

            future_args( Future< ArgsStruct > * future, ArgsStruct * userArgs, void (* fn_p)(ArgsStruct *) )
                : futureAddr( make_global( future ) )
                , userArgs( make_global( userArgs ) )
                , user_fn_p( fn_p )
            { }
        };

        future_args task_args;

        struct future_done_args {
            Future< ArgsStruct > * futurePtr;
        };

        static void future_done_am( future_done_args * args, size_t args_size, void * payload, size_t payload_size ) {
            args->futurePtr->done = true;
            if ( args->futurePtr->waiter != NULL ) {
                SoftXMT_wake( args->futurePtr->waiter );
                args->futurePtr->waiter = NULL;
            }
        }

        /////////////////
        //Custom delegate operation
        //TODO: use a generalized mechanism that abstracts all but function
        /////////////////
        struct started_memory_descriptor {
            Thread * t;
            GlobalAddress< Future<ArgsStruct> > address;
            int64_t data;
            bool done;
        };

        struct started_reply_args {
            GlobalAddress<started_memory_descriptor> descriptor;
        };

        static void started_reply_am( started_reply_args * args, size_t size, void * payload, size_t payload_size ) {
            assert( payload_size == sizeof(int64_t ) );
            args->descriptor.pointer()->data = *(static_cast<int64_t*>(payload));
            args->descriptor.pointer()->done = true;
            SoftXMT_wake( args->descriptor.pointer()->t );
        }

        struct started_request_args {
            GlobalAddress<started_memory_descriptor> descriptor;
            GlobalAddress< Future<ArgsStruct> > address;
        };

        static void started_request_am( started_request_args * args, size_t size, void * payload, size_t payload_size ) {
            Future< ArgsStruct > * fptr = args->address.pointer();

            CHECK((int64_t)fptr>0x1000) << "dequeued request (node:" << args->descriptor.node()
                    << "): future ptr:" << (void*)fptr
                    << "(id:"<<fptr->getId()<<")";
            int64_t data = fptr->started;
            fptr->started = data + 1;

            // If future was already started in this case, it must have been started by touching thread.
            // Incrementing started again will tell the touching thread it can deallocate the Future
            if ( data > 0 ) {
                VLOG(2) << "already started:(id:"<<fptr->getId();
                if ( fptr->done ) { //if it is done then toucher is waiting for the dequeue
                    VLOG(2) << "need to wake:(id:"<<fptr->getId();
                    CHECK ( fptr->waiter!=NULL ) << "future ptr:" << (void*)fptr <<"\n done="<<fptr->done<<" (id:"<<fptr->getId()<<")";
                    SoftXMT_wake( fptr->waiter );
                    fptr->waiter = NULL;
                } else {
                    VLOG(2) << "not need to wake:(id:"<<fptr->getId();
                }
            } else {
                VLOG(2) << "not already started:(id:"<<fptr->getId();
            }

            started_reply_args reply_args;
            reply_args.descriptor = args->descriptor;
            SoftXMT_call_on( args->descriptor.node(), &started_reply_am,
                    &reply_args, sizeof(reply_args),
                    &data, sizeof(data) );
        }

        static int64_t future_delegate_started( GlobalAddress< Future<ArgsStruct> > address ) {
            started_memory_descriptor md;
            md.address = address;
            md.data = 0;
            md.done = false;
            md.t = CURRENT_THREAD;
            started_request_args args;
            args.descriptor = make_global(&md);
            args.address = address;
            SoftXMT_call_on( address.node(), &started_request_am, &args );
            while( !md.done ) {
                SoftXMT_suspend();
            }
            return md.data;
        }
        //////////////////////////////////////

        static void future_function( future_args * args ) {
            // TODO #1: the make_global is just calculating location of Future->started
            //if ( SoftXMT_delegate_fetch_and_add_word( make_global( args->startedAddr, args->futureAddr.node() ), 1 ) == 0 ) {
            VLOG(2) << CURRENT_THREAD->id << "args("<<(void*)args<<") will call started am " << args->futureAddr.pointer();
            if ( future_delegate_started( args->futureAddr ) == 0 ) {
                VLOG(2) << CURRENT_THREAD->id << "user_args="<<args->userArgs<<" for ftraddr="<< args->futureAddr.pointer();
                // grab the user arguments
                size_t args_size = sizeof(ArgsStruct);
                ArgsStruct argsbuf;
                typename Incoherent<ArgsStruct>::RO cached_args( args->userArgs, args_size, &argsbuf );
                cached_args.block_until_acquired();

                // call the user task
                args->user_fn_p( &argsbuf );
                cached_args.block_until_released();

                // call wake up AM on Node that has the Future
                future_done_args done_args = { args->futureAddr.pointer() };
                SoftXMT_call_on( args->futureAddr.node(), &future_done_am, &done_args );
            }
        }

        int64_t getId() {
#if DEBUG
            return id;
#else
            return -1;
#endif
        }

    public:
#if DEBUG
        int64_t id;
#endif

        // TODO: NOTE that this does not copy user arguments because we want to
        // preserve the same semantics as normal tasks.
        // --Unfortunately this means there are three communications to start
        // 1) Task.execute fetches args
        // 2) Future atomic started
        // 3) Bring in user arguments and start
        Future ( void (* fn_p)(ArgsStruct *), ArgsStruct * userArgs )
            : started( 0 )
              , waiter( NULL )
              , done( false )
              , userArgs_lp( userArgs )
#if DEBUG
              , id( count_++ )
#endif
              , task_args( this, userArgs, fn_p ) {

           VLOG(2) << CURRENT_THREAD->id << " creates Future:"<< (void*)this << " id:"<< getId() << " args:"<< &task_args;
        }

        void touch( ) {
            // start if not started
            if ( SoftXMT_delegate_fetch_and_add_word( make_global(&started), 1 )==0 ) {
                VLOG(2) << CURRENT_THREAD->id << " gets to touch-go " << getId();
                task_args.user_fn_p( userArgs_lp );
                done = true;
                while ( started < 2 ) { // wait until dequeued
                    VLOG(2) << CURRENT_THREAD->id << " has to wait on dequeue " << getId();
                    waiter = CURRENT_THREAD;
                    SoftXMT_suspend( );
                    VLOG(2) << CURRENT_THREAD->id << " has woke on dequeue " << getId();
                }
            } else  {
                // otherwise block on done event
                while ( !done ) {
                    waiter = CURRENT_THREAD;
                    SoftXMT_suspend( );
                }
            }
        }

        void asPublicTask( ) {
            SoftXMT_publicTask( &future_function, &task_args );
        }
};

#endif
<commit_msg>debug logging for futures<commit_after>#ifndef __FUTURE_HPP__
#define __FUTURE_HPP__

#include "SoftXMT.hpp"
#include "Delegate.hpp"
#include "Cache.hpp"

//TODO: special ArgsCache acquire, which
//uses args pointer field as a global address
//to the future and either
//1. flip the started bit
//2. or simply acquire RW instead of RO (and Cached not Incoherent)

// idea: if we had a need for futures that
// only mutate their arguments (essentially functional but could change the inputs as well)
// then we could allow multiple copies of
// the future to execute RW-shared mode, where only the first release writeback wins

// TODO: do something like this to force users to have
// the future fields but make them invisible
/*
class FutureArgs {
    private:
    GlobalAddress<Future> futurePtr;
    void (* user_fn_p)(A *);
};
class MyArgs : public FutureArgs {
    int i;
    int j;
}
// Additional possibility is method #2 above
    */

//////////////////////////////////////////
#if DEBUG
static int64_t count_ = 0;
#endif

template < typename ArgsStruct >
class Future {
    private:
        int64_t started;
        Thread * waiter;
        bool done;
        ArgsStruct * userArgs_lp;

        struct future_args {
            GlobalAddress<Future> futureAddr;
            void (* user_fn_p)(ArgsStruct *);
            GlobalAddress< ArgsStruct > userArgs;

            future_args( Future< ArgsStruct > * future, ArgsStruct * userArgs, void (* fn_p)(ArgsStruct *) )
                : futureAddr( make_global( future ) )
                , userArgs( make_global( userArgs ) )
                , user_fn_p( fn_p )
            { }
        };

        future_args task_args;

        struct future_done_args {
            Future< ArgsStruct > * futurePtr;
        };

        static void future_done_am( future_done_args * args, size_t args_size, void * payload, size_t payload_size ) {
            args->futurePtr->done = true;
            if ( args->futurePtr->waiter != NULL ) {
                SoftXMT_wake( args->futurePtr->waiter );
                args->futurePtr->waiter = NULL;
            }
        }

        /////////////////
        //Custom delegate operation
        //TODO: use a generalized mechanism that abstracts all but function
        /////////////////
        struct started_memory_descriptor {
            Thread * t;
            GlobalAddress< Future<ArgsStruct> > address;
            int64_t data;
            bool done;
        };

        struct started_reply_args {
            GlobalAddress<started_memory_descriptor> descriptor;
        };

        static void started_reply_am( started_reply_args * args, size_t size, void * payload, size_t payload_size ) {
            assert( payload_size == sizeof(int64_t ) );
            args->descriptor.pointer()->data = *(static_cast<int64_t*>(payload));
            args->descriptor.pointer()->done = true;
            SoftXMT_wake( args->descriptor.pointer()->t );
        }

        struct started_request_args {
            GlobalAddress<started_memory_descriptor> descriptor;
            GlobalAddress< Future<ArgsStruct> > address;
        };

        static void started_request_am( started_request_args * args, size_t size, void * payload, size_t payload_size ) {
            Future< ArgsStruct > * fptr = args->address.pointer();

            DVLOG(5) << "Future(IN_AM) FID "<< fptr->getId();

            CHECK((int64_t)fptr>0x1000) << "dequeued request (descriptor gaddress:" << args->descriptor
                    << "): future ptr:" << (void*)fptr
                    << "(future gaddress:"<<args->address;
            int64_t data = fptr->started;
            fptr->started = data + 1;

            // If future was already started in this case, it must have been started by touching thread.
            // Incrementing started again will tell the touching thread it can deallocate the Future
            if ( data > 0 ) {
                DVLOG(5) << "already started:(id:"<<fptr->getId();
                if ( fptr->done ) { //if it is done then toucher is waiting for the dequeue
                    DVLOG(5) << "need to wake:(id:"<<fptr->getId();
                    CHECK ( fptr->waiter!=NULL ) << "future ptr:" << (void*)fptr <<"\n done="<<fptr->done<<" (id:"<<fptr->getId()<<") data="<<data<< " (AM from "<<args->descriptor.node();
                    SoftXMT_wake( fptr->waiter );
                    fptr->waiter = NULL;
                } else {
                    DVLOG(5) << "not need to wake:(id:"<<fptr->getId();
                }
            } else {
                DVLOG(5) << "not already started:(id:"<<fptr->getId();
            }

            started_reply_args reply_args;
            reply_args.descriptor = args->descriptor;
            SoftXMT_call_on( args->descriptor.node(), &started_reply_am,
                    &reply_args, sizeof(reply_args),
                    &data, sizeof(data) );
        }

        static int64_t future_delegate_started( GlobalAddress< Future<ArgsStruct> > address ) {
            started_memory_descriptor md;
            md.address = address;
            md.data = 0;
            md.done = false;
            md.t = CURRENT_THREAD;
            started_request_args args;
            args.descriptor = make_global(&md);
            args.address = address;
            SoftXMT_call_on( address.node(), &started_request_am, &args );
            while( !md.done ) {
                SoftXMT_suspend();
            }
            return md.data;
        }
        //////////////////////////////////////

        static void future_function( future_args * args ) {
            DVLOG(4) << "Future(other) "<< args->futureAddr;
            // TODO #1: the make_global is just calculating location of Future->started
            //if ( SoftXMT_delegate_fetch_and_add_word( make_global( args->startedAddr, args->futureAddr.node() ), 1 ) == 0 ) {
            DVLOG(5) << CURRENT_THREAD->id << "args("<<(void*)args<<") will call started am " << args->futureAddr.pointer();
            if ( future_delegate_started( args->futureAddr ) == 0 ) {
                DVLOG(5) << CURRENT_THREAD->id << "user_args="<<args->userArgs<<" for ftraddr="<< args->futureAddr.pointer();
                // grab the user arguments
                size_t args_size = sizeof(ArgsStruct);
                ArgsStruct argsbuf;
                typename Incoherent<ArgsStruct>::RO cached_args( args->userArgs, args_size, &argsbuf );
                cached_args.block_until_acquired();

                // call the user task
                args->user_fn_p( &argsbuf );
                cached_args.block_until_released();

                // call wake up AM on Node that has the Future
                future_done_args done_args = { args->futureAddr.pointer() };
                SoftXMT_call_on( args->futureAddr.node(), &future_done_am, &done_args );
            }
        }

        int64_t getId() {
#if DEBUG
            return id;
#else
            return -1;
#endif
        }

    public:
#if DEBUG
        int64_t id;
#endif

        // TODO: NOTE that this does not copy user arguments because we want to
        // preserve the same semantics as normal tasks.
        // --Unfortunately this means there are three communications to start
        // 1) Task.execute fetches args
        // 2) Future atomic started
        // 3) Bring in user arguments and start
        Future ( void (* fn_p)(ArgsStruct *), ArgsStruct * userArgs )
            : started( 0 )
              , waiter( NULL )
              , done( false )
              , userArgs_lp( userArgs )
#if DEBUG
              , id( SoftXMT_mynode() + ((count_++)*SoftXMT_nodes() ) )
#endif
              , task_args( this, userArgs, fn_p ) {

           DVLOG(5) << CURRENT_THREAD->id << " creates Future:"<< (void*)this << " id:"<< getId() << " args:"<< &task_args;
        }

        void touch( ) {
            DVLOG(4) << "Future(touch) FID "<< this->getId() << " ga:"<<task_args.futureAddr;
            // start if not started
            if ( SoftXMT_delegate_fetch_and_add_word( make_global(&started), 1 )==0 ) {
                DVLOG(5) << CURRENT_THREAD->id << " gets to touch-go " << getId();
                task_args.user_fn_p( userArgs_lp );
                CHECK( started <= 2 ) << "started=" << started << " (at most one additional increment should occur)";
                done = true;
                while ( started < 2 ) { // wait until dequeued
                    DVLOG(5) << CURRENT_THREAD->id << " has to wait on dequeue " << getId();
                    waiter = CURRENT_THREAD;
                    SoftXMT_suspend( );
                    DVLOG(5) << CURRENT_THREAD->id << " has woke on dequeue " << getId();
                }
            } else  {
                // otherwise block on done event
                while ( !done ) {
                    waiter = CURRENT_THREAD;
                    SoftXMT_suspend( );
                }
            }
        }

        void asPublicTask( ) {
            DVLOG(4) << "Future(spawn) " << this->getId() << " ga:"<<task_args.futureAddr;
            SoftXMT_publicTask( &future_function, &task_args );
        }
};

#endif
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013, Hernan Saez
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the <organization> nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef CRIMILD_CORE_FOUNDATION_MEMORY_
#define CRIMILD_CORE_FOUNDATION_MEMORY_

#include <mutex>
#include <memory>

namespace crimild {

    template< typename T >
    using SharedPointer = std::shared_ptr< T >;

    template< typename T >
    using UniquePointer = std::unique_ptr< T >;

    template< typename T, typename... Args >
    SharedPointer< T > alloc( Args &&... args )
    {
        // use 'new' instead of 'make_shared' to force the use of
        // the custom allocator.
        // TODO: use alloc_shared?
        return SharedPointer< T >( new T( std::forward< Args >( args )... ) );
    }

    template< typename T, typename... Args >
    UniquePointer< T > alloc_unique( Args &&... args )
    {
        return std::unique_ptr< T >( new T( std::forward< Args >( args )... ) );
    }

    template< typename T >
    SharedPointer< T > retain( T *ptr )
    {
        if ( ptr == nullptr ) {
            return nullptr;
        }

        return std::static_pointer_cast< T >( ptr->shared_from_this() );
    }

    template< typename T >
    inline SharedPointer< T > &retain( SharedPointer< T > &ptr ) noexcept
    {
        // Returns the same pointer.
        // This is a helper function that comes in handy for templates and generic code
        return ptr;
    }

    template< typename T >
    T *get_ptr( SharedPointer< T > const &ptr )
    {
        return ptr.get();
    }

    template< typename T >
    inline T *get_ptr( T *ptr ) noexcept
    {
        // Returns the same pointer.
        // This is a helper function that comes in handy for templates and generic code
        return ptr;
    }

    template< class T, class U >
    SharedPointer< T > cast_ptr( SharedPointer< U > const &ptr )
    {
        return std::static_pointer_cast< T >( ptr );
    }

    template< class T, class U >
    SharedPointer< T > dynamic_cast_ptr( SharedPointer< U > const &ptr )
    {
        return std::dynamic_pointer_cast< T >( ptr );
    }

}

#endif

<commit_msg>Helper function to cast raw pointers<commit_after>/*
 * Copyright (c) 2013, Hernan Saez
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the <organization> nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef CRIMILD_CORE_FOUNDATION_MEMORY_
#define CRIMILD_CORE_FOUNDATION_MEMORY_

#include <memory>
#include <mutex>

namespace crimild {

    template< typename T >
    using SharedPointer = std::shared_ptr< T >;

    template< typename T >
    using UniquePointer = std::unique_ptr< T >;

    template< typename T, typename... Args >
    SharedPointer< T > alloc( Args &&...args )
    {
        // use 'new' instead of 'make_shared' to force the use of
        // the custom allocator.
        // TODO: use alloc_shared?
        return SharedPointer< T >( new T( std::forward< Args >( args )... ) );
    }

    template< typename T, typename... Args >
    UniquePointer< T > alloc_unique( Args &&...args )
    {
        return std::unique_ptr< T >( new T( std::forward< Args >( args )... ) );
    }

    template< typename T >
    SharedPointer< T > retain( T *ptr )
    {
        if ( ptr == nullptr ) {
            return nullptr;
        }

        return std::static_pointer_cast< T >( ptr->shared_from_this() );
    }

    template< typename T >
    inline SharedPointer< T > &retain( SharedPointer< T > &ptr ) noexcept
    {
        // Returns the same pointer.
        // This is a helper function that comes in handy for templates and generic code
        return ptr;
    }

    template< typename T >
    T *get_ptr( SharedPointer< T > const &ptr )
    {
        return ptr.get();
    }

    template< typename T >
    inline T *get_ptr( T *ptr ) noexcept
    {
        // Returns the same pointer.
        // This is a helper function that comes in handy for templates and generic code
        return ptr;
    }

    template< class T, class U >
    SharedPointer< T > cast_ptr( SharedPointer< U > const &ptr )
    {
        return std::static_pointer_cast< T >( ptr );
    }

    template< class T, class U >
    T *cast_ptr( U *ptr )
    {
        return static_cast< T * >( ptr );
    }

    template< class T, class U >
    SharedPointer< T > dynamic_cast_ptr( SharedPointer< U > const &ptr )
    {
        return std::dynamic_pointer_cast< T >( ptr );
    }

}

#endif
<|endoftext|>