<commit_before>#include "performancedialog.h"
#include "ui_performancedialog.h"
#include "socketclient.h"
#include "consts.h"
#include "circularbuffer.h"

#include <QGraphicsRectItem>
#include <QWheelEvent>

const double KLineSize = 10;
const double KLineDrawSize = 5;
const double KScaleX = 1000.0;

class GridScene : public QGraphicsScene
{
public:
    GridScene() {}
    GridScene(qreal x, qreal y, qreal w, qreal h)
        : QGraphicsScene(x, y, w, h)
    { }

protected:
    void drawBackground(QPainter *painter, const QRectF &rect)
    {
        QGraphicsScene::drawBackground(painter, rect);
        const int gridSizeX = 1.0f;
        const int gridSizeY = 60.0f*KLineSize;

        qreal left = int(rect.left()) - (int(rect.left()) % gridSizeX);
        qreal top = int(rect.top()) - (int(rect.top()) % gridSizeY);

        QVarLengthArray<QLineF, 100> lines;

        for (qreal x = left; x < rect.right(); x += gridSizeX)
            lines.append(QLineF(x, rect.top(), x, rect.bottom()));
        for (qreal y = top; y < rect.bottom(); y += gridSizeY)
            lines.append(QLineF(rect.left(), y, rect.right(), y));

        painter->setPen(QPen(QBrush(Qt::darkGray),0,Qt::DashDotDotLine));

        painter->drawLines(lines.data(), lines.size());

        painter->setPen(QPen(QBrush(Qt::lightGray),0,Qt::DashLine));
        painter->drawLine(QLineF(16.66, rect.top(), 16.66, rect.bottom()));
        painter->drawLine(QLineF(33.33, rect.top(), 33.33, rect.bottom()));
    }
};

PerformanceDialog::PerformanceDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::PerformanceDialog),
    mFirstVSync(true),
    mCurrentRow(0)
{
    ui->setupUi(this);
    mScene = new GridScene;
    mScene->setBackgroundBrush(QBrush(Qt::black));
    mScene->setSceneRect(0,0,33.333,100);
    ui->graphicsView->setScene(mScene);

//    ui->graphicsView->setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);
//    ui->graphicsView->viewport()->installEventFilter(this);
    ui->tableWidget->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
    ui->pauseButton->setEnabled(false);

    mUpdateValues = new CircularBuffer(60);
    mRenderValues = new CircularBuffer(60);
    mSwapValues = new CircularBuffer(60);
    QObject::connect(this,SIGNAL(messageAvailable()),this,SLOT(processMessages()), Qt::QueuedConnection);

}

PerformanceDialog::~PerformanceDialog()
{
    delete mScene;
    delete ui;
    delete mUpdateValues;
    delete mRenderValues;
    delete mSwapValues;
}

void PerformanceDialog::setConnection(QString hostAddress, quint16 portNumber)
{
    mHostAddress = hostAddress;
    mPortNumber = portNumber;
}

void PerformanceDialog::resizeEvent(QResizeEvent *event)
{
    ui->graphicsView->fitInView(0,KLineSize*mCurrentRow-ui->graphicsView->height(),33.33,ui->graphicsView->height());
}

//bool PerformanceDialog::eventFilter(QObject *obj, QEvent *event)
//{
//    if (obj == ui->graphicsView->viewport()) {
//        return false;
//    } else {
//        return QDialog::eventFilter(obj, event);
//    }
//}

void PerformanceDialog::MessageReceived(QString& recv)
{
    QMutexLocker lock(&mMessagesMutex);
    mMessages.push_back(recv);
    if (mMessages.size()==1) {
        // only ever signal on 0 -> 1
        emit messageAvailable();
    }
}

void PerformanceDialog::processMessages()
{
    //qWarning() << recv;
    bool messagesAvailable = true;
    int maxMessageCount=0;
    while (messagesAvailable)
    {
        QString recv;
        {
            QMutexLocker lock(&mMessagesMutex);
            int numMessages = mMessages.size();
            if (numMessages > maxMessageCount)
            {
                maxMessageCount = numMessages;
            }
            if (numMessages >0) {
                recv = mMessages.front();
                mMessages.pop_front();
            } else {
                messagesAvailable = false;
            }
        }
        if (messagesAvailable) {
            QStringList args = recv.split(" ");
            if (args.length()>=3) {
                double time = args[0].toDouble();
                if (args[2].compare("V_SYNC")==0) {
                    if (!mFirstVSync) {
                        double vsyncduration = (time - mLastVSync);
                        mLastVSync = time;
                        if (vsyncduration < 0.0333) {
                            addVSync(vsyncduration);
                            mCurrentRow++;
                        } else {
                            mCurrentRow+=5;
                            mUpdateStart = 0;
                            mRenderStart = 0;
                            mSwapStart = 0;
                        }
                    }
                    mFirstVSync = false;
                } else if (!mFirstVSync) {
                    // only process other messages if we have already got a vsync, since the timings are impossible without it
                    if (args[2].compare("UPDATE_START")==0) {
                        mUpdateStart = time;
                        mUpdateVSync = mLastVSync;
                        mUpdateRow = mCurrentRow;
                    } else if (args[2].compare("UPDATE_END")==0) {
                        mUpdateEnd = time;
                        if (mUpdateStart != 0)
                            addUpdate();
                    } else if (args[2].compare("RENDER_START")==0) {
                        mRenderStart = time;
                        mRenderVSync = mLastVSync;
                        mRenderRow = mCurrentRow;
                    } else if (args[2].compare("RENDER_END")==0) {
                        mRenderEnd = time;
                        if (mRenderStart != 0)
                            addRender();
                    } else if (args[2].compare("SWAP_START")==0) {
                        mSwapStart = time;
                        mSwapVSync = mLastVSync;
                        mSwapRow = mCurrentRow;
                    } else if (args[2].compare("SWAP_END")==0) {
                        mSwapEnd = time;
                        if (mSwapStart != 0)
                         addSwapBuffers();
                    }
                }
            }
        }
    }
    double verticalSize = KLineSize * mCurrentRow;
    mScene->setSceneRect(0,0,33.33,verticalSize);
    ui->graphicsView->fitInView(0,verticalSize-ui->graphicsView->height(),33.33,ui->graphicsView->height());

    double update_avg = mUpdateValues->average();
    double render_avg = mRenderValues->average();
    double swap_avg = mSwapValues->average();
    double update_max = mUpdateValues->maxValue();
    double render_max = mRenderValues->maxValue();
    double swap_max = mSwapValues->maxValue();
    ui->tableWidget->setItem(0,1,new QTableWidgetItem(QString::number(update_avg)));
    ui->tableWidget->setItem(1,1,new QTableWidgetItem(QString::number(render_avg)));
    ui->tableWidget->setItem(2,1,new QTableWidgetItem(QString::number(swap_avg)));
    ui->tableWidget->setItem(3,1,new QTableWidgetItem(QString::number(update_avg+render_avg+swap_avg)));

    ui->tableWidget->setItem(0,2,new QTableWidgetItem(QString::number(update_max)));
    ui->tableWidget->setItem(1,2,new QTableWidgetItem(QString::number(render_max)));
    ui->tableWidget->setItem(2,2,new QTableWidgetItem(QString::number(swap_max)));

    if (maxMessageCount>32)
    {
        qDebug() << "message count > 32 " << maxMessageCount;
    }
}

void PerformanceDialog::addBar(double startTime, double duration, double row, QColor startCol, QColor midCol, QColor endCol)
{
    QLinearGradient gradient(startTime,0,startTime+duration, 0);
    gradient.setColorAt(0, startCol);
    gradient.setColorAt(0.5, midCol);
    gradient.setColorAt(1.0, endCol);
    if (startTime + duration > 33.333) {
        duration = 33.333-startTime;
    }
    mScene->addRect(QRectF(startTime,row*(double)KLineSize, duration, KLineDrawSize), Qt::NoPen, QBrush(gradient));
}

void PerformanceDialog::addUpdate()
{
    double startTime = (mUpdateStart-mUpdateVSync)*KScaleX;
    double duration= (mUpdateEnd-mUpdateStart)*KScaleX;
    mUpdateValues->addValue(duration);

    addBar(startTime,duration, mUpdateRow, QColor(180,255,0), QColor(120,255,0),QColor(0,255,0));

    if (mCurrentRow!=mUpdateRow) {
        startTime = 0;
        duration = (mUpdateEnd-mLastVSync)*KScaleX;
        addBar(startTime,duration, mCurrentRow +0.5, QColor(120,255,0), QColor(120,255,0),QColor(0,255,0));
    }
}

void PerformanceDialog::addRender()
{
    double startTime = (mRenderStart-mRenderVSync)*KScaleX;
    double duration= (mRenderEnd-mRenderStart)*KScaleX;
    mRenderValues->addValue(duration);

    addBar(startTime,duration, mRenderRow +0.5, QColor(0,180,255), QColor(0,120,255),QColor(0,0,255));

    if (mCurrentRow!=mRenderRow) {
        startTime = 0;
        duration = (mRenderEnd-mLastVSync)*KScaleX;
        addBar(startTime,duration, mCurrentRow +0.5, QColor(0,120,255), QColor(0,120,255),QColor(0,0,255));
    }
}

void PerformanceDialog::addSwapBuffers()
{
    double startTime = (mSwapStart-mSwapVSync)*KScaleX;
    double duration= (mSwapEnd-mSwapStart)*KScaleX;
    mSwapValues->addValue(duration);

    addBar(startTime,duration, mSwapRow +0.5, QColor(255,255,180), QColor(255,255,120),QColor(255,255,0));

    if (mCurrentRow!=mSwapRow) {
        startTime = 0;
        duration = (mSwapEnd-mLastVSync)*KScaleX;
        addBar(startTime,duration, mCurrentRow +0.5, QColor(255,255,120), QColor(255,255,120),QColor(255,255,0));
    }
}

void PerformanceDialog::addVSync(double time)
{
    double xpos = time * KScaleX;
    mScene->addLine(QLineF(xpos, mCurrentRow*KLineSize, xpos, mCurrentRow*KLineSize+KLineSize), QPen(Qt::red,0));
}

void PerformanceDialog::pause()
{
    mClient.sendCommand(KDaliCmdPerformanceMarkersOff);
    ui->pauseButton->setEnabled(false);
    ui->startButton->setEnabled(true);
}

void PerformanceDialog::start()
{
    mFirstVSync = true;
    mClient.connectSocket(mHostAddress, mPortNumber);
    mClient.sendCommand(KDaliCmdPerformanceMarkersOn);
    mClient.waitForMessages(this);
    ui->pauseButton->setEnabled(true);
    ui->startButton->setEnabled(false);
}
<commit_msg>fix resizing problem<commit_after>#include "performancedialog.h"
#include "ui_performancedialog.h"
#include "socketclient.h"
#include "consts.h"
#include "circularbuffer.h"

#include <QGraphicsRectItem>
#include <QWheelEvent>

const double KLineSize = 10;
const double KLineDrawSize = 5;
const double KScaleX = 1000.0;

class GridScene : public QGraphicsScene
{
public:
    GridScene() {}
    GridScene(qreal x, qreal y, qreal w, qreal h)
        : QGraphicsScene(x, y, w, h)
    { }

protected:
    void drawBackground(QPainter *painter, const QRectF &rect)
    {
        QGraphicsScene::drawBackground(painter, rect);
        const int gridSizeX = 1.0f;
        const int gridSizeY = 60.0f*KLineSize;

        qreal left = int(rect.left()) - (int(rect.left()) % gridSizeX);
        qreal top = int(rect.top()) - (int(rect.top()) % gridSizeY);

        QVarLengthArray<QLineF, 100> lines;

        for (qreal x = left; x < rect.right(); x += gridSizeX)
            lines.append(QLineF(x, rect.top(), x, rect.bottom()));
        for (qreal y = top; y < rect.bottom(); y += gridSizeY)
            lines.append(QLineF(rect.left(), y, rect.right(), y));

        painter->setPen(QPen(QBrush(Qt::darkGray),0,Qt::DashDotDotLine));

        painter->drawLines(lines.data(), lines.size());

        painter->setPen(QPen(QBrush(Qt::lightGray),0,Qt::DashLine));
        painter->drawLine(QLineF(16.66, rect.top(), 16.66, rect.bottom()));
        painter->drawLine(QLineF(33.33, rect.top(), 33.33, rect.bottom()));
    }
};

