<commit_before>/*
  * Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
  *
  * Licensed under the Apache License, Version 2.0 (the "License").
  * You may not use this file except in compliance with the License.
  * A copy of the License is located at
  *
  *  http://aws.amazon.com/apache2.0
  *
  * or in the "license" file accompanying this file. This file 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 <aws/external/gtest.h>
#include <aws/core/utils/Outcome.h>
#include <aws/core/auth/AWSAuthSignerProvider.h>
#include <aws/custom-service/PetStoreClient.h>
#include <aws/custom-service/model/CreatePetRequest.h>
#include <aws/custom-service/model/GetPetRequest.h>
#include <aws/custom-service/model/GetPetsRequest.h>

using namespace Aws::Auth;
using namespace Custom::PetStore;
using namespace Custom::PetStore::Model;

namespace
{
    static const char ALLOCATION_TAG[] = "CustomServicePetStoreTest";

    // This custom AuthSignerProvider always returns NullSigner.
    class CustomAuthSignerProvider : public Aws::Auth::AWSAuthSignerProvider
    {
    public:
        CustomAuthSignerProvider() : m_defaultSigner(Aws::MakeShared<Aws::Client::AWSNullSigner>(ALLOCATION_TAG)) {}

        std::shared_ptr<Aws::Client::AWSAuthSigner> GetSigner(const Aws::String& signerName) const override
        {
            AWS_UNREFERENCED_PARAM(signerName);
            return m_defaultSigner;
        }

    private:
        std::shared_ptr<Aws::Client::AWSAuthSigner> m_defaultSigner;
    };

    class CustomServicePetStoreTest : public ::testing::Test
    {
    public:
        PetStoreClient client;
    };

    // Test the PetStore specific operations, PetStore is an example provided by API Gateway:
    // https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-create-api-from-example-console.html
    // The resources of PetStore is constant and will not be changed even though we call CreatePet successfully:
    // [{ "id": 1, "type": "dog", "price": 249.99 },
    //  { "id": 2, "type": "cat", "price": 124.99 },
    //  { "id": 3, "type": "fish", "price": 0.99 }]
    TEST_F(CustomServicePetStoreTest, TestPetStoreOperations)
    {
        // Test CreatePetRequest
        CreatePetRequest createPetRequest;
        NewPet newPet;
        newPet.SetType(PetType::bird);
        newPet.SetPrice(9.99);
        createPetRequest.SetNewPet(newPet);
        auto createPetOutcome = client.CreatePet(createPetRequest);
        ASSERT_TRUE(createPetOutcome.IsSuccess());

        // Test GetPetRequest
        GetPetRequest getPetRequest;
        getPetRequest.SetPetId(1);

        auto getPetOutcome = client.GetPet(getPetRequest);
        ASSERT_TRUE(getPetOutcome.IsSuccess());
        ASSERT_EQ("dog", getPetOutcome.GetResult().GetPet().GetType());
        ASSERT_EQ(249.99, getPetOutcome.GetResult().GetPet().GetPrice());

        // Test GetPetsRequest
        GetPetsRequest getPetsRequest;
        auto getPetsOutcome = client.GetPets(getPetsRequest);

        ASSERT_TRUE(getPetsOutcome.IsSuccess());
        auto pets = getPetsOutcome.GetResult().GetPets();
        ASSERT_EQ(3u, pets.size());
        auto pet = pets[1];
        ASSERT_EQ(2 , pet.GetId());
        ASSERT_EQ("cat", pet.GetType());
        ASSERT_EQ(124.99, pet.GetPrice());
    }

    TEST_F(CustomServicePetStoreTest, TestCustomSignerProvider)
    {
        // GetPetsRequest doesn't need signer anyway, we just test the interface to make use of custom signer provider in class constructor here.
        std::shared_ptr<Aws::Auth::AWSAuthSignerProvider> customSignerProvider = Aws::MakeShared<CustomAuthSignerProvider>(ALLOCATION_TAG);
        auto clientWithCustomSigner = Aws::MakeShared<PetStoreClient>(ALLOCATION_TAG, customSignerProvider);

        GetPetsRequest getPetsRequest;
        auto getPetsOutcome = client.GetPets(getPetsRequest);

        ASSERT_TRUE(getPetsOutcome.IsSuccess());
        ASSERT_EQ(3u, getPetsOutcome.GetResult().GetPets().size());
    }
}
<commit_msg>Add AddSigner for CustomAuthSignerProvider in custom service integration tests<commit_after>/*
  * Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
  *
  * Licensed under the Apache License, Version 2.0 (the "License").
  * You may not use this file except in compliance with the License.
  * A copy of the License is located at
  *
  *  http://aws.amazon.com/apache2.0
  *
  * or in the "license" file accompanying this file. This file 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 <aws/external/gtest.h>
#include <aws/core/utils/Outcome.h>
#include <aws/core/auth/AWSAuthSignerProvider.h>
#include <aws/custom-service/PetStoreClient.h>
#include <aws/custom-service/model/CreatePetRequest.h>
#include <aws/custom-service/model/GetPetRequest.h>
#include <aws/custom-service/model/GetPetsRequest.h>

using namespace Aws::Auth;
using namespace Custom::PetStore;
using namespace Custom::PetStore::Model;

namespace
{
    static const char ALLOCATION_TAG[] = "CustomServicePetStoreTest";

    // This custom AuthSignerProvider always returns NullSigner.
    class CustomAuthSignerProvider : public Aws::Auth::AWSAuthSignerProvider
    {
    public:
        CustomAuthSignerProvider() : m_defaultSigner(Aws::MakeShared<Aws::Client::AWSNullSigner>(ALLOCATION_TAG)) {}

        std::shared_ptr<Aws::Client::AWSAuthSigner> GetSigner(const Aws::String& signerName) const override
        {
            AWS_UNREFERENCED_PARAM(signerName);
            return m_defaultSigner;
        }

        void AddSigner(std::shared_ptr<Aws::Client::AWSAuthSigner>&)
        {
            // no-op
        }

    private:
        std::shared_ptr<Aws::Client::AWSAuthSigner> m_defaultSigner;
    };

    class CustomServicePetStoreTest : public ::testing::Test
    {
    public:
        PetStoreClient client;
    };

    // Test the PetStore specific operations, PetStore is an example provided by API Gateway:
    // https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-create-api-from-example-console.html
    // The resources of PetStore is constant and will not be changed even though we call CreatePet successfully:
    // [{ "id": 1, "type": "dog", "price": 249.99 },
    //  { "id": 2, "type": "cat", "price": 124.99 },
    //  { "id": 3, "type": "fish", "price": 0.99 }]
    TEST_F(CustomServicePetStoreTest, TestPetStoreOperations)
    {
        // Test CreatePetRequest
        CreatePetRequest createPetRequest;
        NewPet newPet;
        newPet.SetType(PetType::bird);
        newPet.SetPrice(9.99);
        createPetRequest.SetNewPet(newPet);
        auto createPetOutcome = client.CreatePet(createPetRequest);
        ASSERT_TRUE(createPetOutcome.IsSuccess());

        // Test GetPetRequest
        GetPetRequest getPetRequest;
        getPetRequest.SetPetId(1);

        auto getPetOutcome = client.GetPet(getPetRequest);
        ASSERT_TRUE(getPetOutcome.IsSuccess());
        ASSERT_EQ("dog", getPetOutcome.GetResult().GetPet().GetType());
        ASSERT_EQ(249.99, getPetOutcome.GetResult().GetPet().GetPrice());

        // Test GetPetsRequest
        GetPetsRequest getPetsRequest;
        auto getPetsOutcome = client.GetPets(getPetsRequest);

        ASSERT_TRUE(getPetsOutcome.IsSuccess());
        auto pets = getPetsOutcome.GetResult().GetPets();
        ASSERT_EQ(3u, pets.size());
        auto pet = pets[1];
        ASSERT_EQ(2 , pet.GetId());
        ASSERT_EQ("cat", pet.GetType());
        ASSERT_EQ(124.99, pet.GetPrice());
    }

    TEST_F(CustomServicePetStoreTest, TestCustomSignerProvider)
    {
        // GetPetsRequest doesn't need signer anyway, we just test the interface to make use of custom signer provider in class constructor here.
        std::shared_ptr<Aws::Auth::AWSAuthSignerProvider> customSignerProvider = Aws::MakeShared<CustomAuthSignerProvider>(ALLOCATION_TAG);
        auto clientWithCustomSigner = Aws::MakeShared<PetStoreClient>(ALLOCATION_TAG, customSignerProvider);

        GetPetsRequest getPetsRequest;
        auto getPetsOutcome = client.GetPets(getPetsRequest);

        ASSERT_TRUE(getPetsOutcome.IsSuccess());
        ASSERT_EQ(3u, getPetsOutcome.GetResult().GetPets().size());
    }
}
<|endoftext|>


<commit_before>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** No Commercial Usage
**
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** 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 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**************************************************************************/
#include "maemoremotecopyfacility.h"

#include "maemoglobal.h"
#include <QtCore/QDir>

#include <utils/ssh/sshconnection.h>
#include <utils/ssh/sshremoteprocessrunner.h>

using namespace Utils;

namespace Qt4ProjectManager {
namespace Internal {

MaemoRemoteCopyFacility::MaemoRemoteCopyFacility(QObject *parent) :
    QObject(parent), m_isCopying(false)
{
}

MaemoRemoteCopyFacility::~MaemoRemoteCopyFacility() {}

void MaemoRemoteCopyFacility::copyFiles(const SshConnection::Ptr &connection,
    const QList<MaemoDeployable> &deployables, const QString &mountPoint)
{
    Q_ASSERT(connection->state() == SshConnection::Connected);
    Q_ASSERT(!m_isCopying);

    m_deployables = deployables;
    m_mountPoint = mountPoint;

    m_copyRunner = SshRemoteProcessRunner::create(connection);
    connect(m_copyRunner.data(), SIGNAL(connectionError(Utils::SshError)),
        SLOT(handleConnectionError()));
    connect(m_copyRunner.data(), SIGNAL(processOutputAvailable(QByteArray)),
        SLOT(handleRemoteStdout(QByteArray)));
    connect(m_copyRunner.data(),
        SIGNAL(processErrorOutputAvailable(QByteArray)),
        SLOT(handleRemoteStderr(QByteArray)));
    connect(m_copyRunner.data(), SIGNAL(processClosed(int)),
        SLOT(handleCopyFinished(int)));

    m_isCopying = true;
    copyNextFile();
}

void MaemoRemoteCopyFacility::cancel()
{
    Q_ASSERT(m_isCopying);

    SshRemoteProcessRunner::Ptr killProcess
        = SshRemoteProcessRunner::create(m_copyRunner->connection());
    killProcess->run("pkill cp");
    setFinished();
}

void MaemoRemoteCopyFacility::handleConnectionError()
{
    const QString errMsg = m_copyRunner->connection()->errorString();
    setFinished();
    emit finished(tr("Connection failed: %1").arg(errMsg));
}

void MaemoRemoteCopyFacility::handleRemoteStdout(const QByteArray &output)
{
    emit stdoutData(QString::fromUtf8(output));
}

void MaemoRemoteCopyFacility::handleRemoteStderr(const QByteArray &output)
{
    emit stderrData(QString::fromUtf8(output));
}

void MaemoRemoteCopyFacility::handleCopyFinished(int exitStatus)
{
    if (!m_isCopying)
        return;

    if (exitStatus != SshRemoteProcess::ExitedNormally
            || m_copyRunner->process()->exitCode() != 0) {
        setFinished();
        emit finished(tr("Error: Copy command failed."));
    } else {
        emit fileCopied(m_deployables.takeFirst());
        copyNextFile();
    }
}

void MaemoRemoteCopyFacility::copyNextFile()
{
    Q_ASSERT(m_isCopying);

    if (m_deployables.isEmpty()) {
        setFinished();
        emit finished();
        return;
    }

    const MaemoDeployable &d = m_deployables.first();
    QString sourceFilePath = m_mountPoint;
#ifdef Q_OS_WIN
    const QString localFilePath = QDir::fromNativeSeparators(d.localFilePath);
    sourceFilePath += QLatin1Char('/') + localFilePath.at(0).toLower()
        + localFilePath.mid(2);
#else
    sourceFilePath += d.localFilePath;
#endif

    QString command = QString::fromLatin1("%1 cp -r %2 %3")
        .arg(MaemoGlobal::remoteSudo(m_copyRunner->connection()->connectionParameters().userName),
            sourceFilePath, d.remoteDir + QLatin1Char('/'));
    emit progress(tr("Copying file '%1' to directory '%2' on the device...")
        .arg(d.localFilePath, d.remoteDir));
    m_copyRunner->run(command.toUtf8());
}

void MaemoRemoteCopyFacility::setFinished()
{
    disconnect(m_copyRunner.data(), 0, this, 0);
    m_copyRunner.clear();
    m_deployables.clear();
    m_isCopying = false;
}

} // namespace Internal
} // namespace Qt4ProjectManager
<commit_msg>Maemo: Manually create directories when deploying without packaging.<commit_after>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** No Commercial Usage
**
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** 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 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**************************************************************************/
#include "maemoremotecopyfacility.h"