PerformanceDialog::PerformanceDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::PerformanceDialog),
    mFirstVSync(true),
    mCurrentRow(0)
{
    ui->setupUi(this);
    mScene = new GridScene;
    mScene->setBackgroundBrush(QBrush(Qt::black));
    mScene->setSceneRect(0,0,33.333,100);
    ui->graphicsView->setScene(mScene);

    ui->graphicsView->setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);
//    ui->graphicsView->viewport()->installEventFilter(this);
    ui->tableWidget->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
    ui->pauseButton->setEnabled(false);

    mUpdateValues = new CircularBuffer(60);
    mRenderValues = new CircularBuffer(60);
    mSwapValues = new CircularBuffer(60);
    QObject::connect(this,SIGNAL(messageAvailable()),this,SLOT(processMessages()), Qt::QueuedConnection);

}

PerformanceDialog::~PerformanceDialog()
{
    delete mScene;
    delete ui;
    delete mUpdateValues;
    delete mRenderValues;
    delete mSwapValues;
}

void PerformanceDialog::setConnection(QString hostAddress, quint16 portNumber)
{
    mHostAddress = hostAddress;
    mPortNumber = portNumber;
}

void PerformanceDialog::resizeEvent(QResizeEvent *event)
{
    ui->graphicsView->fitInView(0,KLineSize*mCurrentRow-ui->graphicsView->height(),33.33,ui->graphicsView->height());
}

//bool PerformanceDialog::eventFilter(QObject *obj, QEvent *event)
//{
//    if (obj == ui->graphicsView->viewport()) {
//        return false;
//    } else {
//        return QDialog::eventFilter(obj, event);
//    }
//}

void PerformanceDialog::MessageReceived(QString& recv)
{
    QMutexLocker lock(&mMessagesMutex);
    mMessages.push_back(recv);
    if (mMessages.size()==1) {
        // only ever signal on 0 -> 1
        emit messageAvailable();
    }
}

void PerformanceDialog::processMessages()
{
    //qWarning() << recv;
    bool messagesAvailable = true;
    int maxMessageCount=0;
    while (messagesAvailable)
    {
        QString recv;
        {
            QMutexLocker lock(&mMessagesMutex);
            int numMessages = mMessages.size();
            if (numMessages > maxMessageCount)
            {
                maxMessageCount = numMessages;
            }
            if (numMessages >0) {
                recv = mMessages.front();
                mMessages.pop_front();
            } else {
                messagesAvailable = false;
            }
        }
        if (messagesAvailable) {
            QStringList args = recv.split(" ");
            if (args.length()>=3) {
                double time = args[0].toDouble();
                if (args[2].compare("V_SYNC")==0) {
                    if (!mFirstVSync) {
                        double vsyncduration = (time - mLastVSync);
                        mLastVSync = time;
                        if (vsyncduration < 0.0333) {
                            addVSync(vsyncduration);
                            mCurrentRow++;
                        } else {
                            mCurrentRow+=5;
                            mUpdateStart = 0;
                            mRenderStart = 0;
                            mSwapStart = 0;
                        }
                    }
                    mFirstVSync = false;
                } else if (!mFirstVSync) {
                    // only process other messages if we have already got a vsync, since the timings are impossible without it
                    if (args[2].compare("UPDATE_START")==0) {
                        mUpdateStart = time;
                        mUpdateVSync = mLastVSync;
                        mUpdateRow = mCurrentRow;
                    } else if (args[2].compare("UPDATE_END")==0) {
                        mUpdateEnd = time;
                        if (mUpdateStart != 0)
                            addUpdate();
                    } else if (args[2].compare("RENDER_START")==0) {
                        mRenderStart = time;
                        mRenderVSync = mLastVSync;
                        mRenderRow = mCurrentRow;
                    } else if (args[2].compare("RENDER_END")==0) {
                        mRenderEnd = time;
                        if (mRenderStart != 0)
                            addRender();
                    } else if (args[2].compare("SWAP_START")==0) {
                        mSwapStart = time;
                        mSwapVSync = mLastVSync;
                        mSwapRow = mCurrentRow;
                    } else if (args[2].compare("SWAP_END")==0) {
                        mSwapEnd = time;
                        if (mSwapStart != 0)
                         addSwapBuffers();
                    }
                }
            }
        }
    }
    double verticalSize = KLineSize * mCurrentRow;
    mScene->setSceneRect(0,0,33.33,verticalSize);
    ui->graphicsView->fitInView(0,verticalSize-ui->graphicsView->height(),33.33,ui->graphicsView->height());

    double update_avg = mUpdateValues->average();
    double render_avg = mRenderValues->average();
    double swap_avg = mSwapValues->average();
    double update_max = mUpdateValues->maxValue();
    double render_max = mRenderValues->maxValue();
    double swap_max = mSwapValues->maxValue();
    ui->tableWidget->setItem(0,1,new QTableWidgetItem(QString::number(update_avg)));
    ui->tableWidget->setItem(1,1,new QTableWidgetItem(QString::number(render_avg)));
    ui->tableWidget->setItem(2,1,new QTableWidgetItem(QString::number(swap_avg)));
    ui->tableWidget->setItem(3,1,new QTableWidgetItem(QString::number(update_avg+render_avg+swap_avg)));

    ui->tableWidget->setItem(0,2,new QTableWidgetItem(QString::number(update_max)));
    ui->tableWidget->setItem(1,2,new QTableWidgetItem(QString::number(render_max)));
    ui->tableWidget->setItem(2,2,new QTableWidgetItem(QString::number(swap_max)));

    if (maxMessageCount>32)
    {
        qDebug() << "message count > 32 " << maxMessageCount;
    }
}

void PerformanceDialog::addBar(double startTime, double duration, double row, QColor startCol, QColor midCol, QColor endCol)
{
    QLinearGradient gradient(startTime,0,startTime+duration, 0);
    gradient.setColorAt(0, startCol);
    gradient.setColorAt(0.5, midCol);
    gradient.setColorAt(1.0, endCol);
    if (startTime + duration > 33.333) {
        duration = 33.333-startTime;
    }
    mScene->addRect(QRectF(startTime,row*(double)KLineSize, duration, KLineDrawSize), Qt::NoPen, QBrush(gradient));
}

void PerformanceDialog::addUpdate()
{
    double startTime = (mUpdateStart-mUpdateVSync)*KScaleX;
    double duration= (mUpdateEnd-mUpdateStart)*KScaleX;
    mUpdateValues->addValue(duration);

    addBar(startTime,duration, mUpdateRow, QColor(180,255,0), QColor(120,255,0),QColor(0,255,0));

    if (mCurrentRow!=mUpdateRow) {
        startTime = 0;
        duration = (mUpdateEnd-mLastVSync)*KScaleX;
        addBar(startTime,duration, mCurrentRow +0.5, QColor(120,255,0), QColor(120,255,0),QColor(0,255,0));
    }
}

void PerformanceDialog::addRender()
{
    double startTime = (mRenderStart-mRenderVSync)*KScaleX;
    double duration= (mRenderEnd-mRenderStart)*KScaleX;
    mRenderValues->addValue(duration);

    addBar(startTime,duration, mRenderRow +0.5, QColor(0,180,255), QColor(0,120,255),QColor(0,0,255));

    if (mCurrentRow!=mRenderRow) {
        startTime = 0;
        duration = (mRenderEnd-mLastVSync)*KScaleX;
        addBar(startTime,duration, mCurrentRow +0.5, QColor(0,120,255), QColor(0,120,255),QColor(0,0,255));
    }
}

void PerformanceDialog::addSwapBuffers()
{
    double startTime = (mSwapStart-mSwapVSync)*KScaleX;
    double duration= (mSwapEnd-mSwapStart)*KScaleX;
    mSwapValues->addValue(duration);

    addBar(startTime,duration, mSwapRow +0.5, QColor(255,255,180), QColor(255,255,120),QColor(255,255,0));

    if (mCurrentRow!=mSwapRow) {
        startTime = 0;
        duration = (mSwapEnd-mLastVSync)*KScaleX;
        addBar(startTime,duration, mCurrentRow +0.5, QColor(255,255,120), QColor(255,255,120),QColor(255,255,0));
    }
}

void PerformanceDialog::addVSync(double time)
{
    double xpos = time * KScaleX;
    mScene->addLine(QLineF(xpos, mCurrentRow*KLineSize, xpos, mCurrentRow*KLineSize+KLineSize), QPen(Qt::red,0));
}

void PerformanceDialog::pause()
{
    mClient.sendCommand(KDaliCmdPerformanceMarkersOff);
    ui->pauseButton->setEnabled(false);
    ui->startButton->setEnabled(true);
}

void PerformanceDialog::start()
{
    mFirstVSync = true;
    mClient.connectSocket(mHostAddress, mPortNumber);
    mClient.sendCommand(KDaliCmdPerformanceMarkersOn);
    mClient.waitForMessages(this);
    ui->pauseButton->setEnabled(true);
    ui->startButton->setEnabled(false);
}
<|endoftext|>


<commit_before>#pragma once

#include <WinSock2.h>

namespace common {

class Copyable {
protected:
  Copyable() noexcept = default;
  ~Copyable() noexcept = default;
  Copyable(const Copyable&) noexcept = default;
  Copyable(Copyable&&) noexcept = default;
  Copyable& operator=(const Copyable&) noexcept = default;
  Copyable& operator=(Copyable&&) noexcept = default;
};

class UnCopyable {
  UnCopyable(const UnCopyable&) noexcept = delete;
  UnCopyable(UnCopyable&&) noexcept = delete;
  UnCopyable& operator=(const UnCopyable&) noexcept = delete;
  UnCopyable& operator=(UnCopyable&&) noexcept = delete;
protected:
  UnCopyable() noexcept = default;
  ~UnCopyable() noexcept = default;
};

template <typename T> class Singleton : private UnCopyable {
public:
  static T& get_instance() noexcept {
    static T _ins;
    return _ins;
  }
};

class WSGuarder final : private UnCopyable {
public:
  WSGuarder() noexcept;
  ~WSGuarder() noexcept;
};

class UniqueSocket final : private UnCopyable {
  SOCKET sockfd_{};
public:
  UniqueSocket(SOCKET fd) noexcept : sockfd_(fd) {}
  ~UniqueSocket() noexcept;

  inline operator SOCKET() const { return sockfd_; }
  inline operator bool() const { return sockfd_ != INVALID_SOCKET; }
};

}<commit_msg>:construction: chore(common): updated the common utils<commit_after>#pragma once

#include <WinSock2.h>
#include <thread>
#include <future>
#include <iostream>

namespace common {

class Copyable {
protected:
  Copyable() noexcept = default;
  ~Copyable() noexcept = default;
  Copyable(const Copyable&) noexcept = default;
  Copyable(Copyable&&) noexcept = default;
  Copyable& operator=(const Copyable&) noexcept = default;
  Copyable& operator=(Copyable&&) noexcept = default;
};

class UnCopyable {
  UnCopyable(const UnCopyable&) noexcept = delete;
  UnCopyable(UnCopyable&&) noexcept = delete;
  UnCopyable& operator=(const UnCopyable&) noexcept = delete;
  UnCopyable& operator=(UnCopyable&&) noexcept = delete;
protected:
  UnCopyable() noexcept = default;
  ~UnCopyable() noexcept = default;
};

template <typename T> class Singleton : private UnCopyable {
public:
  static T& get_instance() noexcept {
    static T _ins;
    return _ins;
  }
};

class WSGuarder final : private UnCopyable {
public:
  WSGuarder() noexcept;
  ~WSGuarder() noexcept;
};

class UniqueSocket final : private UnCopyable {
  SOCKET sockfd_{};
public:
  UniqueSocket(SOCKET fd) noexcept : sockfd_(fd) {}
  ~UniqueSocket() noexcept;