#include "maemoglobal.h"
#include <QtCore/QDir>

#include <utils/ssh/sshconnection.h>
#include <utils/ssh/sshremoteprocessrunner.h>

using namespace Utils;

namespace Qt4ProjectManager {
namespace Internal {

MaemoRemoteCopyFacility::MaemoRemoteCopyFacility(QObject *parent) :
    QObject(parent), m_isCopying(false)
{
}

MaemoRemoteCopyFacility::~MaemoRemoteCopyFacility() {}

void MaemoRemoteCopyFacility::copyFiles(const SshConnection::Ptr &connection,
    const QList<MaemoDeployable> &deployables, const QString &mountPoint)
{
    Q_ASSERT(connection->state() == SshConnection::Connected);
    Q_ASSERT(!m_isCopying);

    m_deployables = deployables;
    m_mountPoint = mountPoint;

    m_copyRunner = SshRemoteProcessRunner::create(connection);
    connect(m_copyRunner.data(), SIGNAL(connectionError(Utils::SshError)),
        SLOT(handleConnectionError()));
    connect(m_copyRunner.data(), SIGNAL(processOutputAvailable(QByteArray)),
        SLOT(handleRemoteStdout(QByteArray)));
    connect(m_copyRunner.data(),
        SIGNAL(processErrorOutputAvailable(QByteArray)),
        SLOT(handleRemoteStderr(QByteArray)));
    connect(m_copyRunner.data(), SIGNAL(processClosed(int)),
        SLOT(handleCopyFinished(int)));

    m_isCopying = true;
    copyNextFile();
}

void MaemoRemoteCopyFacility::cancel()
{
    Q_ASSERT(m_isCopying);

    SshRemoteProcessRunner::Ptr killProcess
        = SshRemoteProcessRunner::create(m_copyRunner->connection());
    killProcess->run("pkill cp");
    setFinished();
}

void MaemoRemoteCopyFacility::handleConnectionError()
{
    const QString errMsg = m_copyRunner->connection()->errorString();
    setFinished();
    emit finished(tr("Connection failed: %1").arg(errMsg));
}

void MaemoRemoteCopyFacility::handleRemoteStdout(const QByteArray &output)
{
    emit stdoutData(QString::fromUtf8(output));
}

void MaemoRemoteCopyFacility::handleRemoteStderr(const QByteArray &output)
{
    emit stderrData(QString::fromUtf8(output));
}

void MaemoRemoteCopyFacility::handleCopyFinished(int exitStatus)
{
    if (!m_isCopying)
        return;

    if (exitStatus != SshRemoteProcess::ExitedNormally
            || m_copyRunner->process()->exitCode() != 0) {
        setFinished();
        emit finished(tr("Error: Copy command failed."));
    } else {
        emit fileCopied(m_deployables.takeFirst());
        copyNextFile();
    }
}

void MaemoRemoteCopyFacility::copyNextFile()
{
    Q_ASSERT(m_isCopying);

    if (m_deployables.isEmpty()) {
        setFinished();
        emit finished();
        return;
    }

    const MaemoDeployable &d = m_deployables.first();
    QString sourceFilePath = m_mountPoint;
#ifdef Q_OS_WIN
    const QString localFilePath = QDir::fromNativeSeparators(d.localFilePath);
    sourceFilePath += QLatin1Char('/') + localFilePath.at(0).toLower()
        + localFilePath.mid(2);
#else
    sourceFilePath += d.localFilePath;
#endif

    QString command = QString::fromLatin1("%1 mkdir -p %3 && %1 cp -r %2 %3")
        .arg(MaemoGlobal::remoteSudo(m_copyRunner->connection()->connectionParameters().userName),
            sourceFilePath, d.remoteDir);
    emit progress(tr("Copying file '%1' to directory '%2' on the device...")
        .arg(d.localFilePath, d.remoteDir));
    m_copyRunner->run(command.toUtf8());
}

void MaemoRemoteCopyFacility::setFinished()
{
    disconnect(m_copyRunner.data(), 0, this, 0);
    m_copyRunner.clear();
    m_deployables.clear();
    m_isCopying = false;
}

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


<commit_before>////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
///
/// 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.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Kaveh Vahedipour
////////////////////////////////////////////////////////////////////////////////

#include "Inception.h"

#include "Agency/Agent.h"
#include "Agency/GossipCallback.h"
#include "Agency/MeasureCallback.h"
#include "Basics/ConditionLocker.h"
#include "Cluster/ClusterComm.h"

#include <chrono>
#include <numeric>
#include <thread>

using namespace arangodb::consensus;

Inception::Inception() : Thread("Inception"), _agent(nullptr) {}

Inception::Inception(Agent* agent) : Thread("Inception"), _agent(agent) {}

// Shutdown if not already
Inception::~Inception() { shutdown(); }

/// Gossip to others
/// - Get snapshot of gossip peers and agent pool
/// - Create outgoing gossip.
/// - Send to all peers
void Inception::gossip() {

  auto s = std::chrono::system_clock::now();
  std::chrono::seconds timeout(120);
  size_t i = 0;

  CONDITION_LOCKER(guard, _cv);

  while (!this->isStopping() && !_agent->isStopping()) {

    config_t config = _agent->config();  // get a copy of conf

    // Build gossip message
    query_t out = std::make_shared<Builder>();
    out->openObject();
    out->add("endpoint", VPackValue(config.endpoint()));
    out->add("id", VPackValue(config.id()));
    out->add("pool", VPackValue(VPackValueType::Object));
    for (auto const& i : config.pool()) {
      out->add(i.first, VPackValue(i.second));
    }
    out->close();
    out->close();

    std::string path = privApiPrefix + "gossip";

    // gossip peers
    for (auto const& p : config.gossipPeers()) {
      if (p != config.endpoint()) {
        std::string clientid = config.id() + std::to_string(i++);
        auto hf =
            std::make_unique<std::unordered_map<std::string, std::string>>();
        arangodb::ClusterComm::instance()->asyncRequest(
          clientid, 1, p, rest::RequestType::POST, path,
          std::make_shared<std::string>(out->toJson()), hf,
          std::make_shared<GossipCallback>(_agent), 1.0, true, 0.5);
      }
    }

    // pool entries
    for (auto const& pair : config.pool()) {
      if (pair.second != config.endpoint()) {
        std::string clientid = config.id() + std::to_string(i++);
        auto hf =
            std::make_unique<std::unordered_map<std::string, std::string>>();
        arangodb::ClusterComm::instance()->asyncRequest(
          clientid, 1, pair.second, rest::RequestType::POST, path,
          std::make_shared<std::string>(out->toJson()), hf,
          std::make_shared<GossipCallback>(_agent), 1.0, true, 0.5);
      }
    }

    // don't panic
    _cv.wait(100000);

    // Timed out? :(
    if ((std::chrono::system_clock::now() - s) > timeout) {
      if (config.poolComplete()) {
        LOG_TOPIC(DEBUG, Logger::AGENCY) << "Stopping active gossipping!";
      } else {
        LOG_TOPIC(ERR, Logger::AGENCY)
          << "Failed to find complete pool of agents. Giving up!";
      }
      break;
    }

    // We're done
    if (config.poolComplete()) {
      _agent->startConstituent();
      break;
    }

  }

}

// @brief Active agency from persisted database
bool Inception::activeAgencyFromPersistence() {

  auto myConfig = _agent->config();
  std::string const path = pubApiPrefix + "config";

  // Can only be done responcibly, if we are complete
  if (myConfig.poolComplete()) {

    // Contact hosts on pool in hopes of finding a leader Id
    for (auto const& pair : myConfig.pool()) {

      if (pair.first != myConfig.id()) {

        auto comres = arangodb::ClusterComm::instance()->syncRequest(
          myConfig.id(), 1, pair.second, rest::RequestType::GET, path,
          std::string(), std::unordered_map<std::string, std::string>(), 1.0);

        if (comres->status == CL_COMM_SENT) {

          auto body = comres->result->getBodyVelocyPack();
          auto theirConfig = body->slice();

          std::string leaderId;

          // LeaderId in configuration?
          try {
            leaderId = theirConfig.get("leaderId").copyString();
          } catch (std::exception const& e) {
            LOG_TOPIC(DEBUG, Logger::AGENCY)
              << "Failed to get leaderId from" << pair.second << ": "
              << e.what();
          }

          if (leaderId != "") { // Got leaderId. Let's get do it.

            try {
              LOG_TOPIC(DEBUG, Logger::AGENCY)
                << "Found active agency with leader " << leaderId
                << " at endpoint "
                << theirConfig.get("configuration").get(
                  "pool").get(leaderId).copyString();
            } catch (std::exception const& e) {
              LOG_TOPIC(DEBUG, Logger::AGENCY)
                << "Failed to get leaderId from" << pair.second << ": "
                << e.what();
            }

            auto agency = std::make_shared<Builder>();
            agency->openObject();
            agency->add("term", theirConfig.get("term"));
            agency->add("id", VPackValue(leaderId));
            agency->add("active", theirConfig.get("configuration").get("active"));
            agency->add("pool", theirConfig.get("configuration").get("pool"));
            agency->close();
            _agent->notify(agency);

            return true;

          } else { // No leaderId. Move on.

            LOG_TOPIC(DEBUG, Logger::AGENCY)
              << "Failed to get leaderId from" << pair.second;

          }

        }

      }

    }
  }

  return false;

}

bool Inception::restartingActiveAgent() {

  auto myConfig = _agent->config();
  std::string const path = pubApiPrefix + "config";

  auto s = std::chrono::system_clock::now();
  std::chrono::seconds timeout(60);

  // Can only be done responcibly, if we are complete
  if (myConfig.poolComplete()) {

    auto pool = myConfig.pool();
    auto active = myConfig.active();

    CONDITION_LOCKER(guard, _cv);

    while (!this->isStopping() && !_agent->isStopping()) {

      active.erase(
        std::remove(active.begin(), active.end(), myConfig.id()), active.end());
      active.erase(
        std::remove(active.begin(), active.end(), ""), active.end());

      if (active.empty()) {
        return true;
      }

      for (auto& i : active) {

        if (i != myConfig.id() && i != "") {

          auto clientId = myConfig.id();
          auto comres   = arangodb::ClusterComm::instance()->syncRequest(
            clientId, 1, pool.at(i), rest::RequestType::GET, path, std::string(),
            std::unordered_map<std::string, std::string>(), 2.0);

          if (comres->status == CL_COMM_SENT) {

            try {

              auto theirActive = comres->result->getBodyVelocyPack()->
                slice().get("configuration").get("active").toJson();
              auto myActive = myConfig.activeToBuilder()->toJson();

              if (theirActive != myActive) {
                LOG_TOPIC(FATAL, Logger::AGENCY)
                  << "Assumed active RAFT peer and I disagree on active membership."
                  << "Administrative intervention needed.";
                FATAL_ERROR_EXIT();
                return false;
              } else {
                i = "";
              }

            } catch (std::exception const& e) {
              LOG_TOPIC(FATAL, Logger::AGENCY)
                << "Assumed active RAFT peer has no active agency list: " << e.what()
                << "Administrative intervention needed.";
              FATAL_ERROR_EXIT();
              return false;
            }
          }
        }

      }

      // Timed out? :(
      if ((std::chrono::system_clock::now() - s) > timeout) {
        if (myConfig.poolComplete()) {
          LOG_TOPIC(DEBUG, Logger::AGENCY) << "Stopping active gossipping!";
        } else {
          LOG_TOPIC(ERR, Logger::AGENCY)
            << "Failed to find complete pool of agents. Giving up!";
        }
        break;
      }

      _cv.wait(500000);

    }
  }

  return false;

}

inline static int64_t timeStamp() {
  using namespace std::chrono;
  return duration_cast<microseconds>(
    steady_clock::now().time_since_epoch()).count();
}

void Inception::reportIn(std::string const& peerId, uint64_t start) {
  MUTEX_LOCKER(lock, _pLock);
  _pings.push_back(1.0e-3*(double)(timeStamp()-start));
}

void Inception::reportIn(query_t const& query) {

  VPackSlice slice = query->slice();

  TRI_ASSERT(slice.isObject());
  TRI_ASSERT(slice.hasKey("mean"));
  TRI_ASSERT(slice.hasKey("stdev"));
  TRI_ASSERT(slice.hasKey("min"));
  TRI_ASSERT(slice.hasKey("max"));

  MUTEX_LOCKER(lock, _mLock);
  _measurements.push_back(
    std::vector<double>(
      {slice.get("mean").getDouble(), slice.get("stdev").getDouble(),
          slice.get("max").getDouble(), slice.get("min").getDouble()} ));

}

bool Inception::estimateRAFTInterval() {

  using namespace std::chrono;

  std::string path("/_api/agency/config");
  auto pool = _agent->config().pool();
  auto myid = _agent->id();

  for (size_t i = 0; i < 25; ++i) {
    for (auto const& peer : pool) {
      if (peer.first != myid) {
        std::string clientid = peer.first + std::to_string(i);
        auto hf =
          std::make_unique<std::unordered_map<std::string, std::string>>();
        arangodb::ClusterComm::instance()->asyncRequest(
          clientid, 1, peer.second, rest::RequestType::GET, path,
          std::make_shared<std::string>(), hf,
          std::make_shared<MeasureCallback>(this, peer.second, timeStamp()),
          2.0, true);
      }
    }
  }

  auto s = system_clock::now();
  seconds timeout(3);

  CONDITION_LOCKER(guard, _cv);

  while (true) {

    _cv.wait(50000);

    {
      MUTEX_LOCKER(lock, _pLock);
      if (_pings.size() == 25*(pool.size()-1)) {
        LOG_TOPIC(DEBUG, Logger::AGENCY) << "All pings are in";
        break;
      }
    }

    if ((system_clock::now() - s) > timeout) {
      LOG_TOPIC(DEBUG, Logger::AGENCY) << "Timed out waiting for pings";
      break;
    }

  }

  double sum, mean, sq_sum, stdev, mx, mn;

  try {

    MUTEX_LOCKER(lock, _pLock);
    size_t num = _pings.size();
    sum    = std::accumulate(_pings.begin(), _pings.end(), 0.0);
    mean   = sum / num;
    mx     = *std::max_element(_pings.begin(), _pings.end());
    mn     = *std::min_element(_pings.begin(), _pings.end());
    std::transform(_pings.begin(), _pings.end(), _pings.begin(),
                   std::bind2nd(std::minus<double>(), mean));
    sq_sum =
      std::inner_product(_pings.begin(), _pings.end(), _pings.begin(), 0.0);
    stdev = std::sqrt(sq_sum / num);

    LOG_TOPIC(DEBUG, Logger::AGENCY)
      << "mean(" << mean << ") stdev(" << stdev<< ")";

  } catch (std::exception const& e) {
    LOG_TOPIC(WARN, Logger::AGENCY) << e.what();
  }

  Builder measurement;
  measurement.openObject();
  measurement.add("mean", VPackValue(mean));
  measurement.add("stdev", VPackValue(stdev));
  measurement.add("min", VPackValue(mn));
  measurement.add("max", VPackValue(mx));
  measurement.close();
  std::string measjson = measurement.toJson();

  path = privApiPrefix + "measure";
  for (auto const& peer : pool) {
    if (peer.first != myid) {
      auto clientId = "1";
      auto comres   = arangodb::ClusterComm::instance()->syncRequest(
        clientId, 1, peer.second, rest::RequestType::POST, path,
        measjson, std::unordered_map<std::string, std::string>(), 2.0);
    }
  }

  {
    MUTEX_LOCKER(lock, _mLock);
    _measurements.push_back(std::vector<double>({mean, stdev, mx, mn}));
  }
  s = system_clock::now();
  while (true) {

    _cv.wait(50000);

    {
      MUTEX_LOCKER(lock, _mLock);
      if (_measurements.size() == pool.size()) {
        LOG_TOPIC(DEBUG, Logger::AGENCY) << "All measurements are in";
        break;
      }
    }

    if ((system_clock::now() - s) > timeout) {
      LOG_TOPIC(WARN, Logger::AGENCY)
        << "Timed out waiting for other measurements. Auto-adaptation failed!";
      return false;
    }

  }

  double maxmean  = .0;
  double maxstdev = .0;
  for (auto const& meas : _measurements) {
    if (maxmean < meas[0]) {
      maxmean = meas[0];
    }
    if (maxstdev < meas[1]) {
      maxstdev = meas[1];
    }
  }

  LOG_TOPIC(INFO, Logger::AGENCY)
    << "Auto-adapting RAFT timing to: " << 5.*maxmean << " " << 25.*maxmean;

  _agent->resetRAFTTimes(5.e-3*maxmean, 25.e-3*maxmean);

  return true;

}

// @brief Active agency from persisted database
bool Inception::activeAgencyFromCommandLine() {
  return false;
}

// @brief Thread main
void Inception::run() {

  // 1. If active agency, do as you're told
  if (activeAgencyFromPersistence()) {
    _agent->ready(true);
  }

  // 2. If we think that we used to be active agent
  if (!_agent->ready() && restartingActiveAgent()) {
    _agent->ready(true);
  }

  // 3. Else gossip
  config_t config = _agent->config();
  if (!_agent->ready() && !config.poolComplete()) {
    gossip();
  }

  // 4. If still incomplete bail out :(
  config = _agent->config();
  if (!_agent->ready() && !config.poolComplete()) {
    LOG_TOPIC(FATAL, Logger::AGENCY)
      << "Failed to build environment for RAFT algorithm. Bailing out!";
    FATAL_ERROR_EXIT();
  }

  estimateRAFTInterval();

  _agent->ready(true);

}

// @brief Graceful shutdown
void Inception::beginShutdown() {
  Thread::beginShutdown();
  CONDITION_LOCKER(guard, _cv);
  guard.broadcast();
}
<commit_msg>RAFT timeout estimation output<commit_after>////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
///
/// 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.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Kaveh Vahedipour
////////////////////////////////////////////////////////////////////////////////

#include "Inception.h"

#include "Agency/Agent.h"
#include "Agency/GossipCallback.h"
#include "Agency/MeasureCallback.h"
#include "Basics/ConditionLocker.h"
#include "Cluster/ClusterComm.h"

#include <chrono>
#include <numeric>
#include <thread>

using namespace arangodb::consensus;

Inception::Inception() : Thread("Inception"), _agent(nullptr) {}

Inception::Inception(Agent* agent) : Thread("Inception"), _agent(agent) {}

// Shutdown if not already
Inception::~Inception() { shutdown(); }

/// Gossip to others
/// - Get snapshot of gossip peers and agent pool
/// - Create outgoing gossip.
/// - Send to all peers
void Inception::gossip() {

  auto s = std::chrono::system_clock::now();
  std::chrono::seconds timeout(120);
  size_t i = 0;

  CONDITION_LOCKER(guard, _cv);

  while (!this->isStopping() && !_agent->isStopping()) {

    config_t config = _agent->config();  // get a copy of conf

    // Build gossip message
    query_t out = std::make_shared<Builder>();
    out->openObject();
    out->add("endpoint", VPackValue(config.endpoint()));
    out->add("id", VPackValue(config.id()));
    out->add("pool", VPackValue(VPackValueType::Object));
    for (auto const& i : config.pool()) {
      out->add(i.first, VPackValue(i.second));
    }
    out->close();
    out->close();

    std::string path = privApiPrefix + "gossip";

    // gossip peers
    for (auto const& p : config.gossipPeers()) {
      if (p != config.endpoint()) {
        std::string clientid = config.id() + std::to_string(i++);
        auto hf =
            std::make_unique<std::unordered_map<std::string, std::string>>();
        arangodb::ClusterComm::instance()->asyncRequest(
          clientid, 1, p, rest::RequestType::POST, path,
          std::make_shared<std::string>(out->toJson()), hf,
          std::make_shared<GossipCallback>(_agent), 1.0, true, 0.5);
      }
    }

    // pool entries
    for (auto const& pair : config.pool()) {
      if (pair.second != config.endpoint()) {
        std::string clientid = config.id() + std::to_string(i++);
        auto hf =
            std::make_unique<std::unordered_map<std::string, std::string>>();
        arangodb::ClusterComm::instance()->asyncRequest(
          clientid, 1, pair.second, rest::RequestType::POST, path,
          std::make_shared<std::string>(out->toJson()), hf,
          std::make_shared<GossipCallback>(_agent), 1.0, true, 0.5);
      }
    }

    // don't panic
    _cv.wait(100000);

    // Timed out? :(
    if ((std::chrono::system_clock::now() - s) > timeout) {
      if (config.poolComplete()) {
        LOG_TOPIC(DEBUG, Logger::AGENCY) << "Stopping active gossipping!";
      } else {
        LOG_TOPIC(ERR, Logger::AGENCY)
          << "Failed to find complete pool of agents. Giving up!";
      }
      break;
    }

    // We're done
    if (config.poolComplete()) {
      _agent->startConstituent();
      break;
    }

  }

}

// @brief Active agency from persisted database
bool Inception::activeAgencyFromPersistence() {

  auto myConfig = _agent->config();
  std::string const path = pubApiPrefix + "config";

  // Can only be done responcibly, if we are complete
  if (myConfig.poolComplete()) {

    // Contact hosts on pool in hopes of finding a leader Id
    for (auto const& pair : myConfig.pool()) {

      if (pair.first != myConfig.id()) {

        auto comres = arangodb::ClusterComm::instance()->syncRequest(
          myConfig.id(), 1, pair.second, rest::RequestType::GET, path,
          std::string(), std::unordered_map<std::string, std::string>(), 1.0);

        if (comres->status == CL_COMM_SENT) {

          auto body = comres->result->getBodyVelocyPack();
          auto theirConfig = body->slice();

          std::string leaderId;

          // LeaderId in configuration?
          try {
            leaderId = theirConfig.get("leaderId").copyString();
          } catch (std::exception const& e) {
            LOG_TOPIC(DEBUG, Logger::AGENCY)
              << "Failed to get leaderId from" << pair.second << ": "
              << e.what();
          }

          if (leaderId != "") { // Got leaderId. Let's get do it.

            try {
              LOG_TOPIC(DEBUG, Logger::AGENCY)
                << "Found active agency with leader " << leaderId
                << " at endpoint "
                << theirConfig.get("configuration").get(
                  "pool").get(leaderId).copyString();
            } catch (std::exception const& e) {
              LOG_TOPIC(DEBUG, Logger::AGENCY)
                << "Failed to get leaderId from" << pair.second << ": "
                << e.what();
            }

            auto agency = std::make_shared<Builder>();
            agency->openObject();
            agency->add("term", theirConfig.get("term"));
            agency->add("id", VPackValue(leaderId));
            agency->add("active", theirConfig.get("configuration").get("active"));
            agency->add("pool", theirConfig.get("configuration").get("pool"));
            agency->close();
            _agent->notify(agency);

            return true;

          } else { // No leaderId. Move on.

            LOG_TOPIC(DEBUG, Logger::AGENCY)
              << "Failed to get leaderId from" << pair.second;

          }

        }

      }

    }
  }

  return false;

}

bool Inception::restartingActiveAgent() {

  auto myConfig = _agent->config();
  std::string const path = pubApiPrefix + "config";

  auto s = std::chrono::system_clock::now();
  std::chrono::seconds timeout(60);

  // Can only be done responcibly, if we are complete
  if (myConfig.poolComplete()) {

    auto pool = myConfig.pool();
    auto active = myConfig.active();

    CONDITION_LOCKER(guard, _cv);

    while (!this->isStopping() && !_agent->isStopping()) {

      active.erase(
        std::remove(active.begin(), active.end(), myConfig.id()), active.end());
      active.erase(
        std::remove(active.begin(), active.end(), ""), active.end());

      if (active.empty()) {
        return true;
      }

      for (auto& i : active) {

        if (i != myConfig.id() && i != "") {

          auto clientId = myConfig.id();
          auto comres   = arangodb::ClusterComm::instance()->syncRequest(
            clientId, 1, pool.at(i), rest::RequestType::GET, path, std::string(),
            std::unordered_map<std::string, std::string>(), 2.0);

          if (comres->status == CL_COMM_SENT) {

            try {

              auto theirActive = comres->result->getBodyVelocyPack()->
                slice().get("configuration").get("active").toJson();
              auto myActive = myConfig.activeToBuilder()->toJson();

              if (theirActive != myActive) {
                LOG_TOPIC(FATAL, Logger::AGENCY)
                  << "Assumed active RAFT peer and I disagree on active membership."
                  << "Administrative intervention needed.";
                FATAL_ERROR_EXIT();
                return false;
              } else {
                i = "";
              }

            } catch (std::exception const& e) {
              LOG_TOPIC(FATAL, Logger::AGENCY)
                << "Assumed active RAFT peer has no active agency list: " << e.what()
                << "Administrative intervention needed.";
              FATAL_ERROR_EXIT();
              return false;
            }
          }
        }

      }

      // Timed out? :(
      if ((std::chrono::system_clock::now() - s) > timeout) {
        if (myConfig.poolComplete()) {
          LOG_TOPIC(DEBUG, Logger::AGENCY) << "Stopping active gossipping!";
        } else {
          LOG_TOPIC(ERR, Logger::AGENCY)
            << "Failed to find complete pool of agents. Giving up!";
        }
        break;
      }

      _cv.wait(500000);

    }
  }

  return false;

}

inline static int64_t timeStamp() {
  using namespace std::chrono;
  return duration_cast<microseconds>(
    steady_clock::now().time_since_epoch()).count();
}

void Inception::reportIn(std::string const& peerId, uint64_t start) {
  MUTEX_LOCKER(lock, _pLock);
  _pings.push_back(1.0e-3*(double)(timeStamp()-start));
}

void Inception::reportIn(query_t const& query) {

  VPackSlice slice = query->slice();

  TRI_ASSERT(slice.isObject());
  TRI_ASSERT(slice.hasKey("mean"));
  TRI_ASSERT(slice.hasKey("stdev"));
  TRI_ASSERT(slice.hasKey("min"));
  TRI_ASSERT(slice.hasKey("max"));

  MUTEX_LOCKER(lock, _mLock);
  _measurements.push_back(
    std::vector<double>(
      {slice.get("mean").getDouble(), slice.get("stdev").getDouble(),
          slice.get("max").getDouble(), slice.get("min").getDouble()} ));

}

bool Inception::estimateRAFTInterval() {

  using namespace std::chrono;

  std::string path("/_api/agency/config");
  auto pool = _agent->config().pool();
  auto myid = _agent->id();

  for (size_t i = 0; i < 25; ++i) {
    for (auto const& peer : pool) {
      if (peer.first != myid) {
        std::string clientid = peer.first + std::to_string(i);
        auto hf =
          std::make_unique<std::unordered_map<std::string, std::string>>();
        arangodb::ClusterComm::instance()->asyncRequest(
          clientid, 1, peer.second, rest::RequestType::GET, path,
          std::make_shared<std::string>(), hf,
          std::make_shared<MeasureCallback>(this, peer.second, timeStamp()),
          2.0, true);
      }
    }
  }

  auto s = system_clock::now();
  seconds timeout(3);

  CONDITION_LOCKER(guard, _cv);

  while (true) {

    _cv.wait(50000);

    {
      MUTEX_LOCKER(lock, _pLock);
      if (_pings.size() == 25*(pool.size()-1)) {
        LOG_TOPIC(DEBUG, Logger::AGENCY) << "All pings are in";
        break;
      }
    }

    if ((system_clock::now() - s) > timeout) {
      LOG_TOPIC(DEBUG, Logger::AGENCY) << "Timed out waiting for pings";
      break;
    }

  }

  double sum, mean, sq_sum, stdev, mx, mn;

  try {

    MUTEX_LOCKER(lock, _pLock);
    size_t num = _pings.size();
    sum    = std::accumulate(_pings.begin(), _pings.end(), 0.0);
    mean   = sum / num;
    mx     = *std::max_element(_pings.begin(), _pings.end());
    mn     = *std::min_element(_pings.begin(), _pings.end());
    std::transform(_pings.begin(), _pings.end(), _pings.begin(),
                   std::bind2nd(std::minus<double>(), mean));
    sq_sum =
      std::inner_product(_pings.begin(), _pings.end(), _pings.begin(), 0.0);
    stdev = std::sqrt(sq_sum / num);

    LOG_TOPIC(DEBUG, Logger::AGENCY)
      << "mean(" << mean << ") stdev(" << stdev<< ")";

  } catch (std::exception const& e) {
    LOG_TOPIC(WARN, Logger::AGENCY) << e.what();
  }

  Builder measurement;
  measurement.openObject();
  measurement.add("mean", VPackValue(mean));
  measurement.add("stdev", VPackValue(stdev));
  measurement.add("min", VPackValue(mn));
  measurement.add("max", VPackValue(mx));
  measurement.close();
  std::string measjson = measurement.toJson();

  path = privApiPrefix + "measure";
  for (auto const& peer : pool) {
    if (peer.first != myid) {
      auto clientId = "1";
      auto comres   = arangodb::ClusterComm::instance()->syncRequest(
        clientId, 1, peer.second, rest::RequestType::POST, path,
        measjson, std::unordered_map<std::string, std::string>(), 2.0);
    }
  }

  {
    MUTEX_LOCKER(lock, _mLock);
    _measurements.push_back(std::vector<double>({mean, stdev, mx, mn}));
  }
  s = system_clock::now();
  while (true) {

    _cv.wait(50000);

    {
      MUTEX_LOCKER(lock, _mLock);
      if (_measurements.size() == pool.size()) {
        LOG_TOPIC(DEBUG, Logger::AGENCY) << "All measurements are in";
        break;
      }
    }

    if ((system_clock::now() - s) > timeout) {
      LOG_TOPIC(WARN, Logger::AGENCY)
        << "Timed out waiting for other measurements. Auto-adaptation failed! Will stick to command line arguments";
      return false;
    }

  }

  double maxmean  = .0;
  double maxstdev = .0;
  for (auto const& meas : _measurements) {
    if (maxmean < meas[0]) {
      maxmean = meas[0];
    }
    if (maxstdev < meas[1]) {
      maxstdev = meas[1];
    }
  }

  LOG_TOPIC(INFO, Logger::AGENCY)
    << "Auto-adapting RAFT timing to: " << 5.*maxmean << " " << 25.*maxmean;

  _agent->resetRAFTTimes(5.e-3*maxmean, 25.e-3*maxmean);

  return true;

}

// @brief Active agency from persisted database
bool Inception::activeAgencyFromCommandLine() {
  return false;
}

// @brief Thread main
void Inception::run() {

  // 1. If active agency, do as you're told
  if (activeAgencyFromPersistence()) {
    _agent->ready(true);
  }

  // 2. If we think that we used to be active agent
  if (!_agent->ready() && restartingActiveAgent()) {
    _agent->ready(true);
  }

  // 3. Else gossip
  config_t config = _agent->config();
  if (!_agent->ready() && !config.poolComplete()) {
    gossip();
  }

  // 4. If still incomplete bail out :(
  config = _agent->config();
  if (!_agent->ready() && !config.poolComplete()) {
    LOG_TOPIC(FATAL, Logger::AGENCY)
      << "Failed to build environment for RAFT algorithm. Bailing out!";
    FATAL_ERROR_EXIT();
  }

  estimateRAFTInterval();

  _agent->ready(true);

}

// @brief Graceful shutdown
void Inception::beginShutdown() {
  Thread::beginShutdown();
  CONDITION_LOCKER(guard, _cv);
  guard.broadcast();
}
<|endoftext|>


<commit_before>#include <thread>
#include <atomic>
#include <mutex>
#include <condition_variable>
#include <iostream>
using namespace std;

std::mutex mut;
std::condition_variable cond;
bool go = false;

typedef char buffer[4096*2];
using atom = std::atomic<size_t>;
typedef void (*fnc_type)(size_t, size_t);

atom lock_instr_test;

struct {
    atom v1;
    atom v2;
} __attribute__((aligned(64))) test_same_line;

struct test_different_line {
    buffer back;
    atom value;
    buffer front;
};

test_different_line *lines;

void test_single_add(size_t id, size_t nrun) {
    for (size_t i = 0; i < nrun; i++) {
        lock_instr_test.fetch_add(1, std::memory_order_relaxed);
    }
}

void test_single_cas(size_t id, size_t nrun) {
    size_t dummy;
    for (size_t i = 0; i < nrun; i++) {
        while (!lock_instr_test.compare_exchange_weak(dummy,
                                                      dummy+1,
                                                      std::memory_order_relaxed,
                                                      std::memory_order_relaxed))
        {}
    }
}

void test_mfence(size_t id, size_t nrun) {
    for (size_t i = 0; i < nrun; i++) {
        std::atomic_thread_fence(std::memory_order_seq_cst);
    }
}

void test_same_line_f(size_t id, size_t nrun) {
    atom *a;
    if (id % 2) {
        a = &test_same_line.v1;
    }
    else {
        a = &test_same_line.v2;
    }

    for (size_t i = 0; i < nrun; i++) {
        a->fetch_add(1, std::memory_order_relaxed);
    }
}

void test_different_line_f(size_t id, size_t nrun) {
    atom *a = &lines[id].value;
    for (size_t i = 0; i < nrun; i++) {
        a->fetch_add(1, std::memory_order_relaxed);
    }
}

void time_threads(size_t ntesters, size_t nrun, fnc_type op, std::string name) {
    std::thread *ths = new std::thread[ntesters];
    for (size_t i = 0; i < ntesters; i++) {
        ths[i] = std::thread([&]() {
            {
                std::unique_lock<std::mutex> lck(mut);
                cond.wait(lck, [&]() {return go; });
            }
            op(i, nrun);
        });
    }
    std::this_thread::sleep_for(std::chrono::milliseconds(5));
    auto cclock = std::chrono::steady_clock::now();
    go = true;
    cond.notify_all();
    for (size_t i = 0; i < ntesters; i++) {
        ths[i].join();
    }
    auto diff = std::chrono::steady_clock::now() - cclock;
    auto td = chrono::duration_cast<chrono::milliseconds>(diff).count() - 5;
    double tdiff = ((double)td / 1000.0);
    auto nthread = ntesters;
    auto total_elem = (nthread * nrun); //* 2 since popping them all
    auto elempt = total_elem / tdiff;
    auto elemptpt = elempt / nthread;
    cout << "Took " << tdiff
         << " seconds for " << nthread << " threads and "
         << nrun << " elements per thread" << endl
         << "This amounts to " << elemptpt
         << " operations per thread per second "
         << "for " << name << endl << endl;
    delete[] ths;
}

int main() {
    size_t num_test = 3e7;
    for (size_t i = 1; i <= 10; i++) {
        time_threads(i, num_test, test_single_add, "same add");
        time_threads(i, num_test, test_single_cas, "same cas");
        time_threads(i, num_test, test_mfence, "mfence");
        time_threads(i, num_test, test_same_line_f, "same line");
        lines = new test_different_line[i+1];
        time_threads(i, num_test, test_different_line_f, "different_lines");
        delete[] lines;
        cout << endl << endl;
    }

    return 0;
}
<commit_msg>adding nanoseconds per element<commit_after>#include <thread>
#include <atomic>
#include <mutex>
#include <condition_variable>
#include <iostream>
using namespace std;

std::mutex mut;
std::condition_variable cond;
bool go = false;

typedef char buffer[4096*2];
using atom = std::atomic<size_t>;
typedef void (*fnc_type)(size_t, size_t);

atom lock_instr_test;

struct {
    atom v1;
    atom v2;
} __attribute__((aligned(64))) test_same_line;

struct test_different_line {
    buffer back;
    atom value;
    buffer front;
};

test_different_line *lines;

void test_single_add(size_t id, size_t nrun) {
    for (size_t i = 0; i < nrun; i++) {
        lock_instr_test.fetch_add(1, std::memory_order_relaxed);
    }
}

void test_single_cas(size_t id, size_t nrun) {
    size_t dummy;
    for (size_t i = 0; i < nrun; i++) {
        while (!lock_instr_test.compare_exchange_weak(dummy,
                                                      dummy+1,
                                                      std::memory_order_relaxed,
                                                      std::memory_order_relaxed))
        {}
    }
}

void test_mfence(size_t id, size_t nrun) {
    for (size_t i = 0; i < nrun; i++) {
        std::atomic_thread_fence(std::memory_order_seq_cst);
    }
}

void test_same_line_f(size_t id, size_t nrun) {
    atom *a;
    if (id % 2) {
        a = &test_same_line.v1;
    }
    else {
        a = &test_same_line.v2;
    }

    for (size_t i = 0; i < nrun; i++) {
        a->fetch_add(1, std::memory_order_relaxed);
    }
}

void test_different_line_f(size_t id, size_t nrun) {
    atom *a = &lines[id].value;
    for (size_t i = 0; i < nrun; i++) {
        a->fetch_add(1, std::memory_order_relaxed);
    }
}

void time_threads(size_t ntesters, size_t nrun, fnc_type op, std::string name) {
    std::thread *ths = new std::thread[ntesters];
    for (size_t i = 0; i < ntesters; i++) {
        ths[i] = std::thread([&]() {
            {
                std::unique_lock<std::mutex> lck(mut);
                cond.wait(lck, [&]() {return go; });
            }
            op(i, nrun);
        });
    }
    std::this_thread::sleep_for(std::chrono::milliseconds(5));
    auto cclock = std::chrono::steady_clock::now();
    go = true;
    cond.notify_all();
    for (size_t i = 0; i < ntesters; i++) {
        ths[i].join();
    }
    auto diff = std::chrono::steady_clock::now() - cclock;
    auto td = chrono::duration_cast<chrono::milliseconds>(diff).count();
    double tdiff = ((double)td / 1000.0);
    auto nthread = ntesters;
    auto total_elem = (nthread * nrun); //* 2 since popping them all
    auto elempt = total_elem / tdiff;
    auto elemptpt = elempt / nthread;
    auto ns_per_elem = 1e9 * tdiff / nrun;
    cout << "Took " << tdiff
         << " seconds for " << nthread << " threads and "
         << nrun << " elements per thread" << endl
         << "This amounts to " << ns_per_elem
         << " nanoseconds per operation "
         << "for " << name << endl << endl;
    delete[] ths;
}

int main() {
    size_t num_test = 1e8;
    for (size_t i = 1; i <= 10; i++) {
        time_threads(i, num_test, test_single_add, "same add");
        time_threads(i, num_test, test_single_cas, "same cas");
        time_threads(i, num_test, test_mfence, "mfence");
        time_threads(i, num_test, test_same_line_f, "same line");
        lines = new test_different_line[i+1];
        time_threads(i, num_test, test_different_line_f, "different_lines");
        delete[] lines;
        cout << endl << endl;
    }

    return 0;
}
<|endoftext|>


<commit_before>#include "Scaly.h"
namespace scaly {

_Page::_Page(size_t size)
: currentPage(this), nextPage(0), previousPage(0), size(size), extendingPage(0) {
	reset();
}

_Page** _Page::getLastExtensionPageLocation() {
    return ((_Page**) ((char*) this + size)) - 1;
}

_Page* _Page::next() {
    if (nextPage == 0)
        nextPage = allocateNextPage(this);
    else
        nextPage->reset();

    return nextPage;
}

_Page* _Page::previous() {
    return previousPage;
}

void* _Page::operator new(size_t size, void* location) {
    return location;
}

void* _Page::allocateObject(size_t size) {
    if (this != currentPage) {
        // We're already known to be full, so we delegate to the current page
        void* newObject = currentPage->allocateObject(size);

        // Possibly our current page was also full so we propagate back the new current page
        _Page* allocatingPage = ((Object*)newObject)->getPage();
        if ((allocatingPage != currentPage) && (allocatingPage->getSize() == _Page::pageSize))
            currentPage = allocatingPage;

        return newObject;
    }

    // Try to allocate from ourselves
    void* nextLocation = align((char*) nextObject + size);
    if (nextLocation <= (void*) nextExtensionPageLocation) {
        void* location = nextObject;
        nextObject = nextLocation;
        return location;
    }

    // So the space did not fit.

    // Check first whether we need an ordinary extension
    if ((_Page**)nextObject == nextExtensionPageLocation) {
        // Allocate an extension page with default size
        _Page* defaultExtensionPage = allocateExtensionPage(1);
        // Make it our new current
        currentPage = defaultExtensionPage;
        // Try again with the new extension page
        return defaultExtensionPage->allocateObject(size);
    }

    // Make the extension page with the desired size.
    _Page* extensionPage = allocateExtensionPage(size);

    // If it was not an oversize page
    if (extensionPage->getSize() <= _Page::pageSize) {
        // If we are pretty full, make it the new current page
        if (((void*)nextObject) >= (void*)nextExtensionPageLocation )
            currentPage = extensionPage;
    }

    return extensionPage->allocateObject(size);
}

_Page* _Page::allocateExtensionPage(size_t size) {
    size_t grossSize = size + sizeof(_Page) + sizeof(_Page**);
    if (grossSize < pageSize) {
        grossSize = pageSize;
    }
    else if (grossSize > pageSize) {
        grossSize += _alignment;
        grossSize &= ~(_alignment - 1);
    }

    _Page* pageLocation = allocate(grossSize);
    if (!pageLocation)
        return 0;

    *nextExtensionPageLocation = pageLocation;
    nextExtensionPageLocation--;
    _Page* extensionPage = new (pageLocation) _Page(grossSize);
    extensionPage->extendingPage = this;
    return extensionPage;
}

_Page* _Page::allocateNextPage(_Page* previousPage) {
    void* pageLocation = allocate(pageSize);
    if (!pageLocation)
        return 0;

    _Page* nextPage = new (pageLocation) _Page(pageSize);
    nextPage->previousPage = previousPage;
    return nextPage;
}

void _Page::reset() {
    nextExtensionPageLocation = getLastExtensionPageLocation();
    *nextExtensionPageLocation = 0;
    nextObject = align((char*) this + sizeof(_Page));

}

_Page* _Page::allocate(size_t size) {
    void* pageLocation;

    if (posix_memalign(&pageLocation, pageSize, size))
        return 0;
    else
        return (_Page*)pageLocation;
}

bool _Page::extend(void* address, size_t size) {
    if (!size)
        size = 1;

    void* nextLocation = align((char*) address + size);

    if (nextLocation == (void*) nextObject)
        return true;

    if (nextLocation < nextObject)
        return false;

    if (nextLocation >= (void*) nextExtensionPageLocation)
        return false;

    nextObject = nextLocation;
    return true;
}

void _Page::deallocate() {
    // Find the end of the stack Page chain
    _Page* page = this;
    while (page)
        if (page->nextPage == 0)
            break;
        else
            page = page->nextPage;

    // Deallocate the chain starting from the end
    while (page) {
        _Page* previousPage = page->previousPage;
        page->deallocatePageExtensions();
        free(page);
        page = previousPage;
    }
}

void _Page::deallocatePageExtensions() {
    _Page** currentExtensionPosition = this->getLastExtensionPageLocation();
    while (currentExtensionPosition > this->nextExtensionPageLocation) {
        _Page* currentExtension = *currentExtensionPosition;
        if (currentExtension)
            currentExtension->deallocate();
        currentExtensionPosition--;
    }
}

void _Page::reclaimArray(void* address) {
    _Page* page = ((Object*)address)->getPage();
    page->extendingPage->freeExtensionPage(page);
}

_Page* _Page::getPage(void* address) {
    return (_Page*)
    (
        ((intptr_t)address)

    & ~(intptr_t)(pageSize - 1));
}

void _Page::freeExtensionPage(_Page* _page) {
    // Find the extension Page pointer
    _Page** extensionPosition = getLastExtensionPageLocation();
    while (extensionPosition > nextExtensionPageLocation) {
        if (*extensionPosition == _page)
            break;

        extensionPosition--;
    }

    // Shift the remaining array one position up
    for (_Page** shiftedPosition = extensionPosition; shiftedPosition > nextExtensionPageLocation; shiftedPosition--)
        *shiftedPosition = *(shiftedPosition - 1);

    // Make room for one more extension
    nextExtensionPageLocation++;

    _page->deallocate();
}

size_t _Page::getSize() {
    return size;
}

} // namespace
<commit_msg>Page allocation<commit_after>#include "Scaly.h"
namespace scaly {

_Page::_Page(size_t size)
: currentPage(this), nextPage(0), previousPage(0), size(size), extendingPage(0) {
	reset();
}

_Page** _Page::getLastExtensionPageLocation() {
    // Frome where the ordinary extension page is pointed to
    return ((_Page**) ((char*) this + size)) - 1;
}

_Page* _Page::next() {
    if (nextPage == 0)
        // This frame is new
        nextPage = allocateNextPage(this);
    else
        // We've already been at that frame, we re-use it
        nextPage->reset();

    return nextPage;
}

_Page* _Page::previous() {
    return previousPage;
}

void* _Page::operator new(size_t size, void* location) {
    return location;
}

void* _Page::allocateObject(size_t size) {
    if (this != currentPage) {
        // We're already known to be full, so we delegate to the current page
        void* newObject = currentPage->allocateObject(size);

        // Possibly our current page was also full so we propagate back the new current page
        _Page* allocatingPage = ((Object*)newObject)->getPage();
        if ((allocatingPage != currentPage) && (allocatingPage->getSize() == _Page::pageSize))
            currentPage = allocatingPage;

        return newObject;
    }

    // Try to allocate from ourselves
    void* nextLocation = align((char*) nextObject + size);
    if (nextLocation <= (void*) nextExtensionPageLocation) {
        void* location = nextObject;
        nextObject = nextLocation;
        return location;
    }

    // So the space did not fit.

    // Check first whether we need an ordinary extension
    size_t grossSize = sizeof(_Page) + size + sizeof(_Page**) + _alignment;

    if ((_Page**)nextObject == getLastExtensionPageLocation() || (grossSize < pageSize)) {
        // Allocate an extension page with default size
        _Page* defaultExtensionPage = allocateExtensionPage(1);
        // Make it our new current
        currentPage = defaultExtensionPage;
        // Try again with the new extension page
        return defaultExtensionPage->allocateObject(size);
    }

    // Make the oversized extension page.
    _Page* extensionPage = allocateExtensionPage(size);
    return extensionPage->allocateObject(size);
}

_Page* _Page::allocateExtensionPage(size_t size) {
    size_t grossSize = sizeof(_Page) + size + sizeof(_Page**) + _alignment;
    if (grossSize < pageSize) {
        grossSize = pageSize;
    }
    else if (grossSize > pageSize) {
        grossSize += _alignment;
        grossSize &= ~(_alignment - 1);
    }

    _Page* pageLocation = allocate(grossSize);
    if (!pageLocation)
        return 0;

    _Page* extensionPage = new (pageLocation) _Page(grossSize);

    if (grossSize > pageSize) {
        *nextExtensionPageLocation = pageLocation;
        nextExtensionPageLocation--;
    }
    else {
        *getLastExtensionPageLocation() = pageLocation;
    }

    extensionPage->extendingPage = this;
    return extensionPage;
}

_Page* _Page::allocateNextPage(_Page* previousPage) {
    void* pageLocation = allocate(pageSize);
    if (!pageLocation)
        return 0;

    _Page* nextPage = new (pageLocation) _Page(pageSize);
    nextPage->previousPage = previousPage;
    return nextPage;
}

void _Page::reset() {
    // Allocate default extension page pointer and initialize it to zero
    nextExtensionPageLocation = getLastExtensionPageLocation();
    *nextExtensionPageLocation = 0;
    nextExtensionPageLocation--;
    // Allocate space for the page itself
    nextObject = align((char*) this + sizeof(_Page));
}

_Page* _Page::allocate(size_t size) {
    void* pageLocation;

    if (posix_memalign(&pageLocation, pageSize, size))
        return 0;
    else
        return (_Page*)pageLocation;
}

bool _Page::extend(void* address, size_t size) {
    if (!size)
        size = 1;

    void* nextLocation = align((char*) address + size);

    // If nextObject would not change because of the alignment, that's it
    if (nextLocation == (void*) nextObject)
        return true;

    // If nextObject is already higher, other objects were allocated in the meantime
    if (nextLocation < nextObject)
        return false;

    // Now we still have to check whether we still would have space left
    if (nextLocation >= (void*) nextExtensionPageLocation)
        return false;

    // Allocate the extension
    nextObject = nextLocation;
    return true;
}

void _Page::deallocate() {
    // Find the end of the stack Page chain
    _Page* page = this;
    while (page)
        if (page->nextPage == 0)
            break;
        else
            page = page->nextPage;

    // Deallocate the chain starting from the end
    while (page) {
        _Page* prevPage = page->previousPage;
        page->deallocatePageExtensions();
        free(page);
        page = prevPage;
    }
}

void _Page::deallocatePageExtensions() {
    // Find the end of the extension Page chain
    _Page* page = this;
    while (page)
        if (*(page->getLastExtensionPageLocation()) == 0)
            break;
        else
            page = *(page->getLastExtensionPageLocation());

    // Deallocate the extension page chain starting from the end
    while (page) {
        _Page* prevExtPage = page->extendingPage;
        // Deallocate the oversized pages
        for (_Page** ppPage = page->getLastExtensionPageLocation(); ppPage > page->nextExtensionPageLocation; ppPage--)
            free(*ppPage);

        page = prevExtPage;
    }
}

void _Page::reclaimArray(void* address) {
    _Page* page = ((Object*)address)->getPage();
    page->extendingPage->freeExtensionPage(page);
}

_Page* _Page::getPage(void* address) {
    return (_Page*)
    (
        ((intptr_t)address)

    & ~(intptr_t)(pageSize - 1));
}

void _Page::freeExtensionPage(_Page* _page) {
    // Find the extension Page pointer
    _Page** extensionPosition = getLastExtensionPageLocation() - 1;
    while (extensionPosition > nextExtensionPageLocation) {
        if (*extensionPosition == _page)
            break;

        extensionPosition--;
    }

    // Shift the remaining array one position up
    for (_Page** shiftedPosition = extensionPosition; shiftedPosition > nextExtensionPageLocation; shiftedPosition--)
        *shiftedPosition = *(shiftedPosition - 1);

    // Make room for one more extension
    nextExtensionPageLocation++;

    free(_page);
}

size_t _Page::getSize() {
    return size;
}

} // namespace
<|endoftext|>


<commit_before>/*

Copyright (c) 2008, Arvid Norberg
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 author 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 THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/ip_filter.hpp"
#include <boost/utility.hpp>

#include "test.hpp"
#include "libtorrent/socket_io.hpp"

/*

Currently this test only tests that the filter can handle
IPv4 addresses. Maybe it should be extended to IPv6 as well,
but the actual code is just a template, so it is probably
pretty safe to assume that as long as it works for IPv4 it
also works for IPv6.

*/

using namespace libtorrent;

template <class Addr>
bool compare(ip_range<Addr> const& lhs
	, ip_range<Addr> const& rhs)
{
	return lhs.first == rhs.first
		&& lhs.last == rhs.last
		&& lhs.flags == rhs.flags;
}

#define IP(x) address::from_string(x, ec)
#define IP4(x) address_v4::from_string(x, ec)

void test_rules_invariant(std::vector<ip_range<address_v4> > const& r, ip_filter const& f)
{
	typedef std::vector<ip_range<address_v4> >::const_iterator iterator;
	TEST_CHECK(!r.empty());
	if (r.empty()) return;

	error_code ec;
	TEST_CHECK(r.front().first == IP("0.0.0.0"));
	TEST_CHECK(r.back().last == IP("255.255.255.255"));

	iterator i = r.begin();
	iterator j = boost::next(i);
	for (iterator i(r.begin()), j(boost::next(r.begin()))
		, end(r.end()); j != end; ++j, ++i)
	{
		TEST_CHECK(f.access(i->last) == i->flags);
		TEST_CHECK(f.access(j->first) == j->flags);
		TEST_CHECK(i->last.to_ulong() + 1 == j->first.to_ulong());
	}
}

int test_main()
{
	using namespace libtorrent;

	std::vector<ip_range<address_v4> > range;
	error_code ec;

	// **** test joining of ranges at the end ****
	ip_range<address_v4> expected1[] =
	{
		{IP4("0.0.0.0"), IP4("0.255.255.255"), 0}
		, {IP4("1.0.0.0"), IP4("3.0.0.0"), ip_filter::blocked}
		, {IP4("3.0.0.1"), IP4("255.255.255.255"), 0}
	};

	{
		ip_filter f;
		f.add_rule(IP("1.0.0.0"), IP("2.0.0.0"), ip_filter::blocked);
		f.add_rule(IP("2.0.0.1"), IP("3.0.0.0"), ip_filter::blocked);

#if TORRENT_USE_IPV6
		range = boost::get<0>(f.export_filter());
#else
		range = f.export_filter();
#endif
		test_rules_invariant(range, f);

		TEST_CHECK(range.size() == 3);
		TEST_CHECK(std::equal(range.begin(), range.end(), expected1, &compare<address_v4>));

	}

	// **** test joining of ranges at the start ****

	{
		ip_filter f;
		f.add_rule(IP("2.0.0.1"), IP("3.0.0.0"), ip_filter::blocked);
		f.add_rule(IP("1.0.0.0"), IP("2.0.0.0"), ip_filter::blocked);

#if TORRENT_USE_IPV6
		range = boost::get<0>(f.export_filter());
#else
		range = f.export_filter();
#endif
		test_rules_invariant(range, f);

		TEST_CHECK(range.size() == 3);
		TEST_CHECK(std::equal(range.begin(), range.end(), expected1, &compare<address_v4>));

	}

	// **** test joining of overlapping ranges at the start ****

	{
		ip_filter f;
		f.add_rule(IP("2.0.0.1"), IP("3.0.0.0"), ip_filter::blocked);
		f.add_rule(IP("1.0.0.0"), IP("2.4.0.0"), ip_filter::blocked);

#if TORRENT_USE_IPV6
		range = boost::get<0>(f.export_filter());
#else
		range = f.export_filter();
#endif
		test_rules_invariant(range, f);

		TEST_CHECK(range.size() == 3);
		TEST_CHECK(std::equal(range.begin(), range.end(), expected1, &compare<address_v4>));

	}

	// **** test joining of overlapping ranges at the end ****

	{
		ip_filter f;
		f.add_rule(IP("1.0.0.0"), IP("2.4.0.0"), ip_filter::blocked);
		f.add_rule(IP("2.0.0.1"), IP("3.0.0.0"), ip_filter::blocked);

#if TORRENT_USE_IPV6
		range = boost::get<0>(f.export_filter());
#else
		range = f.export_filter();
#endif
		test_rules_invariant(range, f);

		TEST_CHECK(range.size() == 3);
		TEST_CHECK(std::equal(range.begin(), range.end(), expected1, &compare<address_v4>));

	}

	// **** test joining of multiple overlapping ranges 1 ****

	{
		ip_filter f;
		f.add_rule(IP("1.0.0.0"), IP("2.0.0.0"), ip_filter::blocked);
		f.add_rule(IP("3.0.0.0"), IP("4.0.0.0"), ip_filter::blocked);
		f.add_rule(IP("5.0.0.0"), IP("6.0.0.0"), ip_filter::blocked);
		f.add_rule(IP("7.0.0.0"), IP("8.0.0.0"), ip_filter::blocked);

		f.add_rule(IP("1.0.1.0"), IP("9.0.0.0"), ip_filter::blocked);

#if TORRENT_USE_IPV6
		range = boost::get<0>(f.export_filter());
#else
		range = f.export_filter();
#endif
		test_rules_invariant(range, f);

		TEST_CHECK(range.size() == 3);
		ip_range<address_v4> expected[] =
		{
			{IP4("0.0.0.0"), IP4("0.255.255.255"), 0}
			, {IP4("1.0.0.0"), IP4("9.0.0.0"), ip_filter::blocked}
			, {IP4("9.0.0.1"), IP4("255.255.255.255"), 0}
		};

		TEST_CHECK(std::equal(range.begin(), range.end(), expected, &compare<address_v4>));

	}

	// **** test joining of multiple overlapping ranges 2 ****

	{
		ip_filter f;
		f.add_rule(IP("1.0.0.0"), IP("2.0.0.0"), ip_filter::blocked);
		f.add_rule(IP("3.0.0.0"), IP("4.0.0.0"), ip_filter::blocked);
		f.add_rule(IP("5.0.0.0"), IP("6.0.0.0"), ip_filter::blocked);
		f.add_rule(IP("7.0.0.0"), IP("8.0.0.0"), ip_filter::blocked);

		f.add_rule(IP("0.0.1.0"), IP("7.0.4.0"), ip_filter::blocked);

#if TORRENT_USE_IPV6
		range = boost::get<0>(f.export_filter());
#else
		range = f.export_filter();
#endif
		test_rules_invariant(range, f);

		TEST_CHECK(range.size() == 3);
		ip_range<address_v4> expected[] =
		{
			{IP4("0.0.0.0"), IP4("0.0.0.255"), 0}
			, {IP4("0.0.1.0"), IP4("8.0.0.0"), ip_filter::blocked}
			, {IP4("8.0.0.1"), IP4("255.255.255.255"), 0}
		};

		TEST_CHECK(std::equal(range.begin(), range.end(), expected, &compare<address_v4>));

	}

	port_filter pf;

	// default contructed port filter should allow any port
	TEST_CHECK(pf.access(0) == 0);
	TEST_CHECK(pf.access(65535) == 0);
	TEST_CHECK(pf.access(6881) == 0);

	// block port 100 - 300
	pf.add_rule(100, 300, port_filter::blocked);

	TEST_CHECK(pf.access(0) == 0);
	TEST_CHECK(pf.access(99) == 0);
	TEST_CHECK(pf.access(100) == port_filter::blocked);
	TEST_CHECK(pf.access(150) == port_filter::blocked);
	TEST_CHECK(pf.access(300) == port_filter::blocked);
	TEST_CHECK(pf.access(301) == 0);
	TEST_CHECK(pf.access(6881) == 0);
	TEST_CHECK(pf.access(65535) == 0);

	return 0;
}

<commit_msg>extend ip_filter test to include some IPv6<commit_after>/*

Copyright (c) 2008, Arvid Norberg
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 author 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 THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/ip_filter.hpp"
#include <boost/utility.hpp>

#include "test.hpp"
#include "libtorrent/socket_io.hpp"

/*

Currently this test only tests that the filter can handle
IPv4 addresses. Maybe it should be extended to IPv6 as well,
but the actual code is just a template, so it is probably
pretty safe to assume that as long as it works for IPv4 it
also works for IPv6.

*/

using namespace libtorrent;

template <class Addr>
bool compare(ip_range<Addr> const& lhs
	, ip_range<Addr> const& rhs)
{
	return lhs.first == rhs.first
		&& lhs.last == rhs.last
		&& lhs.flags == rhs.flags;
}

#define IP(x) address::from_string(x, ec)
#define IP4(x) address_v4::from_string(x, ec)
#define IP6(x) address_v6::from_string(x, ec)

template <class T>
void test_rules_invariant(std::vector<ip_range<T> > const& r, ip_filter const& f)
{
	typedef typename std::vector<ip_range<T> >::const_iterator iterator;
	TEST_CHECK(!r.empty());
	if (r.empty()) return;

	error_code ec;
	if (sizeof(r.front().first) == sizeof(address_v4))
	{
		TEST_CHECK(r.front().first == IP("0.0.0.0"));
		TEST_CHECK(r.back().last == IP("255.255.255.255"));
	}
	else
	{
		TEST_CHECK(r.front().first == IP("::0"));
		TEST_CHECK(r.back().last == IP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"));
	}

	iterator i = r.begin();
	iterator j = boost::next(i);
	for (iterator i(r.begin()), j(boost::next(r.begin()))
		, end(r.end()); j != end; ++j, ++i)
	{
		TEST_CHECK(f.access(i->last) == i->flags);
		TEST_CHECK(f.access(j->first) == j->flags);
		TEST_CHECK(detail::plus_one(i->last.to_bytes()) == j->first.to_bytes());
	}
}

int test_main()
{
	using namespace libtorrent;

	std::vector<ip_range<address_v4> > range;
	error_code ec;

	// **** test joining of ranges at the end ****
	ip_range<address_v4> expected1[] =
	{
		{IP4("0.0.0.0"), IP4("0.255.255.255"), 0}
		, {IP4("1.0.0.0"), IP4("3.0.0.0"), ip_filter::blocked}
		, {IP4("3.0.0.1"), IP4("255.255.255.255"), 0}
	};

	{
		ip_filter f;
		f.add_rule(IP("1.0.0.0"), IP("2.0.0.0"), ip_filter::blocked);
		f.add_rule(IP("2.0.0.1"), IP("3.0.0.0"), ip_filter::blocked);

#if TORRENT_USE_IPV6
		range = boost::get<0>(f.export_filter());
#else
		range = f.export_filter();
#endif
		test_rules_invariant(range, f);

		TEST_CHECK(range.size() == 3);
		TEST_CHECK(std::equal(range.begin(), range.end(), expected1, &compare<address_v4>));

	}

	// **** test joining of ranges at the start ****

	{
		ip_filter f;
		f.add_rule(IP("2.0.0.1"), IP("3.0.0.0"), ip_filter::blocked);
		f.add_rule(IP("1.0.0.0"), IP("2.0.0.0"), ip_filter::blocked);

#if TORRENT_USE_IPV6
		range = boost::get<0>(f.export_filter());
#else
		range = f.export_filter();
#endif
		test_rules_invariant(range, f);

		TEST_CHECK(range.size() == 3);
		TEST_CHECK(std::equal(range.begin(), range.end(), expected1, &compare<address_v4>));

	}

	// **** test joining of overlapping ranges at the start ****

	{
		ip_filter f;
		f.add_rule(IP("2.0.0.1"), IP("3.0.0.0"), ip_filter::blocked);
		f.add_rule(IP("1.0.0.0"), IP("2.4.0.0"), ip_filter::blocked);

#if TORRENT_USE_IPV6
		range = boost::get<0>(f.export_filter());
#else
		range = f.export_filter();
#endif
		test_rules_invariant(range, f);

		TEST_CHECK(range.size() == 3);
		TEST_CHECK(std::equal(range.begin(), range.end(), expected1, &compare<address_v4>));

	}

	// **** test joining of overlapping ranges at the end ****

	{
		ip_filter f;
		f.add_rule(IP("1.0.0.0"), IP("2.4.0.0"), ip_filter::blocked);
		f.add_rule(IP("2.0.0.1"), IP("3.0.0.0"), ip_filter::blocked);

#if TORRENT_USE_IPV6
		range = boost::get<0>(f.export_filter());
#else
		range = f.export_filter();
#endif
		test_rules_invariant(range, f);

		TEST_CHECK(range.size() == 3);
		TEST_CHECK(std::equal(range.begin(), range.end(), expected1, &compare<address_v4>));

	}

	// **** test joining of multiple overlapping ranges 1 ****

	{
		ip_filter f;
		f.add_rule(IP("1.0.0.0"), IP("2.0.0.0"), ip_filter::blocked);
		f.add_rule(IP("3.0.0.0"), IP("4.0.0.0"), ip_filter::blocked);
		f.add_rule(IP("5.0.0.0"), IP("6.0.0.0"), ip_filter::blocked);
		f.add_rule(IP("7.0.0.0"), IP("8.0.0.0"), ip_filter::blocked);

		f.add_rule(IP("1.0.1.0"), IP("9.0.0.0"), ip_filter::blocked);

#if TORRENT_USE_IPV6
		range = boost::get<0>(f.export_filter());
#else
		range = f.export_filter();
#endif
		test_rules_invariant(range, f);

		TEST_CHECK(range.size() == 3);
		ip_range<address_v4> expected[] =
		{
			{IP4("0.0.0.0"), IP4("0.255.255.255"), 0}
			, {IP4("1.0.0.0"), IP4("9.0.0.0"), ip_filter::blocked}
			, {IP4("9.0.0.1"), IP4("255.255.255.255"), 0}
		};

		TEST_CHECK(std::equal(range.begin(), range.end(), expected, &compare<address_v4>));

	}

	// **** test joining of multiple overlapping ranges 2 ****

	{
		ip_filter f;
		f.add_rule(IP("1.0.0.0"), IP("2.0.0.0"), ip_filter::blocked);
		f.add_rule(IP("3.0.0.0"), IP("4.0.0.0"), ip_filter::blocked);
		f.add_rule(IP("5.0.0.0"), IP("6.0.0.0"), ip_filter::blocked);
		f.add_rule(IP("7.0.0.0"), IP("8.0.0.0"), ip_filter::blocked);

		f.add_rule(IP("0.0.1.0"), IP("7.0.4.0"), ip_filter::blocked);

#if TORRENT_USE_IPV6
		range = boost::get<0>(f.export_filter());
#else
		range = f.export_filter();
#endif
		test_rules_invariant(range, f);

		TEST_CHECK(range.size() == 3);
		ip_range<address_v4> expected[] =
		{
			{IP4("0.0.0.0"), IP4("0.0.0.255"), 0}
			, {IP4("0.0.1.0"), IP4("8.0.0.0"), ip_filter::blocked}
			, {IP4("8.0.0.1"), IP4("255.255.255.255"), 0}
		};

		TEST_CHECK(std::equal(range.begin(), range.end(), expected, &compare<address_v4>));

	}

	// **** test IPv6 ****

#if TORRENT_USE_IPV6

	ip_range<address_v6> expected2[] =
	{
		{IP6("::0"), IP6("0:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), 0}
		, {IP6("1::"), IP6("3::"), ip_filter::blocked}
		, {IP6("3::1"), IP6("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), 0}
	};

	{
		ip_filter f;
		f.add_rule(IP("2::1"), IP("3::"), ip_filter::blocked);
		f.add_rule(IP("1::"), IP("2::"), ip_filter::blocked);

		std::vector<ip_range<address_v6> > range;
		range = boost::get<1>(f.export_filter());
		test_rules_invariant(range, f);

		TEST_CHECK(range.size() == 3);
		TEST_CHECK(std::equal(range.begin(), range.end(), expected2, &compare<address_v6>));

	}
#endif

	port_filter pf;

	// default contructed port filter should allow any port
	TEST_CHECK(pf.access(0) == 0);
	TEST_CHECK(pf.access(65535) == 0);
	TEST_CHECK(pf.access(6881) == 0);

	// block port 100 - 300
	pf.add_rule(100, 300, port_filter::blocked);

	TEST_CHECK(pf.access(0) == 0);
	TEST_CHECK(pf.access(99) == 0);
	TEST_CHECK(pf.access(100) == port_filter::blocked);
	TEST_CHECK(pf.access(150) == port_filter::blocked);
	TEST_CHECK(pf.access(300) == port_filter::blocked);
	TEST_CHECK(pf.access(301) == 0);
	TEST_CHECK(pf.access(6881) == 0);
	TEST_CHECK(pf.access(65535) == 0);

	return 0;
}

<|endoftext|>


<commit_before>/*

Copyright (c) 2007, Un Shyam
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 author 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 THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include <algorithm>
#include <iostream>

#include "libtorrent/hasher.hpp"
#include "libtorrent/pe_crypto.hpp"
#include "libtorrent/session.hpp"

#include "setup_transfer.hpp"
#include "test.hpp"

#ifndef TORRENT_DISABLE_ENCRYPTION

char const* pe_policy(libtorrent::pe_settings::enc_policy policy)
{
	using namespace libtorrent;

	if (policy == pe_settings::disabled) return "disabled";
	else if (policy == pe_settings::enabled) return "enabled";
	else if (policy == pe_settings::forced) return "forced";
	return "unknown";
}

void display_pe_settings(libtorrent::pe_settings s)
{
	using namespace libtorrent;

	fprintf(stderr, "out_enc_policy - %s\tin_enc_policy - %s\n"
		, pe_policy(s.out_enc_policy), pe_policy(s.in_enc_policy));

	fprintf(stderr, "enc_level - %s\t\tprefer_rc4 - %s\n"
		, s.allowed_enc_level == pe_settings::plaintext ? "plaintext"
		: s.allowed_enc_level == pe_settings::rc4 ? "rc4"
		: s.allowed_enc_level == pe_settings::both ? "both" : "unknown"
		, s.prefer_rc4 ? "true": "false");
}

void test_transfer(libtorrent::pe_settings::enc_policy policy,
		   libtorrent::pe_settings::enc_level level = libtorrent::pe_settings::both,
		   bool pref_rc4 = false)
{
	using namespace libtorrent;

	// these are declared before the session objects
	// so that they are destructed last. This enables
	// the sessions to destruct in parallel
	session_proxy p1;
	session_proxy p2;

	session ses1(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48800, 49000), "0.0.0.0", 0);
	session ses2(fingerprint("LT", 0, 1, 0, 0), std::make_pair(49800, 50000), "0.0.0.0", 0);
	pe_settings s;

	s.out_enc_policy = libtorrent::pe_settings::enabled;
	s.in_enc_policy = libtorrent::pe_settings::enabled;

	s.allowed_enc_level = pe_settings::both;
	ses2.set_pe_settings(s);

	s.out_enc_policy = policy;
	s.in_enc_policy = policy;
	s.allowed_enc_level = level;
	s.prefer_rc4 = pref_rc4;
	ses1.set_pe_settings(s);

	s = ses1.get_pe_settings();
	fprintf(stderr, " Session1 \n");
	display_pe_settings(s);
	s = ses2.get_pe_settings();
	fprintf(stderr, " Session2 \n");
	display_pe_settings(s);

	torrent_handle tor1;
	torrent_handle tor2;

	using boost::tuples::ignore;
	boost::tie(tor1, tor2, ignore) = setup_transfer(&ses1, &ses2, 0, true, false, true
		, "_pe", 16 * 1024, 0, false, 0, true);

	fprintf(stderr, "waiting for transfer to complete\n");

	for (int i = 0; i < 50; ++i)
	{
		torrent_status s = tor2.status();
		print_alerts(ses1, "ses1");
		print_alerts(ses2, "ses2");

		if (s.is_seeding) break;
		test_sleep(1000);
	}

	TEST_CHECK(tor2.status().is_seeding);
 	if (tor2.status().is_seeding) fprintf(stderr, "done\n");
	ses1.remove_torrent(tor1);
	ses2.remove_torrent(tor2);

	// this allows shutting down the sessions in parallel
	p1 = ses1.abort();
	p2 = ses2.abort();

	error_code ec;
	remove_all("tmp1_pe", ec);
	remove_all("tmp2_pe", ec);
	remove_all("tmp3_pe", ec);
}

void test_enc_handler(libtorrent::encryption_handler* a, libtorrent::encryption_handler* b)
{
	int repcount = 128;
	for (int rep = 0; rep < repcount; ++rep)
	{
		std::size_t buf_len = rand() % (512 * 1024);
		char* buf = new char[buf_len];
		char* cmp_buf = new char[buf_len];

		std::generate(buf, buf + buf_len, &std::rand);
		std::memcpy(cmp_buf, buf, buf_len);

		a->encrypt(buf, buf_len);
		TEST_CHECK(!std::equal(buf, buf + buf_len, cmp_buf));
		b->decrypt(buf, buf_len);
		TEST_CHECK(std::equal(buf, buf + buf_len, cmp_buf));

		b->encrypt(buf, buf_len);
		TEST_CHECK(!std::equal(buf, buf + buf_len, cmp_buf));
		a->decrypt(buf, buf_len);
		TEST_CHECK(std::equal(buf, buf + buf_len, cmp_buf));

		delete[] buf;
		delete[] cmp_buf;
	}
}

#endif

int test_main()
{
	using namespace libtorrent;

#ifndef TORRENT_DISABLE_ENCRYPTION
	int repcount = 128;

	for (int rep = 0; rep < repcount; ++rep)
	{
		dh_key_exchange DH1, DH2;

		DH1.compute_secret(DH2.get_local_key());
		DH2.compute_secret(DH1.get_local_key());

		TEST_CHECK(std::equal(DH1.get_secret(), DH1.get_secret() + 96, DH2.get_secret()));
	}

	dh_key_exchange DH1, DH2;
	DH1.compute_secret(DH2.get_local_key());
	DH2.compute_secret(DH1.get_local_key());

	TEST_CHECK(std::equal(DH1.get_secret(), DH1.get_secret() + 96, DH2.get_secret()));

	sha1_hash test1_key = hasher("test1_key",8).final();
	sha1_hash test2_key = hasher("test2_key",8).final();

	fprintf(stderr, "testing RC4 handler\n");
	rc4_handler rc41;
	rc41.set_incoming_key(&test2_key[0], 20);
	rc41.set_outgoing_key(&test1_key[0], 20);
	rc4_handler rc42;
	rc42.set_incoming_key(&test1_key[0], 20);
	rc42.set_outgoing_key(&test2_key[0], 20);
	test_enc_handler(&rc41, &rc42);

	test_transfer(pe_settings::disabled);

	test_transfer(pe_settings::forced, pe_settings::plaintext);
	test_transfer(pe_settings::forced, pe_settings::rc4);
	test_transfer(pe_settings::forced, pe_settings::both, false);
	test_transfer(pe_settings::forced, pe_settings::both, true);

	test_transfer(pe_settings::enabled, pe_settings::plaintext);
	test_transfer(pe_settings::enabled, pe_settings::rc4);
	test_transfer(pe_settings::enabled, pe_settings::both, false);
	test_transfer(pe_settings::enabled, pe_settings::both, true);
#else
	fprintf(stderr, "PE test not run because it's disabled\n");
#endif

	return 0;
}

<commit_msg>attempt to make test_pe_crypto pass under valgrind in reasonable time<commit_after>/*

Copyright (c) 2007, Un Shyam
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 author 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 THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include <algorithm>
#include <iostream>

#include "libtorrent/hasher.hpp"
#include "libtorrent/pe_crypto.hpp"
#include "libtorrent/session.hpp"

#include "setup_transfer.hpp"
#include "test.hpp"

#ifndef TORRENT_DISABLE_ENCRYPTION

char const* pe_policy(libtorrent::pe_settings::enc_policy policy)
{
	using namespace libtorrent;

	if (policy == pe_settings::disabled) return "disabled";
	else if (policy == pe_settings::enabled) return "enabled";
	else if (policy == pe_settings::forced) return "forced";
	return "unknown";
}

void display_pe_settings(libtorrent::pe_settings s)
{
	using namespace libtorrent;

	fprintf(stderr, "out_enc_policy - %s\tin_enc_policy - %s\n"
		, pe_policy(s.out_enc_policy), pe_policy(s.in_enc_policy));

	fprintf(stderr, "enc_level - %s\t\tprefer_rc4 - %s\n"
		, s.allowed_enc_level == pe_settings::plaintext ? "plaintext"
		: s.allowed_enc_level == pe_settings::rc4 ? "rc4"
		: s.allowed_enc_level == pe_settings::both ? "both" : "unknown"
		, s.prefer_rc4 ? "true": "false");
}

void test_transfer(libtorrent::pe_settings::enc_policy policy
	, int timeout
	, libtorrent::pe_settings::enc_level level = libtorrent::pe_settings::both
	, bool pref_rc4 = false)
{
	using namespace libtorrent;

	// these are declared before the session objects
	// so that they are destructed last. This enables
	// the sessions to destruct in parallel
	session_proxy p1;
	session_proxy p2;

	session ses1(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48800, 49000), "0.0.0.0", 0);
	session ses2(fingerprint("LT", 0, 1, 0, 0), std::make_pair(49800, 50000), "0.0.0.0", 0);
	pe_settings s;

	s.out_enc_policy = libtorrent::pe_settings::enabled;
	s.in_enc_policy = libtorrent::pe_settings::enabled;

	s.allowed_enc_level = pe_settings::both;
	ses2.set_pe_settings(s);

	s.out_enc_policy = policy;
	s.in_enc_policy = policy;
	s.allowed_enc_level = level;
	s.prefer_rc4 = pref_rc4;
	ses1.set_pe_settings(s);

	s = ses1.get_pe_settings();
	fprintf(stderr, " Session1 \n");
	display_pe_settings(s);
	s = ses2.get_pe_settings();
	fprintf(stderr, " Session2 \n");
	display_pe_settings(s);

	torrent_handle tor1;
	torrent_handle tor2;

	using boost::tuples::ignore;
	boost::tie(tor1, tor2, ignore) = setup_transfer(&ses1, &ses2, 0, true, false, true
		, "_pe", 16 * 1024, 0, false, 0, true);

	fprintf(stderr, "waiting for transfer to complete\n");

	for (int i = 0; i < timeout * 10; ++i)
	{
		torrent_status s = tor2.status();
		print_alerts(ses1, "ses1");
		print_alerts(ses2, "ses2");

		if (s.is_seeding) break;
		test_sleep(100);
	}

	TEST_CHECK(tor2.status().is_seeding);
 	if (tor2.status().is_seeding) fprintf(stderr, "done\n");
	ses1.remove_torrent(tor1);
	ses2.remove_torrent(tor2);

	// this allows shutting down the sessions in parallel
	p1 = ses1.abort();
	p2 = ses2.abort();

	error_code ec;
	remove_all("tmp1_pe", ec);
	remove_all("tmp2_pe", ec);
	remove_all("tmp3_pe", ec);
}

void test_enc_handler(libtorrent::encryption_handler* a, libtorrent::encryption_handler* b)
{
#ifdef TORRENT_USE_VALGRIND
	const int repcount = 10;
#else
	const int repcount = 128;
#endif
	for (int rep = 0; rep < repcount; ++rep)
	{
		std::size_t buf_len = rand() % (512 * 1024);
		char* buf = new char[buf_len];
		char* cmp_buf = new char[buf_len];

		std::generate(buf, buf + buf_len, &std::rand);
		std::memcpy(cmp_buf, buf, buf_len);

		a->encrypt(buf, buf_len);
		TEST_CHECK(!std::equal(buf, buf + buf_len, cmp_buf));
		b->decrypt(buf, buf_len);
		TEST_CHECK(std::equal(buf, buf + buf_len, cmp_buf));

		b->encrypt(buf, buf_len);
		TEST_CHECK(!std::equal(buf, buf + buf_len, cmp_buf));
		a->decrypt(buf, buf_len);
		TEST_CHECK(std::equal(buf, buf + buf_len, cmp_buf));

		delete[] buf;
		delete[] cmp_buf;
	}
}

#endif

int test_main()
{
	using namespace libtorrent;

#ifndef TORRENT_DISABLE_ENCRYPTION

#ifdef TORRENT_USE_VALGRIND
	const int repcount = 10;
#else
	const int repcount = 128;
#endif

	for (int rep = 0; rep < repcount; ++rep)
	{
		dh_key_exchange DH1, DH2;

		DH1.compute_secret(DH2.get_local_key());
		DH2.compute_secret(DH1.get_local_key());

		TEST_CHECK(std::equal(DH1.get_secret(), DH1.get_secret() + 96, DH2.get_secret()));
	}

	dh_key_exchange DH1, DH2;
	DH1.compute_secret(DH2.get_local_key());
	DH2.compute_secret(DH1.get_local_key());

	TEST_CHECK(std::equal(DH1.get_secret(), DH1.get_secret() + 96, DH2.get_secret()));

	sha1_hash test1_key = hasher("test1_key",8).final();
	sha1_hash test2_key = hasher("test2_key",8).final();

	fprintf(stderr, "testing RC4 handler\n");
	rc4_handler rc41;
	rc41.set_incoming_key(&test2_key[0], 20);
	rc41.set_outgoing_key(&test1_key[0], 20);
	rc4_handler rc42;
	rc42.set_incoming_key(&test1_key[0], 20);
	rc42.set_outgoing_key(&test2_key[0], 20);
	test_enc_handler(&rc41, &rc42);

#ifdef TORRENT_USE_VALGRIND
	const int timeout = 10;
#else
	const int timeout = 5;
#endif

	test_transfer(pe_settings::disabled, timeout);

	test_transfer(pe_settings::forced, timeout, pe_settings::plaintext);
	test_transfer(pe_settings::forced, timeout, pe_settings::rc4);
	test_transfer(pe_settings::forced, timeout, pe_settings::both, false);
	test_transfer(pe_settings::forced, timeout, pe_settings::both, true);

	test_transfer(pe_settings::enabled, timeout, pe_settings::plaintext);
	test_transfer(pe_settings::enabled, timeout, pe_settings::rc4);
	test_transfer(pe_settings::enabled, timeout, pe_settings::both, false);
	test_transfer(pe_settings::enabled, timeout, pe_settings::both, true);
#else
	fprintf(stderr, "PE test not run because it's disabled\n");
#endif

	return 0;
}

<|endoftext|>