  inline operator SOCKET() const { return sockfd_; }
  inline operator bool() const { return sockfd_ != INVALID_SOCKET; }
};

template <typename Fn, typename... Args>
inline auto async(Fn&& fn, Args&&... args) {
  return std::async(std::launch::async, std::forward<Fn>(fn), std::forward<Args>(args)...);
}

}<|endoftext|>


<commit_before>// Problem 10 from Project Euler
// http://projecteuler.net/problem=10

#include <vector>
#include <iostream>
#include <algorithm>
#include <unordered_map>

int main()
{
  std::unordered_map<unsigned long long, bool> number_map;
  unsigned long long size = 2000000;
  unsigned long long iter = 2;
  while (iter <= size)
    {
      number_map[iter] = true;
      ++iter;
    }

  // The Sieve of Eratosthenes
  for (auto keyValue : number_map)
    {
      if (keyValue.second)
        {
          unsigned long long multiplicand = 2;
          unsigned long long n_product = multiplicand * keyValue.first;

          // While the product is less than the greatest element of number_map
          while (n_product <= size)
            {
              number_map[n_product] = false;
              ++multiplicand;
              n_product = multiplicand * keyValue.first;
            }
        }
    }

  unsigned long long primes_sum = 0;
  for (auto keyValue : number_map)
    {
      if (keyValue.second)
        {
          primes_sum += keyValue.first;
          //std::cout << keyValue.first << std::endl;
        }
    }
  std::cout << primes_sum << std::endl;
}
<commit_msg>Minor code cleanup<commit_after>// Problem 10 from Project Euler
// http://projecteuler.net/problem=10

#include <vector>
#include <iostream>
#include <algorithm>
#include <unordered_map>

int main()
{
  std::unordered_map<unsigned long long, bool> number_map;
  unsigned long long size = 2000000;
  unsigned long long iter = 2;
  // Populate the map
  while (iter <= size)
    {
      number_map[iter] = true;
      ++iter;
    }

  // The Sieve of Eratosthenes
  for (auto keyValue : number_map)
    {
      if (keyValue.second)
        {
          unsigned long long multiplicand = 2;
          unsigned long long n_product = multiplicand * keyValue.first;

          // While the product is less than the greatest element of number_map
          while (n_product <= size)
            {
              number_map[n_product] = false;
              ++multiplicand;
              n_product = multiplicand * keyValue.first;
            }
        }
    }

  // Add all the primes together
  unsigned long long primes_sum = 0;
  for (auto keyValue : number_map)
    {
      if (keyValue.second)
        {
          primes_sum += keyValue.first;
        }
    }
  std::cout << primes_sum << std::endl;
}
<|endoftext|>


<commit_before>// Copyright (c) 2011, Christian Rorvik
// Distributed under the Simplified BSD License (See accompanying file LICENSE.txt)

#include "crunch/concurrency/meta_scheduler.hpp"

#include "crunch/base/assert.hpp"
#include "crunch/base/noncopyable.hpp"
#include "crunch/base/override.hpp"
#include "crunch/base/stack_alloc.hpp"
#include "crunch/concurrency/event.hpp"
#include "crunch/concurrency/yield.hpp"
#include "crunch/concurrency/detail/system_semaphore.hpp"

#include <algorithm>

namespace Crunch { namespace Concurrency {

class MetaScheduler::Context : NonCopyable, Waiter
{
public:
    Context(MetaScheduler& owner)
        : mOwner(owner)
        , mWaitSemaphore(0)
        , mWaitSpinCount(400)
        , mWaitState(WAIT_STATE_SPINNING)
    {}

    static int const WAIT_STATE_SPINNING = 0;
    static int const WAIT_STATE_WAITING = 1;
    static int const WAIT_STATE_DONE = 2;

    void Notify() CRUNCH_OVERRIDE
    {
        int s = WAIT_STATE_SPINNING;

        // Try to unblock from spinning
        if (mWaitState.CompareAndSwap(WAIT_STATE_DONE, s))
            return;

        // Must be waiting
        CRUNCH_ASSERT(s == WAIT_STATE_WAITING);
        mWaitSemaphore.Post();
    }

    void WaitFor(IWaitable& waitable)
    {
        // TODO: Let active scheduler handle the wait if it can
        // TODO: Keep in mind if active scheduler is a fiber scheduler we might come back on a different system thread.. (and this thread might be used for other things.. i.e. waiter must be stack local)

        mWaitState.Store(WAIT_STATE_SPINNING, MEMORY_ORDER_RELAXED);

        waitable.AddWaiter(this);

        uint32 spinLeft = mWaitSpinCount;
        while (spinLeft--)
        {
            if (mWaitState.Load(MEMORY_ORDER_RELAXED) == WAIT_STATE_DONE)
                return;

            CRUNCH_PAUSE();
        }

        int s = WAIT_STATE_SPINNING;
        if (!mWaitState.CompareAndSwap(WAIT_STATE_WAITING, s))
            return; // Must have completed

        mWaitSemaphore.Wait();
    }

private:
    MetaScheduler& mOwner;
    Detail::SystemSemaphore mWaitSemaphore;
    uint32 mWaitSpinCount;
    Atomic<int> mWaitState;
};

CRUNCH_THREAD_LOCAL MetaScheduler::Context* MetaScheduler::tCurrentContext = NULL;
Detail::SystemMutex MetaScheduler::sSharedEventLock;
Detail::SystemEvent MetaScheduler::sSharedEvent;

struct ContextRunState : Waiter
{
    ISchedulerContext* context;

    virtual void Notify() CRUNCH_OVERRIDE
    {
        // Add to active set
        // Wake up scheduler if sleeping
    }
};

class MetaScheduler::MetaThread
{
public:
    MetaThread(MetaThreadConfig const& config)
        : mConfig(config)
    {}

private:
    MetaThreadConfig mConfig;
};

MetaScheduler::MetaScheduler(const SchedulerList& schedulers)
    : mSchedulers(schedulers)
{}

MetaScheduler::~MetaScheduler()
{}

MetaScheduler::MetaThreadHandle MetaScheduler::CreateMetaThread(MetaThreadConfig const& config)
{
    Detail::SystemMutex::ScopedLock lock(mMetaThreadsLock);

    MetaThreadPtr mt(new MetaThread(config));
    mIdleMetaThreads.push_back(std::move(mt));
    return MetaThreadHandle();
}

void MetaScheduler::Join()
{
    CRUNCH_ASSERT_ALWAYS(tCurrentContext == nullptr);
    tCurrentContext = new Context(*this);
}

void MetaScheduler::Leave()
{
    CRUNCH_ASSERT_ALWAYS(tCurrentContext != nullptr);
    delete tCurrentContext;
    tCurrentContext = nullptr;
}

void MetaScheduler::Run(IWaitable& until)
{
    // while not done
    //     Acquire idle meta thread
    //     Affinitize to meta thread processor affinity
    //     Set meta thread in TLS context
    //     Add schedulers for meta thread
    // 1:  Run schedulers until idle
    //         If blocked (from within scheduler -- can't switch meta threads at this point)
    //             If supported, re-enter scheduler until idle or not blocked or done
    //             Release meta thread (put in high demand idle list to increase chance of re-acquiring)
    //             Yield until not blocked or done
    //             Wait for same meta thread to become available (actually.. must be able to migrate meta thread at this point or might starve)
    //         If only 1 scheduler active
    //             Run scheduler until done or meta thread required by other blocked threads
    //     Release meta thread
    //     Yield until not-idle or done
    //     Attempt to re-acquire same meta thread
    //         If successful
    //             Goto 1:
    //         Else
    //             Notify schedulers of meta thread move

    volatile bool done = false;
    auto doneWaiter = MakeWaiter([&] {done = true;});
    until.AddWaiter(&doneWaiter);
    while (!done)
    {

    }
}

void WaitFor(IWaitable& waitable, WaitMode)
{
    MetaScheduler::Context* context = MetaScheduler::tCurrentContext;

    if (context)
        context->WaitFor(waitable);
    else
    {
        auto waiter = MakeWaiter([&] { MetaScheduler::sSharedEvent.Set(); });
        Detail::SystemMutex::ScopedLock lock(MetaScheduler::sSharedEventLock);

        MetaScheduler::sSharedEvent.Reset();
        waitable.AddWaiter(&waiter);
        MetaScheduler::sSharedEvent.Wait();
    }
}

void WaitForAll(IWaitable** waitables, std::size_t count, WaitMode)
{
    IWaitable** unordered = CRUNCH_STACK_ALLOC_T(IWaitable*, count);
    IWaitable** ordered = CRUNCH_STACK_ALLOC_T(IWaitable*, count);
    std::size_t orderedCount = 0;
    std::size_t unorderedCount = 0;

    for (std::size_t i = 0; i < count; ++i)
    {
        if (waitables[i]->IsOrderDependent())
            ordered[orderedCount++] = waitables[i];
        else
            unordered[unorderedCount++] = waitables[i];
    }

    if (orderedCount != 0)
        std::sort(ordered, ordered + orderedCount);

    MetaScheduler::Context* context = MetaScheduler::tCurrentContext;

    if (context)
    {
        // Order dependent doesn't imply fair, so we need to wait for one at a time
        for (std::size_t i = 0; i < orderedCount; ++i)
            context->WaitFor(*ordered[i]);

        // TODO: could add all waiters in one go and do only one wait
        for (std::size_t i = 0; i < unorderedCount; ++i)
            context->WaitFor(*unordered[i]);
    }
    else
    {
        auto waiter = MakeWaiter([&] { MetaScheduler::sSharedEvent.Set(); });
        auto waitHelper = [&] (IWaitable& waitable) {
            MetaScheduler::sSharedEvent.Reset();
            waitable.AddWaiter(&waiter);
            MetaScheduler::sSharedEvent.Wait();
        };

        Detail::SystemMutex::ScopedLock lock(MetaScheduler::sSharedEventLock);

        for (std::size_t i = 0; i < orderedCount; ++i)
            waitHelper(*ordered[i]);

        for (std::size_t i = 0; i < unorderedCount; ++i)
            waitHelper(*unordered[i]);
    }
}

void WaitForAny(IWaitable** waitables, std::size_t count, WaitMode)
{
    struct WaiterHelper : Waiter, NonCopyable
    {
        WaiterHelper(Event& event)
            : event(event)
        {}

        virtual void Notify() CRUNCH_OVERRIDE
        {
            event.Set();
        }

        Event& event;
    };

    WaiterHelper* waiters = CRUNCH_STACK_ALLOC_T(WaiterHelper, count);

    Event event(false);

    for (std::size_t i = 0; i < count; ++i)
    {
        ::new (&waiters[i]) WaiterHelper(event);
        waitables[i]->AddWaiter(&waiters[i]);
    }

    MetaScheduler::Context* context = MetaScheduler::tCurrentContext;
    if (context)
    {
        context->WaitFor(event);
    }
    else
    {
        auto waiter = MakeWaiter([&] { MetaScheduler::sSharedEvent.Set(); });

        Detail::SystemMutex::ScopedLock lock(MetaScheduler::sSharedEventLock);
        MetaScheduler::sSharedEvent.Reset();
        event.AddWaiter(&waiter);
        MetaScheduler::sSharedEvent.Wait();
    }

    for (std::size_t i = 0; i < count; ++i)
    {
        waitables[i]->RemoveWaiter(&waiters[i]);
    }
}

}}
<commit_msg>crunch_concurrency - Simplified MetaScheduler WaitFor after SystemSemaphore improvements.<commit_after>// Copyright (c) 2011, Christian Rorvik
// Distributed under the Simplified BSD License (See accompanying file LICENSE.txt)

#include "crunch/concurrency/meta_scheduler.hpp"

#include "crunch/base/assert.hpp"
#include "crunch/base/noncopyable.hpp"
#include "crunch/base/override.hpp"
#include "crunch/base/stack_alloc.hpp"
#include "crunch/concurrency/event.hpp"
#include "crunch/concurrency/yield.hpp"
#include "crunch/concurrency/detail/system_semaphore.hpp"

#include <algorithm>

namespace Crunch { namespace Concurrency {

class MetaScheduler::Context : NonCopyable, Waiter
{
public:
    Context(MetaScheduler& owner)
        : mOwner(owner)
        , mWaitSemaphore(0)
        , mWaitSpinCount(400)
    {}

    void Notify() CRUNCH_OVERRIDE
    {
        mWaitSemaphore.Post();
    }

    void WaitFor(IWaitable& waitable)
    {
        // TODO: Let active scheduler handle the wait if it can
        // TODO: Keep in mind if active scheduler is a fiber scheduler we might come back on a different system thread.. (and this thread might be used for other things.. i.e. waiter must be stack local)
        waitable.AddWaiter(this);
        mWaitSemaphore.SpinWait(mWaitSpinCount);
    }

private:
    MetaScheduler& mOwner;
    Detail::SystemSemaphore mWaitSemaphore;
    uint32 mWaitSpinCount;
};

CRUNCH_THREAD_LOCAL MetaScheduler::Context* MetaScheduler::tCurrentContext = NULL;
Detail::SystemMutex MetaScheduler::sSharedEventLock;
Detail::SystemEvent MetaScheduler::sSharedEvent;

struct ContextRunState : Waiter
{
    ISchedulerContext* context;

    virtual void Notify() CRUNCH_OVERRIDE
    {
        // Add to active set
        // Wake up scheduler if sleeping
    }
};

class MetaScheduler::MetaThread
{
public:
    MetaThread(MetaThreadConfig const& config)
        : mConfig(config)
    {}

private:
    MetaThreadConfig mConfig;
};

MetaScheduler::MetaScheduler(const SchedulerList& schedulers)
    : mSchedulers(schedulers)
{}

MetaScheduler::~MetaScheduler()
{}

MetaScheduler::MetaThreadHandle MetaScheduler::CreateMetaThread(MetaThreadConfig const& config)
{
    Detail::SystemMutex::ScopedLock lock(mMetaThreadsLock);

    MetaThreadPtr mt(new MetaThread(config));
    mIdleMetaThreads.push_back(std::move(mt));
    return MetaThreadHandle();
}

void MetaScheduler::Join()
{
    CRUNCH_ASSERT_ALWAYS(tCurrentContext == nullptr);
    tCurrentContext = new Context(*this);
}

void MetaScheduler::Leave()
{
    CRUNCH_ASSERT_ALWAYS(tCurrentContext != nullptr);
    delete tCurrentContext;
    tCurrentContext = nullptr;
}

void MetaScheduler::Run(IWaitable& until)
{
    // while not done
    //     Acquire idle meta thread
    //     Affinitize to meta thread processor affinity
    //     Set meta thread in TLS context
    //     Add schedulers for meta thread
    // 1:  Run schedulers until idle
    //         If blocked (from within scheduler -- can't switch meta threads at this point)
    //             If supported, re-enter scheduler until idle or not blocked or done
    //             Release meta thread (put in high demand idle list to increase chance of re-acquiring)
    //             Yield until not blocked or done
    //             Wait for same meta thread to become available (actually.. must be able to migrate meta thread at this point or might starve)
    //         If only 1 scheduler active
    //             Run scheduler until done or meta thread required by other blocked threads
    //     Release meta thread
    //     Yield until not-idle or done
    //     Attempt to re-acquire same meta thread
    //         If successful
    //             Goto 1:
    //         Else
    //             Notify schedulers of meta thread move

    volatile bool done = false;
    auto doneWaiter = MakeWaiter([&] {done = true;});
    until.AddWaiter(&doneWaiter);
    while (!done)
    {

    }
}

void WaitFor(IWaitable& waitable, WaitMode)
{
    MetaScheduler::Context* context = MetaScheduler::tCurrentContext;

    if (context)
        context->WaitFor(waitable);
    else
    {
        auto waiter = MakeWaiter([&] { MetaScheduler::sSharedEvent.Set(); });
        Detail::SystemMutex::ScopedLock lock(MetaScheduler::sSharedEventLock);

        MetaScheduler::sSharedEvent.Reset();
        waitable.AddWaiter(&waiter);
        MetaScheduler::sSharedEvent.Wait();
    }
}

void WaitForAll(IWaitable** waitables, std::size_t count, WaitMode)
{
    IWaitable** unordered = CRUNCH_STACK_ALLOC_T(IWaitable*, count);
    IWaitable** ordered = CRUNCH_STACK_ALLOC_T(IWaitable*, count);
    std::size_t orderedCount = 0;
    std::size_t unorderedCount = 0;

    for (std::size_t i = 0; i < count; ++i)
    {
        if (waitables[i]->IsOrderDependent())
            ordered[orderedCount++] = waitables[i];
        else
            unordered[unorderedCount++] = waitables[i];
    }

    if (orderedCount != 0)
        std::sort(ordered, ordered + orderedCount);

    MetaScheduler::Context* context = MetaScheduler::tCurrentContext;

    if (context)
    {
        // Order dependent doesn't imply fair, so we need to wait for one at a time
        for (std::size_t i = 0; i < orderedCount; ++i)
            context->WaitFor(*ordered[i]);

        // TODO: could add all waiters in one go and do only one wait
        for (std::size_t i = 0; i < unorderedCount; ++i)
            context->WaitFor(*unordered[i]);
    }
    else
    {
        auto waiter = MakeWaiter([&] { MetaScheduler::sSharedEvent.Set(); });
        auto waitHelper = [&] (IWaitable& waitable) {
            MetaScheduler::sSharedEvent.Reset();
            waitable.AddWaiter(&waiter);
            MetaScheduler::sSharedEvent.Wait();
        };

        Detail::SystemMutex::ScopedLock lock(MetaScheduler::sSharedEventLock);

        for (std::size_t i = 0; i < orderedCount; ++i)
            waitHelper(*ordered[i]);

        for (std::size_t i = 0; i < unorderedCount; ++i)
            waitHelper(*unordered[i]);
    }
}

void WaitForAny(IWaitable** waitables, std::size_t count, WaitMode)
{
    struct WaiterHelper : Waiter, NonCopyable
    {
        WaiterHelper(Event& event)
            : event(event)
        {}

        virtual void Notify() CRUNCH_OVERRIDE
        {
            event.Set();
        }

        Event& event;
    };

    WaiterHelper* waiters = CRUNCH_STACK_ALLOC_T(WaiterHelper, count);

    Event event(false);

    for (std::size_t i = 0; i < count; ++i)
    {
        ::new (&waiters[i]) WaiterHelper(event);
        waitables[i]->AddWaiter(&waiters[i]);
    }

    MetaScheduler::Context* context = MetaScheduler::tCurrentContext;
    if (context)
    {
        context->WaitFor(event);
    }
    else
    {
        auto waiter = MakeWaiter([&] { MetaScheduler::sSharedEvent.Set(); });

        Detail::SystemMutex::ScopedLock lock(MetaScheduler::sSharedEventLock);
        MetaScheduler::sSharedEvent.Reset();
        event.AddWaiter(&waiter);
        MetaScheduler::sSharedEvent.Wait();
    }

    for (std::size_t i = 0; i < count; ++i)
    {
        waitables[i]->RemoveWaiter(&waiters[i]);
    }
}

}}
<|endoftext|>


<commit_before>#include "handler.hpp"

#include <algorithm>
#include <cstdint>
#include <memory>
#include <string>
#include <vector>

using std::size_t;
using std::uint16_t;
using std::uint32_t;
using std::uint64_t;
using std::uint8_t;

namespace blobs
{

namespace internal
{

/**
 * @brief: Get baseId from a blob id string
 * @param blobId: Input blob id which is expected to only contain alphanumerical
 *                characters and '/'.
 * @returns: the baseId containing the blobId, stripping all contents from the
 *           last '/'. If no '/' is present, an empty string is returned.
 */
static std::string getBaseFromId(const std::string& blobId)
{
    return blobId.substr(0, blobId.find_last_of('/') + 1);
}

} // namespace internal

void BinaryStoreBlobHandler::addNewBinaryStore(
    std::unique_ptr<binstore::BinaryStoreInterface> store)
{
    // TODO: this is a very rough measure to test the mock interface for now.
    stores_[store->getBaseBlobId()] = std::move(store);
}

bool BinaryStoreBlobHandler::canHandleBlob(const std::string& path)
{
    auto base = internal::getBaseFromId(path);
    if (base.empty() || base == path)
    {
        /* Operations on baseId itself or an empty base is not allowed */
        return false;
    }

    return std::any_of(stores_.begin(), stores_.end(),
                       [&](const auto& baseStorePair) {
                           return base == baseStorePair.second->getBaseBlobId();
                       });
}

std::vector<std::string> BinaryStoreBlobHandler::getBlobIds()
{
    std::vector<std::string> result;

    for (const auto& baseStorePair : stores_)
    {
        const auto& ids = baseStorePair.second->getBlobIds();
        result.insert(result.end(), ids.begin(), ids.end());
    }

    return result;
}

bool BinaryStoreBlobHandler::deleteBlob(const std::string& path)
{
    auto it = stores_.find(internal::getBaseFromId(path));
    if (it == stores_.end())
    {
        return false;
    }

    return it->second->deleteBlob(path);
}

bool BinaryStoreBlobHandler::stat(const std::string& path,
                                  struct BlobMeta* meta)
{
    // TODO: implement
    return false;
}

bool BinaryStoreBlobHandler::open(uint16_t session, uint16_t flags,
                                  const std::string& path)
{
    if (!canHandleBlob(path))
    {
        return false;
    }

    auto found = sessions_.find(session);
    if (found != sessions_.end())
    {
        /* This session is already active */
        return false;
    }

    const auto& base = internal::getBaseFromId(path);

    if (stores_.find(base) == stores_.end())
    {
        return false;
    }

    if (!stores_[base]->openOrCreateBlob(path, flags))
    {
        return false;
    }

    sessions_[session] = stores_[base].get();
    return true;
}

std::vector<uint8_t> BinaryStoreBlobHandler::read(uint16_t session,
                                                  uint32_t offset,
                                                  uint32_t requestedSize)
{
    auto it = sessions_.find(session);
    if (it == sessions_.end())
    {
        return std::vector<uint8_t>();
    }

    return it->second->read(offset, requestedSize);
}

bool BinaryStoreBlobHandler::write(uint16_t session, uint32_t offset,
                                   const std::vector<uint8_t>& data)
{
    auto it = sessions_.find(session);
    if (it == sessions_.end())
    {
        return false;
    }

    return it->second->write(offset, data);
}

bool BinaryStoreBlobHandler::writeMeta(uint16_t session, uint32_t offset,
                                       const std::vector<uint8_t>& data)
{
    /* Binary store handler doesn't support write meta */
    return false;
}

bool BinaryStoreBlobHandler::commit(uint16_t session,
                                    const std::vector<uint8_t>& data)
{
    auto it = sessions_.find(session);
    if (it == sessions_.end())
    {
        return false;
    }

    return it->second->commit();
}

bool BinaryStoreBlobHandler::close(uint16_t session)
{
    auto it = sessions_.find(session);
    if (it == sessions_.end())
    {
        return false;
    }

    if (!it->second->close())
    {
        return false;
    }

    sessions_.erase(session);
    return true;
}

bool BinaryStoreBlobHandler::stat(uint16_t session, struct BlobMeta* meta)
{
    // TODO: implement
    return false;
}

bool BinaryStoreBlobHandler::expire(uint16_t session)
{
    /* Binary store handler doesn't support expire */
    return false;
}

} // namespace blobs
<commit_msg>Implement expire<commit_after>#include "handler.hpp"

#include <algorithm>
#include <cstdint>
#include <memory>
#include <string>
#include <vector>

using std::size_t;
using std::uint16_t;
using std::uint32_t;
using std::uint64_t;
using std::uint8_t;

namespace blobs
{

namespace internal
{

/**
 * @brief: Get baseId from a blob id string
 * @param blobId: Input blob id which is expected to only contain alphanumerical
 *                characters and '/'.
 * @returns: the baseId containing the blobId, stripping all contents from the
 *           last '/'. If no '/' is present, an empty string is returned.
 */
static std::string getBaseFromId(const std::string& blobId)
{
    return blobId.substr(0, blobId.find_last_of('/') + 1);
}

} // namespace internal

void BinaryStoreBlobHandler::addNewBinaryStore(
    std::unique_ptr<binstore::BinaryStoreInterface> store)
{
    // TODO: this is a very rough measure to test the mock interface for now.
    stores_[store->getBaseBlobId()] = std::move(store);
}

bool BinaryStoreBlobHandler::canHandleBlob(const std::string& path)
{
    auto base = internal::getBaseFromId(path);
    if (base.empty() || base == path)
    {
        /* Operations on baseId itself or an empty base is not allowed */
        return false;
    }

    return std::any_of(stores_.begin(), stores_.end(),
                       [&](const auto& baseStorePair) {
                           return base == baseStorePair.second->getBaseBlobId();
                       });
}

std::vector<std::string> BinaryStoreBlobHandler::getBlobIds()
{
    std::vector<std::string> result;

    for (const auto& baseStorePair : stores_)
    {
        const auto& ids = baseStorePair.second->getBlobIds();
        result.insert(result.end(), ids.begin(), ids.end());
    }

    return result;
}

bool BinaryStoreBlobHandler::deleteBlob(const std::string& path)
{
    auto it = stores_.find(internal::getBaseFromId(path));
    if (it == stores_.end())
    {
        return false;
    }

    return it->second->deleteBlob(path);
}

bool BinaryStoreBlobHandler::stat(const std::string& path,
                                  struct BlobMeta* meta)
{
    // TODO: implement
    return false;
}

bool BinaryStoreBlobHandler::open(uint16_t session, uint16_t flags,
                                  const std::string& path)
{
    if (!canHandleBlob(path))
    {
        return false;
    }

    auto found = sessions_.find(session);
    if (found != sessions_.end())
    {
        /* This session is already active */
        return false;
    }

    const auto& base = internal::getBaseFromId(path);

    if (stores_.find(base) == stores_.end())
    {
        return false;
    }

    if (!stores_[base]->openOrCreateBlob(path, flags))
    {
        return false;
    }

    sessions_[session] = stores_[base].get();
    return true;
}

std::vector<uint8_t> BinaryStoreBlobHandler::read(uint16_t session,
                                                  uint32_t offset,
                                                  uint32_t requestedSize)
{
    auto it = sessions_.find(session);
    if (it == sessions_.end())
    {
        return std::vector<uint8_t>();
    }

    return it->second->read(offset, requestedSize);
}

bool BinaryStoreBlobHandler::write(uint16_t session, uint32_t offset,
                                   const std::vector<uint8_t>& data)
{
    auto it = sessions_.find(session);
    if (it == sessions_.end())
    {
        return false;
    }

    return it->second->write(offset, data);
}

bool BinaryStoreBlobHandler::writeMeta(uint16_t session, uint32_t offset,
                                       const std::vector<uint8_t>& data)
{
    /* Binary store handler doesn't support write meta */
    return false;
}

bool BinaryStoreBlobHandler::commit(uint16_t session,
                                    const std::vector<uint8_t>& data)
{
    auto it = sessions_.find(session);
    if (it == sessions_.end())
    {
        return false;
    }

    return it->second->commit();
}

bool BinaryStoreBlobHandler::close(uint16_t session)
{
    auto it = sessions_.find(session);
    if (it == sessions_.end())
    {
        return false;
    }

    if (!it->second->close())
    {
        return false;
    }

    sessions_.erase(session);
    return true;
}

bool BinaryStoreBlobHandler::stat(uint16_t session, struct BlobMeta* meta)
{
    // TODO: implement
    return false;
}

bool BinaryStoreBlobHandler::expire(uint16_t session)
{
    return close(session);
}

} // namespace blobs
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2007-2009 Digital Bazaar, Inc. All rights reserved.
 */
#include "db/net/ConnectionInputStream.h"

#include "db/net/Connection.h"
#include "db/util/Math.h"

#include <cstring>

using namespace std;
using namespace db::io;
using namespace db::net;
using namespace db::rt;
using namespace db::util;

#define MAX_READ_SIZE   1023

ConnectionInputStream::ConnectionInputStream(Connection* c) :
   mConnection(c),
   mBytesRead(0),
   mPeekBuffer(0),
   mPeeking(false)
{
}

ConnectionInputStream::~ConnectionInputStream()
{
}

inline int ConnectionInputStream::read(char* b, int length)
{
   int rval = 0;

   if(mPeeking || mPeekBuffer.isEmpty())
   {
      // throttle the read as appropriate
      BandwidthThrottler* bt = mConnection->getBandwidthThrottler(true);
      if(bt != NULL)
      {
         bt->requestBytes(length, length);
      }

      // read from the socket input stream
      InputStream* is = mConnection->getSocket()->getInputStream();
      if(is != NULL)
      {
         rval = is->read(b, length);
      }
      else
      {
         ExceptionRef e = new Exception(
            "Could not read from connection. Socket closed.",
            "db.net.Socket.Closed");
         Exception::set(e);
         rval = -1;
      }
   }
   else
   {
      // read from peek buffer
      rval = mPeekBuffer.get(b, length);
   }

   if(rval > 0 && !mPeeking)
   {
      // update bytes read (reset as necessary)
      if(mBytesRead > Math::HALF_MAX_LONG_VALUE)
      {
         mBytesRead = 0;
      }

      mBytesRead += rval;
   }

   return rval;
}

int ConnectionInputStream::readFully(char* b, int length)
{
   int rval = 0;

   // keep reading until eos, error, or length reached
   int remaining = length;
   int offset = 0;
   int numBytes = 0;
   while(remaining > 0 && (numBytes = read(b + offset, remaining)) > 0)
   {
      remaining -= numBytes;
      offset += numBytes;
      rval += numBytes;
   }

   if(numBytes == -1)
   {
      rval = -1;
   }

   return rval;
}

int ConnectionInputStream::readLine(string& line)
{
   int rval = 0;

   line.erase();

   // read one character at a time
   char c;
   int numBytes;
   while((numBytes = read(&c, 1)) > 0 && c != '\n')
   {
      // see if the character is a carriage return
      if(c == '\r')
      {
         // see if the next character is an eol -- and we've found a CRLF
         if(peek(&c, 1) > 0 && c == '\n')
         {
            // read the character in and discard it
            read(&c, 1);
         }

         // set character to an eol since a carriage return is treated the same
         c = '\n';
      }
      else
      {
         // append the character
         line.push_back(c);

         // a character was appended, so not end of stream
         rval = 1;
      }
   }

   if(numBytes == -1)
   {
      rval = -1;
   }

   return rval;
}

int ConnectionInputStream::readCrlf(string& line)
{
   int rval = 0;

   // reset line and create buffer to parse for CRLFs
   line.erase();
   char b[MAX_READ_SIZE + 1];

   // keep peeking ahead until there's an error or a line is completed either
   // by CRLF or EOF
   int numBytes;
   bool block = false;
   int readSize = MAX_READ_SIZE;
   bool eof = false;
   while(rval == 0 && !eof &&
         (numBytes = peek(b, readSize, block)) != -1)
   {
      if(numBytes == 0)
      {
         if(!block)
         {
            // not enough peek bytes available, but we didn't block,
            // so activate blocking to try and get more bytes
            block = true;
         }
         else
         {
            // we were blocking but still didn't get any peek bytes, so
            // we've hit the end of the stream
            eof = true;
         }
      }
      else
      {
         // NULL-terminate our buffer so we can use strchr() to find the
         // next CR
         b[numBytes] = 0;

         // now that peeked bytes are available, deactivate blocking and
         // reset the readSize
         block = false;
         readSize = MAX_READ_SIZE;

         // look for a CR (which will either find a novel CR, or a CR that
         // we left in the underlying peek buffer from a previous pass)
         char* i = strchr(b, '\r');
         if(i == NULL)
         {
            // CR not found, append all peeked bytes to the line and
            // then discard them
            line.append(b, numBytes);
            mPeekBuffer.clear(numBytes);
         }
         else
         {
            // determine the length of the data before the CR and append
            // all peeked bytes up to but not including the CR to the line
            int beforeCR = i - b;
            line.append(b, beforeCR);

            // see if there are more bytes in the buffer after the CR
            bool hasMore = ((beforeCR + 1) < numBytes);
            if(hasMore)
            {
               // check for an LF that immediately follows the CR
               if(i[1] == '\n')
               {
                  // CRLF found before end of stream, so a valid CRLF line has
                  // been found
                  rval = 1;

                  // discard peeked bytes and CRLF (+2 chars)
                  mPeekBuffer.clear(beforeCR + 2);
               }
               else
               {
                  // there is no following LF, so append the CR to the line,
                  // as we haven't found a CRLF line only a partial line that
                  // happens to have a CR in it
                  line.push_back('\r');

                  // discard peeked bytes and solo CR (+1 char)
                  mPeekBuffer.clear(beforeCR + 1);
               }
            }
            else
            {
               // there is not enough peeked data to see if there is a
               // LF following the CR we found, so only discard data
               // before the CR so it will stay alive in the underlying peek
               // buffer and come back up at the front of the buffer in the
               // next pass, also only read 2 bytes (CR+LF) in the next pass
               // because we may only have to look at the very next byte to
               // read a full CRLF line and we don't want to block forever
               // (or for a timeout) waiting for more data that won't ever
               // arrive
               mPeekBuffer.clear(beforeCR);
               readSize = 2;
            }
         }
      }

      // maximum line length of 1 MB
      if(rval == 0 && line.length() > (1024 << 10))
      {
         rval = -1;
         ExceptionRef e = new Exception(
            "Could not read CRLF, line too long.", "db.net.CRLFLineTooLong");
         Exception::set(e);
         break;
      }
   }

   if(numBytes == -1)
   {
      rval = -1;
   }

   return rval;
}

inline int ConnectionInputStream::peek(char* b, int length, bool block)
{
   int rval = 0;

   // see if more data needs to be read
   if(block && length > mPeekBuffer.length())
   {
      // allocate enough space in the peek buffer
      mPeekBuffer.allocateSpace(length, true);

      // read into the peek buffer from this stream
      mPeeking = true;
      rval = mPeekBuffer.put(this);
      mPeeking = false;
   }

   // check for peeked bytes
   if(!mPeekBuffer.isEmpty() && rval != -1)
   {
      // read from the peek buffer
      rval = mPeekBuffer.get(b, length);

      // reset peek buffer so that data will be read again
      mPeekBuffer.reset(rval);
   }

   return rval;
}

inline void ConnectionInputStream::close()
{
   // close socket input stream
   InputStream* is = mConnection->getSocket()->getInputStream();
   if(is != NULL)
   {
      is->close();
   }
}

inline uint64_t ConnectionInputStream::getBytesRead()
{
   return mBytesRead;
}
<commit_msg>Minor optimization.<commit_after>/*
 * Copyright (c) 2007-2009 Digital Bazaar, Inc. All rights reserved.
 */
#include "db/net/ConnectionInputStream.h"

#include "db/net/Connection.h"
#include "db/util/Math.h"

#include <cstring>

using namespace std;
using namespace db::io;
using namespace db::net;
using namespace db::rt;
using namespace db::util;

#define MAX_READ_SIZE   1023

ConnectionInputStream::ConnectionInputStream(Connection* c) :
   mConnection(c),
   mBytesRead(0),
   mPeekBuffer(0),
   mPeeking(false)
{
}

ConnectionInputStream::~ConnectionInputStream()
{
}

inline int ConnectionInputStream::read(char* b, int length)
{
   int rval = 0;

   if(mPeeking || mPeekBuffer.isEmpty())
   {
      // throttle the read as appropriate
      BandwidthThrottler* bt = mConnection->getBandwidthThrottler(true);
      if(bt != NULL)
      {
         bt->requestBytes(length, length);
      }

      // read from the socket input stream
      InputStream* is = mConnection->getSocket()->getInputStream();
      if(is != NULL)
      {
         rval = is->read(b, length);
      }
      else
      {
         ExceptionRef e = new Exception(
            "Could not read from connection. Socket closed.",
            "db.net.Socket.Closed");
         Exception::set(e);
         rval = -1;
      }
   }
   else
   {
      // read from peek buffer
      rval = mPeekBuffer.get(b, length);
   }

   if(rval > 0 && !mPeeking)
   {
      // update bytes read (reset as necessary)
      if(mBytesRead > Math::HALF_MAX_LONG_VALUE)
      {
         mBytesRead = 0;
      }

      mBytesRead += rval;
   }

   return rval;
}

int ConnectionInputStream::readFully(char* b, int length)
{
   int rval = 0;

   // keep reading until eos, error, or length reached
   int remaining = length;
   int offset = 0;
   int numBytes = 0;
   while(remaining > 0 && (numBytes = read(b + offset, remaining)) > 0)
   {
      remaining -= numBytes;
      offset += numBytes;
      rval += numBytes;
   }

   if(numBytes == -1)
   {
      rval = -1;
   }

   return rval;
}

int ConnectionInputStream::readLine(string& line)
{
   int rval = 0;

   line.erase();

   // read one character at a time
   char c;
   int numBytes;
   while((numBytes = read(&c, 1)) > 0 && c != '\n')
   {
      // see if the character is a carriage return
      if(c == '\r')
      {
         // see if the next character is an eol -- and we've found a CRLF
         if(peek(&c, 1) > 0 && c == '\n')
         {
            // discard the character
            mPeekBuffer.clear(1);
         }

         // set character to an eol since a carriage return is treated the same
         c = '\n';
      }
      else
      {
         // append the character
         line.push_back(c);

         // a character was appended, so not end of stream
         rval = 1;
      }
   }

   if(numBytes == -1)
   {
      rval = -1;
   }

   return rval;
}

int ConnectionInputStream::readCrlf(string& line)
{
   int rval = 0;

   // reset line and create buffer to parse for CRLFs
   line.erase();
   char b[MAX_READ_SIZE + 1];

   // keep peeking ahead until there's an error or a line is completed either
   // by CRLF or EOF
   int numBytes;
   bool block = false;
   int readSize = MAX_READ_SIZE;
   bool eof = false;
   while(rval == 0 && !eof &&
         (numBytes = peek(b, readSize, block)) != -1)
   {
      if(numBytes == 0)
      {
         if(!block)
         {
            // not enough peek bytes available, but we didn't block,
            // so activate blocking to try and get more bytes
            block = true;
         }
         else
         {
            // we were blocking but still didn't get any peek bytes, so
            // we've hit the end of the stream
            eof = true;
         }
      }
      else
      {
         // NULL-terminate our buffer so we can use strchr() to find the
         // next CR
         b[numBytes] = 0;

         // now that peeked bytes are available, deactivate blocking and
         // reset the readSize
         block = false;
         readSize = MAX_READ_SIZE;

         // look for a CR (which will either find a novel CR, or a CR that
         // we left in the underlying peek buffer from a previous pass)
         char* i = strchr(b, '\r');
         if(i == NULL)
         {
            // CR not found, append all peeked bytes to the line and
            // then discard them
            line.append(b, numBytes);
            mPeekBuffer.clear(numBytes);
         }
         else
         {
            // determine the length of the data before the CR and append
            // all peeked bytes up to but not including the CR to the line
            int beforeCR = i - b;
            line.append(b, beforeCR);

            // see if there are more bytes in the buffer after the CR
            bool hasMore = ((beforeCR + 1) < numBytes);
            if(hasMore)
            {
               // check for an LF that immediately follows the CR
               if(i[1] == '\n')
               {
                  // CRLF found before end of stream, so a valid CRLF line has
                  // been found
                  rval = 1;

                  // discard peeked bytes and CRLF (+2 chars)
                  mPeekBuffer.clear(beforeCR + 2);
               }
               else
               {
                  // there is no following LF, so append the CR to the line,
                  // as we haven't found a CRLF line only a partial line that
                  // happens to have a CR in it
                  line.push_back('\r');

                  // discard peeked bytes and solo CR (+1 char)
                  mPeekBuffer.clear(beforeCR + 1);
               }
            }
            else
            {
               // there is not enough peeked data to see if there is a
               // LF following the CR we found, so only discard data
               // before the CR so it will stay alive in the underlying peek
               // buffer and come back up at the front of the buffer in the
               // next pass, also only read 2 bytes (CR+LF) in the next pass
               // because we may only have to look at the very next byte to
               // read a full CRLF line and we don't want to block forever
               // (or for a timeout) waiting for more data that won't ever
               // arrive
               mPeekBuffer.clear(beforeCR);
               readSize = 2;
            }
         }
      }

      // maximum line length of 1 MB
      if(rval == 0 && line.length() > (1024 << 10))
      {
         rval = -1;
         ExceptionRef e = new Exception(
            "Could not read CRLF, line too long.", "db.net.CRLFLineTooLong");
         Exception::set(e);
         break;
      }
   }

   if(numBytes == -1)
   {
      rval = -1;
   }

   return rval;
}

inline int ConnectionInputStream::peek(char* b, int length, bool block)
{
   int rval = 0;

   // see if more data needs to be read
   if(block && length > mPeekBuffer.length())
   {
      // allocate enough space in the peek buffer
      mPeekBuffer.allocateSpace(length, true);

      // read into the peek buffer from this stream
      mPeeking = true;
      rval = mPeekBuffer.put(this);
      mPeeking = false;
   }

   // check for peeked bytes
   if(!mPeekBuffer.isEmpty() && rval != -1)
   {
      // read from the peek buffer
      rval = mPeekBuffer.get(b, length);

      // reset peek buffer so that data will be read again
      mPeekBuffer.reset(rval);
   }

   return rval;
}

inline void ConnectionInputStream::close()
{
   // close socket input stream
   InputStream* is = mConnection->getSocket()->getInputStream();
   if(is != NULL)
   {
      is->close();
   }
}

inline uint64_t ConnectionInputStream::getBytesRead()
{
   return mBytesRead;
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2009-2010 by Bjoern Kolbeck, Zuse Institute Berlin
 *
 * Licensed under the BSD License, see LICENSE file for details.
 *
 */

#include "rpc/client_connection.h"

#include <errno.h>
#include <boost/bind.hpp>
#include <iostream>
#include <string>
#include <vector>

#include "rpc/grid_ssl_socket_channel.h"
#include "rpc/ssl_socket_channel.h"
#include "rpc/tcp_socket_channel.h"
#include "util/logging.h"

namespace xtreemfs {
namespace rpc {

using namespace xtreemfs::pbrpc;
using namespace xtreemfs::util;
using namespace std;
using namespace boost;
using namespace google::protobuf;
using namespace boost::asio::ip;

ClientConnection::ClientConnection(
    const string& server_name,
    const string& port,
    asio::io_service& service,
    request_map *request_table,
    int32_t connect_timeout_s, int32_t max_reconnect_interval_s,
    bool use_gridssl, boost::asio::ssl::context* ssl_context)
    : receive_marker_(NULL),
      receive_hdr_(NULL),
      receive_msg_(NULL),
      receive_data_(NULL),
      connection_state_(IDLE),
      requests_(),
      current_request_(NULL),
      server_name_(server_name),
      server_port_(port),
      service_(service),
      resolver_(service),
      socket_(NULL),
      endpoint_(NULL),
      request_table_(request_table),
      timer_(service),
      connect_timeout_s_(connect_timeout_s),
      max_reconnect_interval_s_(max_reconnect_interval_s),
      next_reconnect_at_(boost::posix_time::not_a_date_time),
      reconnect_interval_s_(1),
      use_gridssl_(use_gridssl),
      ssl_context_(ssl_context) {
  receive_marker_buffer_ = new char[RecordMarker::get_size()];
  CreateChannel();
}

void ClientConnection::AddRequest(ClientRequest* request) {
  requests_.push(request);
  (*request_table_)[request->call_id()] = request;
}

void ClientConnection::SendError(POSIXErrno posix_errno,
                                 const string &error_message) {
  if (!requests_.empty()) {
    Logging::log->getLog(LEVEL_ERROR)
        << "operation failed: errno="
        << posix_errno << " message="
        << error_message << endl;
    RPCHeader::ErrorResponse *err = new RPCHeader::ErrorResponse();
    err->set_error_type(IO_ERROR);
    err->set_posix_errno(posix_errno);
    err->set_error_message(error_message);
    while (!requests_.empty()) {
      ClientRequest *request = requests_.front();
      request_table_->erase(request->call_id());
      requests_.pop();
      request->set_error(new RPCHeader::ErrorResponse(*err));
      request->ExecuteCallback();
    }
    delete err;
  }
}

void ClientConnection::DoProcess() {
  last_used_ = posix_time::second_clock::local_time();

  if (connection_state_ == IDLE) {
    if (endpoint_ == NULL) {
      Connect();
    } else {
      // Do write.
      SendRequest();
    }
  } else if (connection_state_ == WAIT_FOR_RECONNECT) {
    posix_time::ptime now = posix_time::second_clock::local_time();
    if (next_reconnect_at_ <= now) {
      next_reconnect_at_ = posix_time::not_a_date_time;

      if (Logging::log->loggingActive(LEVEL_DEBUG)) {
        Logging::log->getLog(LEVEL_DEBUG) << "trying reconnect..." << endl;
      }

      Connect();
    } else {
      SendError(POSIX_ERROR_EIO,
                string("cannot connect to server, reconnect blocked"));
    }
  }
}

void ClientConnection::CreateChannel() {
  if (socket_ != NULL) {
    socket_->close();
    delete socket_;
  }
  if (ssl_context_ == NULL) {
    socket_ = new TCPSocketChannel(service_);
  } else if (use_gridssl_) {
    socket_ = new GridSSLSocketChannel(service_, *ssl_context_);
  } else {
    socket_ = new SSLSocketChannel(service_, *ssl_context_);
  }
}

void ClientConnection::Connect() {
  connection_state_ = CONNECTING;
  asio::ip::tcp::resolver::query query(server_name_, server_port_);
  resolver_.async_resolve(query,
                          bind(&ClientConnection::PostResolve, this,
                               asio::placeholders::error,
                               asio::placeholders::iterator));
  if (Logging::log->loggingActive(LEVEL_DEBUG)) {
    Logging::log->getLog(LEVEL_DEBUG) << "connect timeout is "
        << connect_timeout_s_ << " seconds\n";
  }
}

void ClientConnection::OnConnectTimeout(const boost::system::error_code& err) {
  if (err != asio::error::operation_aborted) {
    Reset();
    SendError(POSIX_ERROR_EIO, string("connection to '")
        + server_name_ + ":" + server_port_ + "' timed out");
  }
}

void ClientConnection::PostResolve(
    const boost::system::error_code& err,
    tcp::resolver::iterator endpoint_iterator) {
  if (err) {
    Reset();
    SendError(POSIX_ERROR_EIO,
        std::string("could not connect to '")
        + server_name_ + ":" + server_port_
        + "': " + err.message());
  }
  if (endpoint_iterator != tcp::resolver::iterator()) {
    if (Logging::log->loggingActive(LEVEL_DEBUG)) {
      Logging::log->getLog(LEVEL_DEBUG) << "resolved: "
          << (*endpoint_iterator).host_name() << endl;
    }

    endpoint_ = new tcp::endpoint(*endpoint_iterator);

    timer_.expires_from_now(posix_time::seconds(connect_timeout_s_));
    timer_.async_wait(bind(&ClientConnection::OnConnectTimeout,
                           this,
                           asio::placeholders::error));
    socket_->async_connect(*endpoint_,
                           bind(&ClientConnection::PostConnect,
                                this,
                                asio::placeholders::error,
                                endpoint_iterator));
  } else {
    SendError(POSIX_ERROR_EINVAL, string("cannot resolve hostname: '")
        + this->server_name_ + ":" + server_port_ + string("'"));
  }
}

void ClientConnection::PostConnect(
    const boost::system::error_code& err,
    tcp::resolver::iterator endpoint_iterator) {
  timer_.cancel();
  if (err) {
    if (err == asio::error::operation_aborted) {
      return;
    }

    if (++endpoint_iterator != tcp::resolver::iterator()) {
      // Try next endpoint.
      CreateChannel();
      delete endpoint_;
      endpoint_ = NULL;

      if (Logging::log->loggingActive(LEVEL_DEBUG)) {
        Logging::log->getLog(LEVEL_DEBUG) << "failed: next endpoint"
            << err.message() << "\n";
      }

      PostResolve(err, endpoint_iterator);
    } else {
      Reset();
      SendError(POSIX_ERROR_EIO, string("could not connect to host name '")
          + server_name_ + "': " + err.message());
    }
  } else {
    // Do something useful.
    reconnect_interval_s_ = 1;
    next_reconnect_at_ = posix_time::not_a_date_time;

    if (Logging::log->loggingActive(LEVEL_DEBUG)) {
      Logging::log->getLog(LEVEL_DEBUG) << "connected: "
          << (*endpoint_iterator).host_name() << endl;
    }

    connection_state_ = IDLE;
    if (!requests_.empty()) {
      SendRequest();
      ReceiveRequest();
    }
  }
}

void ClientConnection::SendRequest() {
  if (!requests_.empty()) {
    connection_state_ = ACTIVE;

    ClientRequest* rq = requests_.front();
    assert(rq != NULL);

    if (rq->cancelled()) {
      delete rq;
      // The element must be poped from the queue.
      requests_.pop();
      SendRequest();
    }

    const RecordMarker* rrm = rq->request_marker();

    vector<boost::asio::const_buffer> bufs;
    bufs.push_back(boost::asio::buffer(
        reinterpret_cast<const void*>(rq->rq_hdr_msg()),
        RecordMarker::get_size() + rrm->header_len() + rrm->message_len()));

    if (rrm->data_len() > 0) {
      bufs.push_back(boost::asio::buffer(
        reinterpret_cast<const void*> (rq->rq_data()), rrm->data_len()));
    }

    socket_->async_write(bufs, bind(&ClientConnection::PostWrite, this,
        asio::placeholders::error, asio::placeholders::bytes_transferred));
  } else {
    connection_state_ = IDLE;
  }
}

void ClientConnection::ReceiveRequest() {
  if (endpoint_) {
    socket_->async_read(asio::buffer(receive_marker_buffer_,
                                     RecordMarker::get_size()),
                        bind(&ClientConnection::PostReadRecordMarker,
                             this,
                             asio::placeholders::error));
  }
}

void ClientConnection::Reset() {
  CreateChannel();
  delete endpoint_;
  endpoint_ = NULL;
  connection_state_ = WAIT_FOR_RECONNECT;

  if (Logging::log->loggingActive(LEVEL_DEBUG)) {
    Logging::log->getLog(LEVEL_DEBUG)
        << "connection reset, next reconnect in " << reconnect_interval_s_
        << "s " << endl;
  }

  next_reconnect_at_ = posix_time::second_clock::local_time()
      + posix_time::seconds(reconnect_interval_s_);
  reconnect_interval_s_ = reconnect_interval_s_ * 2;
  if (reconnect_interval_s_ > max_reconnect_interval_s_) {
    reconnect_interval_s_ = max_reconnect_interval_s_;
  }
}

void ClientConnection::Close() {
  socket_->close();
  delete socket_;
  socket_ = NULL;
  connection_state_ = CLOSED;
  SendError(POSIX_ERROR_EIO, "connection to '" + server_name_
      + "' closed locally");
}

void ClientConnection::PostWrite(const boost::system::error_code& err,
                                 size_t bytes_written) {
  if (err) {
    Reset();
    SendError(POSIX_ERROR_EIO, "could not send request to '"
        + server_name_+":"+server_port_ + "': " + err.message());
  } else {
    // Send next?
    requests_.pop();
    connection_state_ = IDLE;
    SendRequest();
  }
}

void ClientConnection::PostReadRecordMarker(
    const boost::system::error_code& err) {
  if (err) {
    Reset();
    string msg = "could not read record marker in response from '"
        + server_name_ + ":" + server_port_ + "': " + err.message();
    SendError(POSIX_ERROR_EIO, msg);
  } else {
    // Do read.
    receive_marker_ = new RecordMarker(receive_marker_buffer_);

    vector<boost::asio::mutable_buffer> bufs;
    receive_hdr_ = new char[receive_marker_->header_len()];
    bufs.push_back(asio::buffer(reinterpret_cast<void*> (receive_hdr_),
                                receive_marker_->header_len()));
    if (receive_marker_->message_len() > 0) {
      receive_msg_ = new char[receive_marker_->message_len()];
      bufs.push_back(asio::buffer(reinterpret_cast<void*> (receive_msg_),
                                  receive_marker_->message_len()));
    } else {
      receive_msg_ = NULL;
    }
    if (receive_marker_->data_len() > 0) {
      receive_data_ = new char[receive_marker_->data_len()];
      bufs.push_back(asio::buffer(reinterpret_cast<void*> (receive_data_),
                                  receive_marker_->data_len()));
    } else {
      receive_data_ = NULL;
    }
    socket_->async_read(bufs,
                        bind(&ClientConnection::PostReadMessage,
                             this,
                             asio::placeholders::error));
  }
}

void ClientConnection::PostReadMessage(const boost::system::error_code& err) {
  if (err) {
    Reset();
    SendError(POSIX_ERROR_EIO, "could not read response from '" +
        server_name_ + "': " + err.message());
  } else {
    // Parse header.
    RPCHeader *respHdr = new RPCHeader();
    if (respHdr->ParseFromArray(receive_hdr_, receive_marker_->header_len())) {
      delete[] receive_hdr_;
      receive_hdr_ = NULL;
    } else {
      // Error parsing the header.
      DeleteInternalBuffers();
      delete respHdr;
      Reset();
      SendError(POSIX_ERROR_EINVAL, "received garbage header from '" +
          server_name_ + ", closing connection");
      return;
    }

    // Get request from table.
    request_map::iterator iter = request_table_->find(respHdr->call_id());
    ClientRequest *rq;
    if (iter != request_table_->end()) {
      rq = iter->second;
    } else {
      if (Logging::log->loggingActive(LEVEL_WARN)) {
        Logging::log->getLog(LEVEL_WARN)
            << "Received response for unknown request id: "
            << respHdr->call_id() << " from " << server_name_ << std::endl;
      }
      DeleteInternalBuffers();
      delete respHdr;
      return;
    }

    uint32 call_id = respHdr->call_id();

    if (respHdr->has_error_response()) {
      // Error response.
      rq->set_error(new RPCHeader::ErrorResponse(respHdr->error_response()));
      // Manually cleanup response header.
      delete respHdr;
    } else {
      // Parse message, if exists.
      if (receive_marker_->message_len() > 0) {
        if (!rq->resp_message()) {
          // Not prepared to receive a message.
          // Print error and discard data.
          Logging::log->getLog(LEVEL_WARN)
            << "Received an unexpected response message (expected size 0, got "
            << receive_marker_->message_len() << " bytes) from "
            << server_name_ << std::endl;
        } else {
          assert(receive_msg_ != NULL);
          if (!rq->resp_message()->ParseFromArray(
              receive_msg_,
              receive_marker_->message_len())) {
            // Parsing message failed. Generate error.
            RPCHeader::ErrorResponse *err = new RPCHeader::ErrorResponse();
            err->set_error_type(GARBAGE_ARGS);
            err->set_posix_errno(POSIX_ERROR_NONE);
            err->set_error_message(string("cannot parse message data: ")
                + rq->resp_message()->InitializationErrorString());
            rq->set_error(err);

            // manually cleanup response header
            delete respHdr;
          } else {
            // Message successfully parsed, set data.
            // Hand over responsibility for receive_data_ to request object.
            rq->set_resp_data(receive_data_);
            rq->set_resp_data_len(receive_marker_->data_len());
            receive_data_ = NULL;
          }
        }
      }
      // Always set response header.
      rq->set_resp_header(respHdr);
    }

    // Remove from table and clean up buffers.
    request_table_->erase(call_id);
    DeleteInternalBuffers();
    rq->ExecuteCallback();

    // Receive next request.
    ReceiveRequest();
  }
}

void ClientConnection::DeleteInternalBuffers() {
  delete[] receive_hdr_;
  receive_hdr_ = NULL;
  delete[] receive_msg_;
  receive_msg_ = NULL;
  delete[] receive_data_;
  receive_data_ = NULL;
  delete receive_marker_;
  receive_marker_ = NULL;
}

ClientConnection::~ClientConnection() {
  delete endpoint_;
  delete socket_;
  delete[] receive_marker_buffer_;
  DeleteInternalBuffers();
}

}  // namespace rpc
}  // namespace xtreemfs
<commit_msg>client: Added server port to logging output of RPC Client.<commit_after>/*
 * Copyright (c) 2009-2010 by Bjoern Kolbeck, Zuse Institute Berlin
 *
 * Licensed under the BSD License, see LICENSE file for details.
 *
 */

#include "rpc/client_connection.h"

#include <errno.h>
#include <boost/bind.hpp>
#include <iostream>
#include <string>
#include <vector>

#include "rpc/grid_ssl_socket_channel.h"
#include "rpc/ssl_socket_channel.h"
#include "rpc/tcp_socket_channel.h"
#include "util/logging.h"

namespace xtreemfs {
namespace rpc {

using namespace xtreemfs::pbrpc;
using namespace xtreemfs::util;
using namespace std;
using namespace boost;
using namespace google::protobuf;
using namespace boost::asio::ip;

ClientConnection::ClientConnection(
    const string& server_name,
    const string& port,
    asio::io_service& service,
    request_map *request_table,
    int32_t connect_timeout_s, int32_t max_reconnect_interval_s,
    bool use_gridssl, boost::asio::ssl::context* ssl_context)
    : receive_marker_(NULL),
      receive_hdr_(NULL),
      receive_msg_(NULL),
      receive_data_(NULL),
      connection_state_(IDLE),
      requests_(),
      current_request_(NULL),
      server_name_(server_name),
      server_port_(port),
      service_(service),
      resolver_(service),
      socket_(NULL),
      endpoint_(NULL),
      request_table_(request_table),
      timer_(service),
      connect_timeout_s_(connect_timeout_s),
      max_reconnect_interval_s_(max_reconnect_interval_s),
      next_reconnect_at_(boost::posix_time::not_a_date_time),
      reconnect_interval_s_(1),
      use_gridssl_(use_gridssl),
      ssl_context_(ssl_context) {
  receive_marker_buffer_ = new char[RecordMarker::get_size()];
  CreateChannel();
}

void ClientConnection::AddRequest(ClientRequest* request) {
  requests_.push(request);
  (*request_table_)[request->call_id()] = request;
}

void ClientConnection::SendError(POSIXErrno posix_errno,
                                 const string &error_message) {
  if (!requests_.empty()) {
    Logging::log->getLog(LEVEL_ERROR)
        << "operation failed: errno="
        << posix_errno << " message="
        << error_message << endl;
    RPCHeader::ErrorResponse *err = new RPCHeader::ErrorResponse();
    err->set_error_type(IO_ERROR);
    err->set_posix_errno(posix_errno);
    err->set_error_message(error_message);
    while (!requests_.empty()) {
      ClientRequest *request = requests_.front();
      request_table_->erase(request->call_id());
      requests_.pop();
      request->set_error(new RPCHeader::ErrorResponse(*err));
      request->ExecuteCallback();
    }
    delete err;
  }
}

void ClientConnection::DoProcess() {
  last_used_ = posix_time::second_clock::local_time();

  if (connection_state_ == IDLE) {
    if (endpoint_ == NULL) {
      Connect();
    } else {
      // Do write.
      SendRequest();
    }
  } else if (connection_state_ == WAIT_FOR_RECONNECT) {
    posix_time::ptime now = posix_time::second_clock::local_time();
    if (next_reconnect_at_ <= now) {
      next_reconnect_at_ = posix_time::not_a_date_time;

      if (Logging::log->loggingActive(LEVEL_DEBUG)) {
        Logging::log->getLog(LEVEL_DEBUG) << "trying reconnect..." << endl;
      }

      Connect();
    } else {
      SendError(POSIX_ERROR_EIO,
                "cannot connect to server '" + server_name_ + ":" + server_port_
                    + "', reconnect blocked");
    }
  }
}

void ClientConnection::CreateChannel() {
  if (socket_ != NULL) {
    socket_->close();
    delete socket_;
  }
  if (ssl_context_ == NULL) {
    socket_ = new TCPSocketChannel(service_);
  } else if (use_gridssl_) {
    socket_ = new GridSSLSocketChannel(service_, *ssl_context_);
  } else {
    socket_ = new SSLSocketChannel(service_, *ssl_context_);
  }
}

void ClientConnection::Connect() {
  connection_state_ = CONNECTING;
  asio::ip::tcp::resolver::query query(server_name_, server_port_);
  resolver_.async_resolve(query,
                          bind(&ClientConnection::PostResolve, this,
                               asio::placeholders::error,
                               asio::placeholders::iterator));
  if (Logging::log->loggingActive(LEVEL_DEBUG)) {
    Logging::log->getLog(LEVEL_DEBUG) << "connect timeout is "
        << connect_timeout_s_ << " seconds\n";
  }
}

void ClientConnection::OnConnectTimeout(const boost::system::error_code& err) {
  if (err != asio::error::operation_aborted) {
    Reset();
    SendError(POSIX_ERROR_EIO,
              "connection to '" + server_name_ + ":" + server_port_
                  + "' timed out");
  }
}

void ClientConnection::PostResolve(
    const boost::system::error_code& err,
    tcp::resolver::iterator endpoint_iterator) {
  if (err) {
    Reset();
    SendError(POSIX_ERROR_EIO,
              "could not connect to '" + server_name_ + ":" + server_port_
                  + "': " + err.message());
  }
  if (endpoint_iterator != tcp::resolver::iterator()) {
    if (Logging::log->loggingActive(LEVEL_DEBUG)) {
      Logging::log->getLog(LEVEL_DEBUG) << "resolved: "
          << (*endpoint_iterator).host_name() << endl;
    }

    endpoint_ = new tcp::endpoint(*endpoint_iterator);

    timer_.expires_from_now(posix_time::seconds(connect_timeout_s_));
    timer_.async_wait(bind(&ClientConnection::OnConnectTimeout,
                           this,
                           asio::placeholders::error));
    socket_->async_connect(*endpoint_,
                           bind(&ClientConnection::PostConnect,
                                this,
                                asio::placeholders::error,
                                endpoint_iterator));
  } else {
    SendError(POSIX_ERROR_EINVAL, string("cannot resolve hostname: '")
        + this->server_name_ + ":" + server_port_ + string("'"));
  }
}

void ClientConnection::PostConnect(
    const boost::system::error_code& err,
    tcp::resolver::iterator endpoint_iterator) {
  timer_.cancel();
  if (err) {
    if (err == asio::error::operation_aborted) {
      return;
    }

    if (++endpoint_iterator != tcp::resolver::iterator()) {
      // Try next endpoint.
      CreateChannel();
      delete endpoint_;
      endpoint_ = NULL;

      if (Logging::log->loggingActive(LEVEL_DEBUG)) {
        Logging::log->getLog(LEVEL_DEBUG) << "failed: next endpoint"
            << err.message() << "\n";
      }

      PostResolve(err, endpoint_iterator);
    } else {
      Reset();
      SendError(POSIX_ERROR_EIO,
                "could not connect to host '" + server_name_ + ":"
                    + server_port_ + "': " + err.message());
    }
  } else {
    // Do something useful.
    reconnect_interval_s_ = 1;
    next_reconnect_at_ = posix_time::not_a_date_time;

    if (Logging::log->loggingActive(LEVEL_DEBUG)) {
      Logging::log->getLog(LEVEL_DEBUG) << "connected: "
          << (*endpoint_iterator).host_name() << endl;
    }

    connection_state_ = IDLE;
    if (!requests_.empty()) {
      SendRequest();
      ReceiveRequest();
    }
  }
}

void ClientConnection::SendRequest() {
  if (!requests_.empty()) {
    connection_state_ = ACTIVE;

    ClientRequest* rq = requests_.front();
    assert(rq != NULL);

    if (rq->cancelled()) {
      delete rq;
      // The element must be poped from the queue.
      requests_.pop();
      SendRequest();
    }

    const RecordMarker* rrm = rq->request_marker();

    vector<boost::asio::const_buffer> bufs;
    bufs.push_back(boost::asio::buffer(
        reinterpret_cast<const void*>(rq->rq_hdr_msg()),
        RecordMarker::get_size() + rrm->header_len() + rrm->message_len()));

    if (rrm->data_len() > 0) {
      bufs.push_back(boost::asio::buffer(
        reinterpret_cast<const void*> (rq->rq_data()), rrm->data_len()));
    }

    socket_->async_write(bufs, bind(&ClientConnection::PostWrite, this,
        asio::placeholders::error, asio::placeholders::bytes_transferred));
  } else {
    connection_state_ = IDLE;
  }
}

void ClientConnection::ReceiveRequest() {
  if (endpoint_) {
    socket_->async_read(asio::buffer(receive_marker_buffer_,
                                     RecordMarker::get_size()),
                        bind(&ClientConnection::PostReadRecordMarker,
                             this,
                             asio::placeholders::error));
  }
}

void ClientConnection::Reset() {
  CreateChannel();
  delete endpoint_;
  endpoint_ = NULL;
  connection_state_ = WAIT_FOR_RECONNECT;

  if (Logging::log->loggingActive(LEVEL_DEBUG)) {
    Logging::log->getLog(LEVEL_DEBUG)
        << "connection reset, next reconnect in " << reconnect_interval_s_
        << "s " << endl;
  }

  next_reconnect_at_ = posix_time::second_clock::local_time()
      + posix_time::seconds(reconnect_interval_s_);
  reconnect_interval_s_ = reconnect_interval_s_ * 2;
  if (reconnect_interval_s_ > max_reconnect_interval_s_) {
    reconnect_interval_s_ = max_reconnect_interval_s_;
  }
}

void ClientConnection::Close() {
  socket_->close();
  delete socket_;
  socket_ = NULL;
  connection_state_ = CLOSED;
  SendError(POSIX_ERROR_EIO,
            "connection to '" + server_name_ + ":" + server_port_ + "' closed"
                " locally");
}

void ClientConnection::PostWrite(const boost::system::error_code& err,
                                 size_t bytes_written) {
  if (err) {
    Reset();
    SendError(POSIX_ERROR_EIO,
              "could not send request to '" + server_name_ + ":" +server_port_
                  + "': " + err.message());
  } else {
    // Send next?
    requests_.pop();
    connection_state_ = IDLE;
    SendRequest();
  }
}

void ClientConnection::PostReadRecordMarker(
    const boost::system::error_code& err) {
  if (err) {
    Reset();
    SendError(POSIX_ERROR_EIO,
              "could not read record marker in response from '" + server_name_
                  + ":" + server_port_ + "': " + err.message());
  } else {
    // Do read.
    receive_marker_ = new RecordMarker(receive_marker_buffer_);

    vector<boost::asio::mutable_buffer> bufs;
    receive_hdr_ = new char[receive_marker_->header_len()];
    bufs.push_back(asio::buffer(reinterpret_cast<void*> (receive_hdr_),
                                receive_marker_->header_len()));
    if (receive_marker_->message_len() > 0) {
      receive_msg_ = new char[receive_marker_->message_len()];
      bufs.push_back(asio::buffer(reinterpret_cast<void*> (receive_msg_),
                                  receive_marker_->message_len()));
    } else {
      receive_msg_ = NULL;
    }
    if (receive_marker_->data_len() > 0) {
      receive_data_ = new char[receive_marker_->data_len()];
      bufs.push_back(asio::buffer(reinterpret_cast<void*> (receive_data_),
                                  receive_marker_->data_len()));
    } else {
      receive_data_ = NULL;
    }
    socket_->async_read(bufs,
                        bind(&ClientConnection::PostReadMessage,
                             this,
                             asio::placeholders::error));
  }
}

void ClientConnection::PostReadMessage(const boost::system::error_code& err) {
  if (err) {
    Reset();
    SendError(POSIX_ERROR_EIO,
              "could not read response from '" + server_name_ + ":"
                  + server_port_ + "': " + err.message());
  } else {
    // Parse header.
    RPCHeader *respHdr = new RPCHeader();
    if (respHdr->ParseFromArray(receive_hdr_, receive_marker_->header_len())) {
      delete[] receive_hdr_;
      receive_hdr_ = NULL;
    } else {
      // Error parsing the header.
      DeleteInternalBuffers();
      delete respHdr;
      Reset();
      SendError(POSIX_ERROR_EINVAL,
                "received garbage header from '" + server_name_ + ":"
                    + server_port_ + "', closing connection");
      return;
    }

    // Get request from table.
    request_map::iterator iter = request_table_->find(respHdr->call_id());
    ClientRequest *rq;
    if (iter != request_table_->end()) {
      rq = iter->second;
    } else {
      if (Logging::log->loggingActive(LEVEL_WARN)) {
        Logging::log->getLog(LEVEL_WARN)
            << "Received response for unknown request id: "
            << respHdr->call_id() << " from " << server_name_ << std::endl;
      }
      DeleteInternalBuffers();
      delete respHdr;
      return;
    }

    uint32 call_id = respHdr->call_id();

    if (respHdr->has_error_response()) {
      // Error response.
      rq->set_error(new RPCHeader::ErrorResponse(respHdr->error_response()));
      // Manually cleanup response header.
      delete respHdr;
    } else {
      // Parse message, if exists.
      if (receive_marker_->message_len() > 0) {
        if (!rq->resp_message()) {
          // Not prepared to receive a message.
          // Print error and discard data.
          Logging::log->getLog(LEVEL_WARN)
            << "Received an unexpected response message (expected size 0, got "
            << receive_marker_->message_len() << " bytes) from "
            << server_name_ << std::endl;
        } else {
          assert(receive_msg_ != NULL);
          if (!rq->resp_message()->ParseFromArray(
              receive_msg_,
              receive_marker_->message_len())) {
            // Parsing message failed. Generate error.
            RPCHeader::ErrorResponse *err = new RPCHeader::ErrorResponse();
            err->set_error_type(GARBAGE_ARGS);
            err->set_posix_errno(POSIX_ERROR_NONE);
            err->set_error_message(string("cannot parse message data: ")
                + rq->resp_message()->InitializationErrorString());
            rq->set_error(err);

            // manually cleanup response header
            delete respHdr;
          } else {
            // Message successfully parsed, set data.
            // Hand over responsibility for receive_data_ to request object.
            rq->set_resp_data(receive_data_);
            rq->set_resp_data_len(receive_marker_->data_len());
            receive_data_ = NULL;
          }
        }
      }
      // Always set response header.
      rq->set_resp_header(respHdr);
    }

    // Remove from table and clean up buffers.
    request_table_->erase(call_id);
    DeleteInternalBuffers();
    rq->ExecuteCallback();

    // Receive next request.
    ReceiveRequest();
  }
}

void ClientConnection::DeleteInternalBuffers() {
  delete[] receive_hdr_;
  receive_hdr_ = NULL;
  delete[] receive_msg_;
  receive_msg_ = NULL;
  delete[] receive_data_;
  receive_data_ = NULL;
  delete receive_marker_;
  receive_marker_ = NULL;
}

ClientConnection::~ClientConnection() {
  delete endpoint_;
  delete socket_;
  delete[] receive_marker_buffer_;
  DeleteInternalBuffers();
}

}  // namespace rpc
}  // namespace xtreemfs
<|endoftext|>