<commit_before>#include "ConfirmationPageWidget.h"
#include "ui_ConfirmationPageWidget.h"
#include "DatasetList.h"
#include "SearchQuery.h"
#include "Algorithm.h"
#include "ParameterPageWidget.h"
#include "log.h"
#include <QJsonDocument>
#include <QDebug>
#include <QResizeEvent>
#include <QPointer>

/**
 * @brief ConfirmationPageWidget::ConfirmationPageWidget Constructs a ConfirmationPageWidget.
 */
ConfirmationPageWidget::ConfirmationPageWidget() :
    ui(new Ui::ConfirmationPageWidget)
{
    ui->setupUi(this);
    ui->mSearchButton->setText(tr("Suche starten"));

    ui->mParameters->setRowCount(1);
    ui->mParameters->setColumnCount(3);

    ui->mParameters->setHorizontalHeaderItem(0, new QTableWidgetItem(tr("Datenordner")));
    ui->mParameters->setHorizontalHeaderItem(1, new QTableWidgetItem(tr("Algorithmus")));
    ui->mParameters->setHorizontalHeaderItem(2, new QTableWidgetItem(tr("Parameter")));
    ui->mParameters->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);

}

/**
 * @brief ConfirmationPageWidget::~ConfirmationPageWidget Destructs a ConfirmationPageWidget.
 */
ConfirmationPageWidget::~ConfirmationPageWidget()
{
    delete ui;
}

/**
 * @brief ConfirmationPageWidget::reset
 */
void ConfirmationPageWidget::reset()
{
    clearTable();
    std::shared_ptr<DatasetList> list = nullptr;
    int indexOfChosenDataset = -1;
    const Dataset* chosenDataset = nullptr;

    //read available datasets from the stack
    QVariant varDatasets;
    emit readFromStack(-2, varDatasets);
    if(varDatasets.canConvert<std::shared_ptr<DatasetList>>()){
        list = varDatasets.value<std::shared_ptr<DatasetList>>();
    } else {
        LOG_ERR("no datasets");
    }

    //read the index of the chosen dataset in the dataset list from the stack
    QVariant varChosenDataset;
    emit readFromStack(4, varChosenDataset);
    if(varChosenDataset.canConvert<int>()) {
        indexOfChosenDataset = varChosenDataset.value<int>();

        if((list != nullptr) && (indexOfChosenDataset < list->getDatasetList().size())) {
            chosenDataset = &(list->getDatasetList().at(indexOfChosenDataset));
            ui->mParameters->insertRow(0);
            ui->mParameters->setItem(0, 0, new QTableWidgetItem(chosenDataset->getName()));
            ui->mParameters->item(0, 0)->setTextAlignment(Qt::AlignCenter);
        }
    }
    if(chosenDataset == nullptr) {
        LOG_ERR("no chosen dataset");
        return;
    }

    //read the SearchObject from the stack
    QVariant varSearchQuery;
    emit readFromStack(3, varSearchQuery);
    std::shared_ptr<SearchQuery> searchQuery = nullptr;
    if(varSearchQuery.canConvert<std::shared_ptr<SearchQuery>>()) {
        searchQuery = varSearchQuery.value<std::shared_ptr<SearchQuery>>();
        SearchObject searchObject = searchQuery->getSearchObject();

        QImage chosenImage;
        if(searchObject.getMediumIndex() == 0) {
            chosenImage.load(searchObject.getMedium());
        } else if(searchObject.getMediumIndex() == 1) {
            Dataset dataset(searchObject.getMedium());
            chosenImage.load(dataset.getPath() + "/" +
                ((SingleFrameVideo*)dataset.getMediaList().at(0))->getFrameList().at(searchObject.getFrameIndex()));
        } else {
            LOG_ERR("No valid medium");
        }
        pixmap.convertFromImage(chosenImage);

        if(searchObject.getType() == SearchObject::ROI) {
            pixmap = pixmap.copy(searchObject.getROI());
        } else if(searchObject.getType() == SearchObject::ANNOTATION) {
            if (searchObject.getAnnotation()->getForm() == Annotation::RECTANGLE) {
                RectangleAnnotation* rectAnnotation = (RectangleAnnotation*)searchObject.getAnnotation();
                if(rectAnnotation != nullptr) {
                    QRect rect = static_cast<QRect> (*rectAnnotation);
                    pixmap = pixmap.copy(rect);
                }
            }
        }

        if(!pixmap.isNull()) {
            ui->mImageToSearchLabel->setMinimumSize(1, 1);
            ui->mImageToSearchLabel->setPixmap(pixmap.scaled(ui->mImageToSearchLabel->size(), Qt::KeepAspectRatio));
        } else {
            ui->mImageToSearchLabel->setText(tr("Bild kann nicht geöffnet werden."));
            LOG_ERR("medium of search object cannot be opened");
        }
    } else {
        LOG_ERR("no searchQuery");
    }

    // add names of chosen datasets
    QVariant varDatasetIndices;
    emit readFromStack(1, varDatasetIndices);
    if(varDatasetIndices.canConvert<std::shared_ptr<QModelIndexList>>()) {
        std::shared_ptr<QModelIndexList> indices = varDatasetIndices.value<std::shared_ptr<QModelIndexList>>();
        for(int i = 0; i < indices.get()->size(); i++) {
            int index = indices.get()->at(i).row();
            if((list != nullptr) && (index < list->getDatasetList().size())) {
                QString name = list->getDatasetList().at(index).getName();
                QList<QString> datasetList;
                if(name != chosenDataset->getName()) {
                    ui->mParameters->insertRow(i);
                    ui->mParameters->setItem(i, 0, new QTableWidgetItem(name));
                    ui->mParameters->item(i, 0)->setTextAlignment(Qt::AlignCenter);
                    datasetList.append(list->getDatasetList().at(index).getPath());
                }
                searchQuery->setDatasets(searchQuery->getDatasets()+datasetList);
            } else {
                LOG_ERR("invalid dataset index");
            }
        }
    }

    //read name of algorithm
    QVariant chosenAlgorithm;
    QPointer<Algorithm> algo = nullptr;
    emit readFromStack(2, chosenAlgorithm);
    if(chosenAlgorithm.canConvert<QPointer<Algorithm>>()){
        algo = chosenAlgorithm.value<QPointer<Algorithm>>();
		ui->mParameters->setItem(0, 1, new QTableWidgetItem(algo->getName()));
        ui->mParameters->item(0, 1)->setTextAlignment(Qt::AlignCenter);
    } else {
        LOG_ERR("no algorithm");
    }

    //read parameters and their values
    QVariant varParameters;
    emit readFromStack(0, varParameters);
    if(varParameters.canConvert<std::shared_ptr<QJsonObject>>()) {
        std::shared_ptr<QJsonObject> parameters = varParameters.value<std::shared_ptr<QJsonObject>>();
    /*if (parameterFile.canConvert<QString>()) {
        QString fileName = parameterFile.value<QString>();
        QFile file(fileName);
        if(!file.open(QFile::ReadOnly)) {
            LOG_ERR("cannot open parameter file");
        }
        QJsonObject json = QJsonDocument::fromJson(file.readAll()).object();*/
        QJsonObject object = parameters->value("root").toObject();
        QJsonObject::const_iterator iter;
        int i = 0;
        for(iter = object.begin(); iter != object.end(); iter++) {
            //QVariant value = iter.value().toObject()["default"].toVariant();
            ui->mParameters->setRowCount(std::max(object.keys().size(), ui->mParameters->rowCount()));
            //ui->mParameters->setItem(i, 2, new QTableWidgetItem(iter.key() + " = " + value.toString()));
            QString kex = iter.key();
            QString val = iter.value().toVariant().toString();
            ui->mParameters->setItem(i, 2, new QTableWidgetItem(iter.key() + " = " + iter.value().toVariant().toString()));

    /*mParameterList.clear();
    if(algo != nullptr) {
        QJsonObject parameterJson = algo->getParameters();
        readParameters(parameterJson["Properties"].toObject());
        for(int i = 0; i < mParameterList.size(); i++) {
            ui->mParameters->setRowCount(std::max(mParameterList.size(), ui->mParameters->rowCount()));
            ui->mParameters->setItem(i, 2, new QTableWidgetItem(mParameterList.at(i)));*/

            ui->mParameters->item(i, 2)->setTextAlignment(Qt::AlignCenter);
            i++;
        }
    }

    ui->mParameters->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch);
}

/**
 * @brief ConfirmationPageWidget::readParameters
 * @param object
 */
void ConfirmationPageWidget::readParameters(QJsonObject object) {
    QJsonObject::const_iterator iter;
    for(iter = object.begin(); iter != object.end(); iter++) {
        if (iter.value().toObject()["default"] != QJsonValue::Null) {
            QVariant line = iter.key() + " = " + iter.value().toObject()["default"].toVariant().toString();
            mParameterList.append(line.toString());
        } else {
            if(iter.value().isObject()) {
                mParameterList.append(iter.key() + ":");
                readParameters(iter.value().toObject());
            }
        }
    }
}

/**
 * @brief ConfirmationPageWidget::clearTable Resets the QTableWidget which contains the parameters.
 */
void ConfirmationPageWidget::clearTable() {
    while(ui->mParameters->rowCount() > 0) {
        ui->mParameters->removeRow(0);
    }
}

/**
 * @brief ConfirmationPageWidget::retranslateUi
 */
void ConfirmationPageWidget::retranslateUi() {
    ui->mSearchButton->setText(tr("Suche starten"));
    ui->mParameters->horizontalHeaderItem(0)->setText(tr("Datenordner"));
    ui->mParameters->horizontalHeaderItem(1)->setText(tr("Algorithmus"));
    ui->mParameters->horizontalHeaderItem(2)->setText(tr("Parameter"));
    if(pixmap.isNull()) {
        ui->mImageToSearchLabel->setText(tr("Bild kann nicht geöffnet werden."));
    }
}

/**
 * @brief ConfirmationPageWidget::getName
 * @return
 */
QString ConfirmationPageWidget::getName() {
    return tr("CoBaB - Bestätigung");
}

/**
 * @brief ConfirmationPageWidget::showEvent Sets the label with the photo to the correct size
 * when showing the label for the first time.
 * @param event Is sent when the widget is shown.
 */
void ConfirmationPageWidget::showEvent(QShowEvent* event) {
    if(!event->spontaneous() && !pixmap.isNull()) {
        ui->mImageToSearchLabel->setPixmap(pixmap.scaled(ui->mImageToSearchLabel->size(), Qt::KeepAspectRatio));
    }
}

/**
 * @brief ConfirmationPageWidget::resizeEvent Resizes the label with the photo when the size of the GUI changed.
 * @param event The resizeEvent when the size of the GUI changed.
 */
void ConfirmationPageWidget::resizeEvent(QResizeEvent* event) {
    Q_UNUSED(event);
    ui->mImageToSearchLabel->setMinimumSize(1, 1);
    ui->mImageToSearchLabel->setPixmap(pixmap.scaled(ui->mImageToSearchLabel->size(), Qt::KeepAspectRatio));
}

/**
 * @brief ConfirmationPageWidget::on_mSearchButton_clicked Sends the signal that the ConfirmationPageWidget
 * wants to exit and the ResultsPageWidget should be shown.
 */
void ConfirmationPageWidget::on_mSearchButton_clicked()
{
    exit(EXIT_NEXT);
}

/**
 * @brief ConfirmationPageWidget::EXIT_NEXT Code noting the type of exit,
 * in this case the ResultsPageWidget is the next widget.
 */
const int ConfirmationPageWidget::EXIT_NEXT = 0;
<commit_msg>Corrected search in multiple datasets.<commit_after>#include "ConfirmationPageWidget.h"
#include "ui_ConfirmationPageWidget.h"
#include "DatasetList.h"
#include "SearchQuery.h"
#include "Algorithm.h"
#include "ParameterPageWidget.h"
#include "log.h"
#include <QJsonDocument>
#include <QDebug>
#include <QResizeEvent>
#include <QPointer>

/**
 * @brief ConfirmationPageWidget::ConfirmationPageWidget Constructs a ConfirmationPageWidget.
 */
ConfirmationPageWidget::ConfirmationPageWidget() :
    ui(new Ui::ConfirmationPageWidget)
{
    ui->setupUi(this);
    ui->mSearchButton->setText(tr("Suche starten"));

    ui->mParameters->setRowCount(1);
    ui->mParameters->setColumnCount(3);

    ui->mParameters->setHorizontalHeaderItem(0, new QTableWidgetItem(tr("Datenordner")));
    ui->mParameters->setHorizontalHeaderItem(1, new QTableWidgetItem(tr("Algorithmus")));
    ui->mParameters->setHorizontalHeaderItem(2, new QTableWidgetItem(tr("Parameter")));
    ui->mParameters->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);

}

/**
 * @brief ConfirmationPageWidget::~ConfirmationPageWidget Destructs a ConfirmationPageWidget.
 */
ConfirmationPageWidget::~ConfirmationPageWidget()
{
    delete ui;
}

/**
 * @brief ConfirmationPageWidget::reset
 */
void ConfirmationPageWidget::reset()
{
    clearTable();
    std::shared_ptr<DatasetList> list = nullptr;
    int indexOfChosenDataset = -1;
    const Dataset* chosenDataset = nullptr;

    //read available datasets from the stack
    QVariant varDatasets;
    emit readFromStack(-2, varDatasets);
    if(varDatasets.canConvert<std::shared_ptr<DatasetList>>()){
        list = varDatasets.value<std::shared_ptr<DatasetList>>();
    } else {
        LOG_ERR("no datasets");
    }

    //read the index of the chosen dataset in the dataset list from the stack
    QVariant varChosenDataset;
    emit readFromStack(4, varChosenDataset);
    if(varChosenDataset.canConvert<int>()) {
        indexOfChosenDataset = varChosenDataset.value<int>();

        if((list != nullptr) && (indexOfChosenDataset < list->getDatasetList().size())) {
            chosenDataset = &(list->getDatasetList().at(indexOfChosenDataset));
            ui->mParameters->insertRow(0);
            ui->mParameters->setItem(0, 0, new QTableWidgetItem(chosenDataset->getName()));
            ui->mParameters->item(0, 0)->setTextAlignment(Qt::AlignCenter);
        }
    }
    if(chosenDataset == nullptr) {
        LOG_ERR("no chosen dataset");
        return;
    }

    //read the SearchObject from the stack
    QVariant varSearchQuery;
    emit readFromStack(3, varSearchQuery);
    std::shared_ptr<SearchQuery> searchQuery = nullptr;
    if(varSearchQuery.canConvert<std::shared_ptr<SearchQuery>>()) {
        searchQuery = varSearchQuery.value<std::shared_ptr<SearchQuery>>();
        SearchObject searchObject = searchQuery->getSearchObject();

        QImage chosenImage;
        if(searchObject.getMediumIndex() == 0) {
            chosenImage.load(searchObject.getMedium());
        } else if(searchObject.getMediumIndex() == 1) {
            Dataset dataset(searchObject.getMedium());
            chosenImage.load(dataset.getPath() + "/" +
                ((SingleFrameVideo*)dataset.getMediaList().at(0))->getFrameList().at(searchObject.getFrameIndex()));
        } else {
            LOG_ERR("No valid medium");
        }
        pixmap.convertFromImage(chosenImage);

        if(searchObject.getType() == SearchObject::ROI) {
            pixmap = pixmap.copy(searchObject.getROI());
        } else if(searchObject.getType() == SearchObject::ANNOTATION) {
            if (searchObject.getAnnotation()->getForm() == Annotation::RECTANGLE) {
                RectangleAnnotation* rectAnnotation = (RectangleAnnotation*)searchObject.getAnnotation();
                if(rectAnnotation != nullptr) {
                    QRect rect = static_cast<QRect> (*rectAnnotation);
                    pixmap = pixmap.copy(rect);
                }
            }
        }

        if(!pixmap.isNull()) {
            ui->mImageToSearchLabel->setMinimumSize(1, 1);
            ui->mImageToSearchLabel->setPixmap(pixmap.scaled(ui->mImageToSearchLabel->size(), Qt::KeepAspectRatio));
        } else {
            ui->mImageToSearchLabel->setText(tr("Bild kann nicht geöffnet werden."));
            LOG_ERR("medium of search object cannot be opened");
        }
    } else {
        LOG_ERR("no searchQuery");
    }

    // add names of chosen datasets
    QVariant varDatasetIndices;
    emit readFromStack(1, varDatasetIndices);
    if(varDatasetIndices.canConvert<std::shared_ptr<QModelIndexList>>()) {
        std::shared_ptr<QModelIndexList> indices = varDatasetIndices.value<std::shared_ptr<QModelIndexList>>();
        QList<QString> datasetList;
        datasetList.append(chosenDataset->getPath());
        for(int i = 0; i < indices.get()->size(); i++) {
            int index = indices.get()->at(i).row();
            if((list != nullptr) && (index < list->getDatasetList().size())) {
                QString name = list->getDatasetList().at(index).getName();
                if(name != chosenDataset->getName()) {
                    ui->mParameters->insertRow(i);
                    ui->mParameters->setItem(i, 0, new QTableWidgetItem(name));
                    ui->mParameters->item(i, 0)->setTextAlignment(Qt::AlignCenter);
                    datasetList.append(list->getDatasetList().at(index).getPath());
                }
            } else {
                LOG_ERR("invalid dataset index");
            }
        }
        searchQuery->setDatasets(datasetList);
    }

    //read name of algorithm
    QVariant chosenAlgorithm;
    QPointer<Algorithm> algo = nullptr;
    emit readFromStack(2, chosenAlgorithm);
    if(chosenAlgorithm.canConvert<QPointer<Algorithm>>()){
        algo = chosenAlgorithm.value<QPointer<Algorithm>>();
		ui->mParameters->setItem(0, 1, new QTableWidgetItem(algo->getName()));
        ui->mParameters->item(0, 1)->setTextAlignment(Qt::AlignCenter);
    } else {
        LOG_ERR("no algorithm");
    }

    //read parameters and their values
    QVariant varParameters;
    emit readFromStack(0, varParameters);
    if(varParameters.canConvert<std::shared_ptr<QJsonObject>>()) {
        std::shared_ptr<QJsonObject> parameters = varParameters.value<std::shared_ptr<QJsonObject>>();
    /*if (parameterFile.canConvert<QString>()) {
        QString fileName = parameterFile.value<QString>();
        QFile file(fileName);
        if(!file.open(QFile::ReadOnly)) {
            LOG_ERR("cannot open parameter file");
        }
        QJsonObject json = QJsonDocument::fromJson(file.readAll()).object();*/
        QJsonObject object = parameters->value("root").toObject();
        QJsonObject::const_iterator iter;
        int i = 0;
        for(iter = object.begin(); iter != object.end(); iter++) {
            //QVariant value = iter.value().toObject()["default"].toVariant();
            ui->mParameters->setRowCount(std::max(object.keys().size(), ui->mParameters->rowCount()));
            //ui->mParameters->setItem(i, 2, new QTableWidgetItem(iter.key() + " = " + value.toString()));
            QString kex = iter.key();
            QString val = iter.value().toVariant().toString();
            ui->mParameters->setItem(i, 2, new QTableWidgetItem(iter.key() + " = " + iter.value().toVariant().toString()));

    /*mParameterList.clear();
    if(algo != nullptr) {
        QJsonObject parameterJson = algo->getParameters();
        readParameters(parameterJson["Properties"].toObject());
        for(int i = 0; i < mParameterList.size(); i++) {
            ui->mParameters->setRowCount(std::max(mParameterList.size(), ui->mParameters->rowCount()));
            ui->mParameters->setItem(i, 2, new QTableWidgetItem(mParameterList.at(i)));*/

            ui->mParameters->item(i, 2)->setTextAlignment(Qt::AlignCenter);
            i++;
        }
    }

    ui->mParameters->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch);
}

/**
 * @brief ConfirmationPageWidget::readParameters
 * @param object
 */
void ConfirmationPageWidget::readParameters(QJsonObject object) {
    QJsonObject::const_iterator iter;
    for(iter = object.begin(); iter != object.end(); iter++) {
        if (iter.value().toObject()["default"] != QJsonValue::Null) {
            QVariant line = iter.key() + " = " + iter.value().toObject()["default"].toVariant().toString();
            mParameterList.append(line.toString());
        } else {
            if(iter.value().isObject()) {
                mParameterList.append(iter.key() + ":");
                readParameters(iter.value().toObject());
            }
        }
    }
}

/**
 * @brief ConfirmationPageWidget::clearTable Resets the QTableWidget which contains the parameters.
 */
void ConfirmationPageWidget::clearTable() {
    while(ui->mParameters->rowCount() > 0) {
        ui->mParameters->removeRow(0);
    }
}

/**
 * @brief ConfirmationPageWidget::retranslateUi
 */
void ConfirmationPageWidget::retranslateUi() {
    ui->mSearchButton->setText(tr("Suche starten"));
    ui->mParameters->horizontalHeaderItem(0)->setText(tr("Datenordner"));
    ui->mParameters->horizontalHeaderItem(1)->setText(tr("Algorithmus"));
    ui->mParameters->horizontalHeaderItem(2)->setText(tr("Parameter"));
    if(pixmap.isNull()) {
        ui->mImageToSearchLabel->setText(tr("Bild kann nicht geöffnet werden."));
    }
}

/**
 * @brief ConfirmationPageWidget::getName
 * @return
 */
QString ConfirmationPageWidget::getName() {
    return tr("CoBaB - Bestätigung");
}

/**
 * @brief ConfirmationPageWidget::showEvent Sets the label with the photo to the correct size
 * when showing the label for the first time.
 * @param event Is sent when the widget is shown.
 */
void ConfirmationPageWidget::showEvent(QShowEvent* event) {
    if(!event->spontaneous() && !pixmap.isNull()) {
        ui->mImageToSearchLabel->setPixmap(pixmap.scaled(ui->mImageToSearchLabel->size(), Qt::KeepAspectRatio));
    }
}

/**
 * @brief ConfirmationPageWidget::resizeEvent Resizes the label with the photo when the size of the GUI changed.
 * @param event The resizeEvent when the size of the GUI changed.
 */
void ConfirmationPageWidget::resizeEvent(QResizeEvent* event) {
    Q_UNUSED(event);
    ui->mImageToSearchLabel->setMinimumSize(1, 1);
    ui->mImageToSearchLabel->setPixmap(pixmap.scaled(ui->mImageToSearchLabel->size(), Qt::KeepAspectRatio));
}

/**
 * @brief ConfirmationPageWidget::on_mSearchButton_clicked Sends the signal that the ConfirmationPageWidget
 * wants to exit and the ResultsPageWidget should be shown.
 */
void ConfirmationPageWidget::on_mSearchButton_clicked()
{
    exit(EXIT_NEXT);
}

/**
 * @brief ConfirmationPageWidget::EXIT_NEXT Code noting the type of exit,
 * in this case the ResultsPageWidget is the next widget.
 */
const int ConfirmationPageWidget::EXIT_NEXT = 0;
<|endoftext|>


<commit_before>#include "../src/meshoptimizer.hpp"

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <ctime>

#include "objparser.hpp"

using namespace meshopt;

const size_t kCacheSize = 24;

struct Vertex
{
	float px, py, pz;
	float nx, ny, nz;
	float tx, ty;
};

struct Mesh
{
	std::vector<Vertex> vertices;
	std::vector<unsigned int> indices;
};

Mesh generatePlane(unsigned int N)
{
	Mesh result;

	result.vertices.reserve((N + 1) * (N + 1));
	result.indices.reserve(N * N * 6);

	for (unsigned int y = 0; y <= N; ++y)
		for (unsigned int x = 0; x <= N; ++x)
		{
			Vertex v = {float(x), float(y), 0, 0, 0, 1, float(x) / float(N), float(y) / float(N)};

			result.vertices.push_back(v);
		}

	for (unsigned int y = 0; y < N; ++y)
		for (unsigned int x = 0; x < N; ++x)
		{
			result.indices.push_back((y + 0) * (N + 1) + (x + 0));
			result.indices.push_back((y + 0) * (N + 1) + (x + 1));
			result.indices.push_back((y + 1) * (N + 1) + (x + 0));

			result.indices.push_back((y + 1) * (N + 1) + (x + 0));
			result.indices.push_back((y + 0) * (N + 1) + (x + 1));
			result.indices.push_back((y + 1) * (N + 1) + (x + 1));
		}

	return result;
}

Mesh parseObj(const char* path, clock_t& reindex)
{
	ObjFile file;

	if (!objParseFile(file, path))
	{
		printf("Error loading %s: file not found\n", path);
		return Mesh();
	}

	if (!objValidate(file))
	{
		printf("Error loading %s: invalid file data\n", path);
		return Mesh();
	}

	objTriangulate(file);

	size_t total_indices = file.f.size() / 3;

	std::vector<Vertex> vertices;
	vertices.reserve(total_indices);

	for (size_t i = 0; i < file.f.size(); i += 3)
	{
		int vi = file.f[i + 0];
		int vti = file.f[i + 1];
		int vni = file.f[i + 2];

		Vertex v =
		    {
		        file.v[vi * 3 + 0],
		        file.v[vi * 3 + 1],
		        file.v[vi * 3 + 2],

		        vni >= 0 ? file.vn[vni * 3 + 0] : 0,
		        vni >= 0 ? file.vn[vni * 3 + 1] : 0,
		        vni >= 0 ? file.vn[vni * 3 + 2] : 0,

		        vti >= 0 ? file.vt[vti * 3 + 0] : 0,
		        vti >= 0 ? file.vt[vti * 3 + 1] : 0,
		    };

		vertices.push_back(v);
	}

	reindex = clock();

	Mesh result;

	std::vector<unsigned int> remap(total_indices);

	size_t total_vertices = generateVertexRemap(&remap[0], NULL, total_indices, &vertices[0], total_indices, sizeof(Vertex));

	result.indices.resize(total_indices);
	remapIndexBuffer(&result.indices[0], NULL, total_indices, &remap[0]);

	result.vertices.resize(total_vertices);
	remapVertexBuffer(&result.vertices[0], &vertices[0], total_indices, sizeof(Vertex), &remap[0]);

	return result;
}

void optNone(Mesh& mesh)
{
	(void)mesh;
}

void optRandomShuffle(Mesh& mesh)
{
	std::vector<unsigned int> faces(mesh.indices.size() / 3);

	for (size_t i = 0; i < faces.size(); ++i)
		faces[i] = static_cast<unsigned int>(i);

	std::random_shuffle(faces.begin(), faces.end());

	std::vector<unsigned int> result(mesh.indices.size());

	for (size_t i = 0; i < faces.size(); ++i)
	{
		result[i * 3 + 0] = mesh.indices[faces[i] * 3 + 0];
		result[i * 3 + 1] = mesh.indices[faces[i] * 3 + 1];
		result[i * 3 + 2] = mesh.indices[faces[i] * 3 + 2];
	}

	mesh.indices.swap(result);
}

void optCache(Mesh& mesh)
{
	optimizeVertexCache(&mesh.indices[0], &mesh.indices[0], mesh.indices.size(), mesh.vertices.size(), kCacheSize);
}

void optCache0(Mesh& mesh)
{
	optimizeVertexCache(&mesh.indices[0], &mesh.indices[0], mesh.indices.size(), mesh.vertices.size(), 0);
}

void optOverdraw(Mesh& mesh)
{
	// use worst-case ACMR threshold so that overdraw optimizer can sort *all* triangles
	// warning: this significantly deteriorates the vertex cache efficiency so it is not advised; look at optComplete for the recommended method
	const float kThreshold = 3.f;
	optimizeOverdraw(&mesh.indices[0], &mesh.indices[0], mesh.indices.size(), &mesh.vertices[0].px, sizeof(Vertex), mesh.vertices.size(), kCacheSize, kThreshold);
}

void optFetch(Mesh& mesh)
{
	optimizeVertexFetch(&mesh.vertices[0], &mesh.vertices[0], &mesh.indices[0], mesh.indices.size(), mesh.vertices.size(), sizeof(Vertex));
}

void optComplete(Mesh& mesh)
{
	// vertex cache optimization should go first as it provides data for overdraw
	optimizeVertexCache(&mesh.indices[0], &mesh.indices[0], mesh.indices.size(), mesh.vertices.size(), kCacheSize);

	// reorder indices for overdraw, balancing overdraw and vertex cache efficiency
	const float kThreshold = 1.05f; // allow up to 5% worse ACMR to get more reordering opportunities for overdraw
	optimizeOverdraw(&mesh.indices[0], &mesh.indices[0], mesh.indices.size(), &mesh.vertices[0].px, sizeof(Vertex), mesh.vertices.size(), kCacheSize, kThreshold);

	// vertex fetch optimization should go last as it depends on the final index order
	optimizeVertexFetch(&mesh.vertices[0], &mesh.vertices[0], &mesh.indices[0], mesh.indices.size(), mesh.vertices.size(), sizeof(Vertex));
}

void optCompleteSimplify(Mesh& mesh)
{
	// generate 4 LOD levels (1-4), with each subsequent LOD using 70% triangles
	// note that each LOD uses the same (shared) vertex buffer
	// vertex cache optimization should go first as it provides data for overdraw
	std::vector<unsigned int> lods[5];

	lods[0] = mesh.indices;

	for (size_t i = 1; i < sizeof(lods) / sizeof(lods[0]); ++i)
	{
		std::vector<unsigned int>& lod = lods[i];

		float threshold = powf(0.7f, float(i));
		size_t target_index_count = size_t(mesh.indices.size() * threshold);

		// we can simplify all the way from base level or from the last result
		// simplifying from the base level sometimes produces better results, but simplifying from last level is faster
		const std::vector<unsigned int>& source = lods[i - 1];

		lod.resize(source.size());
		lod.resize(simplify(&lod[0], &source[0], source.size(), &mesh.vertices[0], mesh.vertices.size(), sizeof(Vertex), target_index_count));
	}

	// optimize each individual LOD for vertex cache & overdraw
	for (size_t i = 0; i < sizeof(lods) / sizeof(lods[0]); ++i)
	{
		std::vector<unsigned int>& lod = lods[i];

		optimizeVertexCache(&lod[0], &lod[0], lod.size(), mesh.vertices.size(), kCacheSize);
		optimizeOverdraw(&lod[0], &lod[0], lod.size(), &mesh.vertices[0].px, sizeof(Vertex), mesh.vertices.size(), kCacheSize, 1.0f);
	}

	// concatenate all LODs into one IB
	mesh.indices.clear();

	for (size_t i = 0; i < sizeof(lods) / sizeof(lods[0]); ++i)
	{
		mesh.indices.insert(mesh.indices.end(), lods[i].begin(), lods[i].end());
	}

	// vertex fetch optimization should go last as it depends on the final index order
	// note that the order of LODs above affects vertex fetch results; here we optimize for LOD0 fetch as a primary goal
	optimizeVertexFetch(&mesh.vertices[0], &mesh.vertices[0], &mesh.indices[0], mesh.indices.size(), mesh.vertices.size(), sizeof(Vertex));

	printf("%-9s:", "Simplify");

	for (size_t i = 0; i < sizeof(lods) / sizeof(lods[0]); ++i)
	{
		printf(" LOD%d %d", int(i), int(lods[i].size()) / 3);
	}

	printf("\n");
}

void optimize(const Mesh& mesh, const char* name, void (*optf)(Mesh& mesh))
{
	Mesh copy = mesh;

	clock_t start = clock();
	optf(copy);
	clock_t end = clock();

	VertexCacheStatistics vcs = analyzeVertexCache(&copy.indices[0], copy.indices.size(), copy.vertices.size(), kCacheSize);
	VertexCacheStatistics vc0s = analyzeVertexCache(&copy.indices[0], copy.indices.size(), copy.vertices.size(), 0);
	VertexFetchStatistics vfs = analyzeVertexFetch(&copy.indices[0], copy.indices.size(), copy.vertices.size(), sizeof(Vertex));
	OverdrawStatistics os = analyzeOverdraw(&copy.indices[0], copy.indices.size(), &copy.vertices[0].px, sizeof(Vertex), copy.vertices.size());

	printf("%-9s: ACMR %f ATVR %f (LRU %f) Overfetch %f Overdraw %f in %.2f msec\n", name, vcs.acmr, vcs.atvr, vc0s.atvr, vfs.overfetch, os.overdraw, double(end - start) / CLOCKS_PER_SEC * 1000);
}

void process(const char* path)
{
	Mesh mesh;

	if (path)
	{
		clock_t start = clock();
		clock_t middle;
		mesh = parseObj(path, middle);
		clock_t end = clock();

		if (mesh.vertices.empty())
		{
			printf("Mesh %s is empty, skipping\n", path);
			return;
		}

		printf("# %s: %d vertices, %d triangles; read in %.2f msec; indexed in %.2f msec\n", path, int(mesh.vertices.size()), int(mesh.indices.size() / 3), double(middle - start) / CLOCKS_PER_SEC * 1000, double(end - middle) / CLOCKS_PER_SEC * 1000);
	}
	else
	{
		mesh = generatePlane(1000);

		printf("# tessellated plane: %d vertices, %d triangles\n", int(mesh.vertices.size()), int(mesh.indices.size() / 3));
	}

	optimize(mesh, "Original", optNone);
	optimize(mesh, "Random", optRandomShuffle);
	optimize(mesh, "Cache", optCache);
	optimize(mesh, "Cache0", optCache0);
	optimize(mesh, "Overdraw", optOverdraw);
	optimize(mesh, "Fetch", optFetch);
	optimize(mesh, "Complete", optComplete);

	// note: the ATVR/overdraw output from this pass is not necessarily correct since we analyze all LODs at once
	optimize(mesh, "Simplify", optCompleteSimplify);
}

int main(int argc, char** argv)
{
	if (argc == 1)
	{
		printf("Usage: %s [.obj file]\n", argv[0]);
		process(0);
	}
	else
	{
		for (int i = 1; i < argc; ++i)
		{
			process(argv[i]);
		}
	}
}
<commit_msg>demo: Store coarse LODs before fine LODs in the resulting IB<commit_after>#include "../src/meshoptimizer.hpp"

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <ctime>

#include "objparser.hpp"

using namespace meshopt;

const size_t kCacheSize = 24;

struct Vertex
{
	float px, py, pz;
	float nx, ny, nz;
	float tx, ty;
};

struct Mesh
{
	std::vector<Vertex> vertices;
	std::vector<unsigned int> indices;
};

Mesh generatePlane(unsigned int N)
{
	Mesh result;

	result.vertices.reserve((N + 1) * (N + 1));
	result.indices.reserve(N * N * 6);

	for (unsigned int y = 0; y <= N; ++y)
		for (unsigned int x = 0; x <= N; ++x)
		{
			Vertex v = {float(x), float(y), 0, 0, 0, 1, float(x) / float(N), float(y) / float(N)};

			result.vertices.push_back(v);
		}

	for (unsigned int y = 0; y < N; ++y)
		for (unsigned int x = 0; x < N; ++x)
		{
			result.indices.push_back((y + 0) * (N + 1) + (x + 0));
			result.indices.push_back((y + 0) * (N + 1) + (x + 1));
			result.indices.push_back((y + 1) * (N + 1) + (x + 0));

			result.indices.push_back((y + 1) * (N + 1) + (x + 0));
			result.indices.push_back((y + 0) * (N + 1) + (x + 1));
			result.indices.push_back((y + 1) * (N + 1) + (x + 1));
		}

	return result;
}

Mesh parseObj(const char* path, clock_t& reindex)
{
	ObjFile file;

	if (!objParseFile(file, path))
	{
		printf("Error loading %s: file not found\n", path);
		return Mesh();
	}

	if (!objValidate(file))
	{
		printf("Error loading %s: invalid file data\n", path);
		return Mesh();
	}

	objTriangulate(file);

	size_t total_indices = file.f.size() / 3;

	std::vector<Vertex> vertices;
	vertices.reserve(total_indices);

	for (size_t i = 0; i < file.f.size(); i += 3)
	{
		int vi = file.f[i + 0];
		int vti = file.f[i + 1];
		int vni = file.f[i + 2];

		Vertex v =
		    {
		        file.v[vi * 3 + 0],
		        file.v[vi * 3 + 1],
		        file.v[vi * 3 + 2],

		        vni >= 0 ? file.vn[vni * 3 + 0] : 0,
		        vni >= 0 ? file.vn[vni * 3 + 1] : 0,
		        vni >= 0 ? file.vn[vni * 3 + 2] : 0,

		        vti >= 0 ? file.vt[vti * 3 + 0] : 0,
		        vti >= 0 ? file.vt[vti * 3 + 1] : 0,
		    };

		vertices.push_back(v);
	}

	reindex = clock();

	Mesh result;

	std::vector<unsigned int> remap(total_indices);

	size_t total_vertices = generateVertexRemap(&remap[0], NULL, total_indices, &vertices[0], total_indices, sizeof(Vertex));

	result.indices.resize(total_indices);
	remapIndexBuffer(&result.indices[0], NULL, total_indices, &remap[0]);

	result.vertices.resize(total_vertices);
	remapVertexBuffer(&result.vertices[0], &vertices[0], total_indices, sizeof(Vertex), &remap[0]);

	return result;
}

void optNone(Mesh& mesh)
{
	(void)mesh;
}

void optRandomShuffle(Mesh& mesh)
{
	std::vector<unsigned int> faces(mesh.indices.size() / 3);

	for (size_t i = 0; i < faces.size(); ++i)
		faces[i] = static_cast<unsigned int>(i);

	std::random_shuffle(faces.begin(), faces.end());

	std::vector<unsigned int> result(mesh.indices.size());

	for (size_t i = 0; i < faces.size(); ++i)
	{
		result[i * 3 + 0] = mesh.indices[faces[i] * 3 + 0];
		result[i * 3 + 1] = mesh.indices[faces[i] * 3 + 1];
		result[i * 3 + 2] = mesh.indices[faces[i] * 3 + 2];
	}

	mesh.indices.swap(result);
}

void optCache(Mesh& mesh)
{
	optimizeVertexCache(&mesh.indices[0], &mesh.indices[0], mesh.indices.size(), mesh.vertices.size(), kCacheSize);
}

void optCache0(Mesh& mesh)
{
	optimizeVertexCache(&mesh.indices[0], &mesh.indices[0], mesh.indices.size(), mesh.vertices.size(), 0);
}

void optOverdraw(Mesh& mesh)
{
	// use worst-case ACMR threshold so that overdraw optimizer can sort *all* triangles
	// warning: this significantly deteriorates the vertex cache efficiency so it is not advised; look at optComplete for the recommended method
	const float kThreshold = 3.f;
	optimizeOverdraw(&mesh.indices[0], &mesh.indices[0], mesh.indices.size(), &mesh.vertices[0].px, sizeof(Vertex), mesh.vertices.size(), kCacheSize, kThreshold);
}

void optFetch(Mesh& mesh)
{
	optimizeVertexFetch(&mesh.vertices[0], &mesh.vertices[0], &mesh.indices[0], mesh.indices.size(), mesh.vertices.size(), sizeof(Vertex));
}

void optComplete(Mesh& mesh)
{
	// vertex cache optimization should go first as it provides data for overdraw
	optimizeVertexCache(&mesh.indices[0], &mesh.indices[0], mesh.indices.size(), mesh.vertices.size(), kCacheSize);

	// reorder indices for overdraw, balancing overdraw and vertex cache efficiency
	const float kThreshold = 1.05f; // allow up to 5% worse ACMR to get more reordering opportunities for overdraw
	optimizeOverdraw(&mesh.indices[0], &mesh.indices[0], mesh.indices.size(), &mesh.vertices[0].px, sizeof(Vertex), mesh.vertices.size(), kCacheSize, kThreshold);

	// vertex fetch optimization should go last as it depends on the final index order
	optimizeVertexFetch(&mesh.vertices[0], &mesh.vertices[0], &mesh.indices[0], mesh.indices.size(), mesh.vertices.size(), sizeof(Vertex));
}

void optCompleteSimplify(Mesh& mesh)
{
	static const size_t lod_count = 5;

	// generate 4 LOD levels (1-4), with each subsequent LOD using 70% triangles
	// note that each LOD uses the same (shared) vertex buffer
	// vertex cache optimization should go first as it provides data for overdraw
	std::vector<unsigned int> lods[lod_count];

	lods[0] = mesh.indices;

	for (size_t i = 1; i < lod_count; ++i)
	{
		std::vector<unsigned int>& lod = lods[i];

		float threshold = powf(0.7f, float(i));
		size_t target_index_count = size_t(mesh.indices.size() * threshold);

		// we can simplify all the way from base level or from the last result
		// simplifying from the base level sometimes produces better results, but simplifying from last level is faster
		const std::vector<unsigned int>& source = lods[i - 1];

		lod.resize(source.size());
		lod.resize(simplify(&lod[0], &source[0], source.size(), &mesh.vertices[0], mesh.vertices.size(), sizeof(Vertex), target_index_count));
	}

	// optimize each individual LOD for vertex cache & overdraw
	for (size_t i = 0; i < lod_count; ++i)
	{
		std::vector<unsigned int>& lod = lods[i];

		optimizeVertexCache(&lod[0], &lod[0], lod.size(), mesh.vertices.size(), kCacheSize);
		optimizeOverdraw(&lod[0], &lod[0], lod.size(), &mesh.vertices[0].px, sizeof(Vertex), mesh.vertices.size(), kCacheSize, 1.0f);
	}

	// concatenate all LODs into one IB
	// note: the order of concatenation is important - since we optimize the entire IB for vertex fetch,
	// putting coarse LODs first makes sure that the vertex range referenced by them is as small as possible
	// some GPUs process the entire range referenced by the index buffer region so doing this optimizes the vertex transform
	// cost for coarse LODs
	size_t lod_index_offsets[lod_count] = {};
	size_t lod_index_counts[lod_count] = {};
	size_t total_index_count = 0;

	for (int i = lod_count - 1; i >= 0; --i)
	{
		lod_index_offsets[i] = total_index_count;
		lod_index_counts[i] = lods[i].size();

		total_index_count += lods[i].size();
	}

	mesh.indices.resize(total_index_count);

	for (size_t i = 0; i < lod_count; ++i)
	{
		memcpy(&mesh.indices[lod_index_offsets[i]], lods[i].data(), lods[i].size() * sizeof(lods[i][0]));
	}

	// vertex fetch optimization should go last as it depends on the final index order
	// note that the order of LODs above affects vertex fetch results
	optimizeVertexFetch(&mesh.vertices[0], &mesh.vertices[0], &mesh.indices[0], mesh.indices.size(), mesh.vertices.size(), sizeof(Vertex));

	printf("%-9s:", "Simplify");

	for (size_t i = 0; i < sizeof(lods) / sizeof(lods[0]); ++i)
	{
		printf(" LOD%d %d", int(i), int(lods[i].size()) / 3);
	}

	printf("\n");

	// for using LOD data at runtime, in addition to VB and IB you have to save lod_index_offsets/lod_index_counts.
	(void)lod_index_offsets;
	(void)lod_index_counts;
}

void optimize(const Mesh& mesh, const char* name, void (*optf)(Mesh& mesh))
{
	Mesh copy = mesh;

	clock_t start = clock();
	optf(copy);
	clock_t end = clock();

	VertexCacheStatistics vcs = analyzeVertexCache(&copy.indices[0], copy.indices.size(), copy.vertices.size(), kCacheSize);
	VertexCacheStatistics vc0s = analyzeVertexCache(&copy.indices[0], copy.indices.size(), copy.vertices.size(), 0);
	VertexFetchStatistics vfs = analyzeVertexFetch(&copy.indices[0], copy.indices.size(), copy.vertices.size(), sizeof(Vertex));
	OverdrawStatistics os = analyzeOverdraw(&copy.indices[0], copy.indices.size(), &copy.vertices[0].px, sizeof(Vertex), copy.vertices.size());

	printf("%-9s: ACMR %f ATVR %f (LRU %f) Overfetch %f Overdraw %f in %.2f msec\n", name, vcs.acmr, vcs.atvr, vc0s.atvr, vfs.overfetch, os.overdraw, double(end - start) / CLOCKS_PER_SEC * 1000);
}

void process(const char* path)
{
	Mesh mesh;

	if (path)
	{
		clock_t start = clock();
		clock_t middle;
		mesh = parseObj(path, middle);
		clock_t end = clock();

		if (mesh.vertices.empty())
		{
			printf("Mesh %s is empty, skipping\n", path);
			return;
		}

		printf("# %s: %d vertices, %d triangles; read in %.2f msec; indexed in %.2f msec\n", path, int(mesh.vertices.size()), int(mesh.indices.size() / 3), double(middle - start) / CLOCKS_PER_SEC * 1000, double(end - middle) / CLOCKS_PER_SEC * 1000);
	}
	else
	{
		mesh = generatePlane(1000);

		printf("# tessellated plane: %d vertices, %d triangles\n", int(mesh.vertices.size()), int(mesh.indices.size() / 3));
	}

	optimize(mesh, "Original", optNone);
	optimize(mesh, "Random", optRandomShuffle);
	optimize(mesh, "Cache", optCache);
	optimize(mesh, "Cache0", optCache0);
	optimize(mesh, "Overdraw", optOverdraw);
	optimize(mesh, "Fetch", optFetch);
	optimize(mesh, "Complete", optComplete);

	// note: the ATVR/overdraw output from this pass is not necessarily correct since we analyze all LODs at once
	optimize(mesh, "Simplify", optCompleteSimplify);
}

int main(int argc, char** argv)
{
	if (argc == 1)
	{
		printf("Usage: %s [.obj file]\n", argv[0]);
		process(0);
	}
	else
	{
		for (int i = 1; i < argc; ++i)
		{
			process(argv[i]);
		}
	}
}
<|endoftext|>


<commit_before>/*
** Copyright 2012-2013 Merethis
**
** This file is part of Centreon Broker.
**
** Centreon Broker is free software: you can redistribute it and/or
** modify it under the terms of the GNU General Public License version 2
** as published by the Free Software Foundation.
**
** Centreon Broker is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with Centreon Broker. If not, see
** <http://www.gnu.org/licenses/>.
*/

#include <cerrno>
#include <cstdlib>
#include <cstring>
#include <fcntl.h>
#include <iomanip>
#include <QMutexLocker>
#include <poll.h>
#include <sstream>
#include <unistd.h>
#include "com/centreon/broker/config/applier/endpoint.hh"
#include "com/centreon/broker/config/applier/modules.hh"
#include "com/centreon/broker/exceptions/msg.hh"
#include "com/centreon/broker/logging/logging.hh"
#include "com/centreon/broker/stats/worker.hh"

using namespace com::centreon::broker::stats;

/**************************************
*                                     *
*           Public Methods            *
*                                     *
**************************************/

/**
 *  Default constructor.
 */
worker::worker() : _fd(-1) {}

/**
 *  Destructor.
 */
worker::~worker() throw () {}

/**
 *  Set the exit flag.
 */
void worker::exit() {
  _should_exit = true;
  return ;
}

/**
 *  Run the statistics thread.
 *
 *  @param[in] fifo_file Path to the FIFO file.
 */
void worker::run(QString const& fifo_file) {
  // Close FD.
  _close();

  // Set FIFO file.
  _fifo = fifo_file;

  // Set exit flag.
  _should_exit = false;

  // Launch thread.
  start();

  return ;
}

/**************************************
*                                     *
*           Private Methods           *
*                                     *
**************************************/

/**
 *  Close FIFO fd.
 */
void worker::_close() {
  if (_fd >= 0) {
    close(_fd);
    _fd = -1;
  }
  return ;
}

/**
 *  Generate statistics.
 */
void worker::_generate_stats() {
  // Modules.
  config::applier::modules&
    mod_applier(config::applier::modules::instance());
  for (config::applier::modules::iterator
         it = mod_applier.begin(),
         end = mod_applier.end();
       it != end;
       ++it) {
    _buffer.append("module ");
    _buffer.append(it.key().toStdString());
    _buffer.append("\nstate=loaded\n");
    _buffer.append("\n");
  }

  // Endpoint applier.
  config::applier::endpoint&
    endp_applier(config::applier::endpoint::instance());

  // Print input endpoints.
  {
    QMutexLocker lock(&endp_applier.input_mutex());
    for (config::applier::endpoint::iterator
           it = endp_applier.input_begin(),
           end = endp_applier.input_end();
         it != end;
         ++it) {
      _generate_stats_for_endpoint(*it, _buffer, false);
      _buffer.append("\n");
    }
  }

  // Print output endpoints.
  {
    QMutexLocker lock(&endp_applier.output_mutex());
    for (config::applier::endpoint::iterator
           it = endp_applier.output_begin(),
           end = endp_applier.output_end();
         it != end;
         ++it) {
      _generate_stats_for_endpoint(*it, _buffer, true);
      _buffer.append("\n");
    }
  }

  return ;
}

/**
 *  Generate statistics for an endpoint.
 *
 *  @param[in]  fo     Failover thread of the endpoint.
 *  @param[out] buffer Buffer in which data will be printed.
 *  @param[in]  is_out true if fo manage an output endpoint.
 */
void worker::_generate_stats_for_endpoint(
               processing::failover* fo,
               std::string& buffer,
               bool is_out) {
  // Header.
  buffer.append(is_out ? "output " : "input ");
  buffer.append(fo->_name.toStdString());
  buffer.append("\n");

  // Choose stream we will work on.
  QReadWriteLock* first_rwl;
  misc::shared_ptr<io::stream>* first_s;
  QReadWriteLock* second_rwl;
  misc::shared_ptr<io::stream>* second_s;
  if (is_out) {
    first_rwl = &fo->_tom;
    first_s = &fo->_to;
    second_rwl = &fo->_fromm;
    second_s = &fo->_from;
  }
  else {
    first_rwl = &fo->_fromm;
    first_s = &fo->_from;
    second_rwl = &fo->_tom;
    second_s = &fo->_to;
  }

  {
    // Get primary state.
    buffer.append("state=");
    bool locked(false);
    for (unsigned int i(0); i < 10000; ++i) {
      if (first_rwl->tryLockForRead()) {
        locked = true;
        break ;
      }
      usleep(1);
    }
    try {
      // Could lock RWL.
      if (locked) {
        if (first_s->isNull()) {
          if (!fo->_last_error.isEmpty()) {
            buffer.append("disconnected");
            buffer.append(" (");
            buffer.append(fo->_last_error.toStdString());
            buffer.append(")\n");
          }
          else if (!fo->_endpoint.isNull()
                   && !fo->_endpoint->is_acceptor())
            buffer.append("connecting\n");
          else
            buffer.append("listening\n");
        }
        else if (!fo->_failover.isNull() && fo->_failover->isRunning()) {
          buffer.append("replaying\n");
          (*first_s)->statistics(buffer);
        }
        else {
          buffer.append("connected\n");
          (*first_s)->statistics(buffer);
        }
      }
      // Could not lock RWL.
      else
        buffer.append("blocked\n");
    }
    catch (...) {
      if (locked)
        first_rwl->unlock();
      throw ;
    }
    if (locked)
      first_rwl->unlock();
  }

  {
    // Get secondary state.
    QReadLocker rl(second_rwl);
    if (!second_s->isNull())
      (*second_s)->statistics(buffer);
  }

  {
    // Event processing stats.
    std::ostringstream oss;
    oss << "last event at=" << fo->get_last_event() << "\n"
        << "event processing speed=" << std::fixed
        << std::setprecision(1) << fo->get_event_processing_speed()
        << " events/s\n";
    buffer.append(oss.str());
  }

  // Endpoint stats.
  if (!fo->_endpoint.isNull())
    fo->_endpoint->stats(buffer);

  {
    // Last connection times.
    std::ostringstream oss;
    oss << "last connection attempt=" << fo->_last_connect_attempt
        << "\n" << "last connection success="
        << fo->_last_connect_success << "\n";
    buffer.append(oss.str());
  }

  // Failover.
  if (!fo->_failover.isNull() && fo->_failover->isRunning()) {
    buffer.append("failover\n");
    std::string subbuffer;
    _generate_stats_for_endpoint(
      fo->_failover.data(),
      subbuffer,
      is_out);
    subbuffer.insert(0, "  ");
    size_t pos(subbuffer.find('\n'));
    while ((pos != subbuffer.size() - 1)
           && (pos != std::string::npos)) {
      subbuffer.replace(pos, 1, "\n  ");
      pos = subbuffer.find('\n', pos + 3);
    }
    buffer.append(subbuffer);
  }

  return ;
}

/**
 *  Open FIFO.
 *
 *  @return true on success.
 */
bool worker::_open() {
  bool retval;
  _fd = open(qPrintable(_fifo), O_WRONLY | O_NONBLOCK);
  if (_fd < 0) {
    if (errno != ENXIO) {
      char const* msg(strerror(errno));
      throw (exceptions::msg() << "cannot open FIFO file: " << msg);
    }
    else
      retval = false;
  }
  else
    retval = true;
  return (retval);
}

/**
 *  Thread entry point.
 */
void worker::run() {
  try {
    while (!_should_exit) {
      // Check file opening.
      if (_buffer.empty()) {
        _close();
        usleep(100000);
        if (!_open())
          continue ;
      }

      // FD sets.
      pollfd fds;
      fds.fd = _fd;
      fds.events = POLLOUT;
      fds.revents = 0;

      // Multiplexing.
      int flagged(poll(&fds, 1, 1000));

      // Error.
      if (flagged < 0) {
        // Unrecoverable.
        if (errno != EINTR) {
          char const* msg(strerror(errno));
          throw (exceptions::msg() << "multiplexing failure: " << msg);
        }
      }
      else if (flagged > 0) {
        // FD error.
        if ((fds.revents & (POLLERR | POLLNVAL | POLLHUP)))
          throw (exceptions::msg() << "FIFO fd has pending error");
        // Readable.
        else if ((fds.revents & POLLOUT)) {
          if (_buffer.empty())
            // Generate statistics.
            _generate_stats();

          // Write data.
          ssize_t wb(write(_fd, _buffer.c_str(), _buffer.size()));
          if (wb > 0)
            _buffer.erase(0, wb);
          else
            _buffer.clear();
        }
      }
    }
  }
  catch (std::exception const& e) {
    logging::error(logging::high)
      << "stats: thread will exit due to the following error: "
      << e.what();
  }
  catch (...) {
    logging::error(logging::high)
      << "stats: thread will exit due to an unknown error";
  }
  ::unlink(qPrintable(_fifo));
  return ;
}
<commit_msg>stats: use try-locking with timeout instead of looping.<commit_after>/*
** Copyright 2012-2013 Merethis
**
** This file is part of Centreon Broker.
**
** Centreon Broker is free software: you can redistribute it and/or
** modify it under the terms of the GNU General Public License version 2
** as published by the Free Software Foundation.
**
** Centreon Broker is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with Centreon Broker. If not, see
** <http://www.gnu.org/licenses/>.
*/

#include <cerrno>
#include <cstdlib>
#include <cstring>
#include <fcntl.h>
#include <iomanip>
#include <QMutexLocker>
#include <poll.h>
#include <sstream>
#include <unistd.h>
#include "com/centreon/broker/config/applier/endpoint.hh"
#include "com/centreon/broker/config/applier/modules.hh"
#include "com/centreon/broker/exceptions/msg.hh"
#include "com/centreon/broker/logging/logging.hh"
#include "com/centreon/broker/stats/worker.hh"

using namespace com::centreon::broker::stats;

/**************************************
*                                     *
*           Public Methods            *
*                                     *
**************************************/

/**
 *  Default constructor.
 */
worker::worker() : _fd(-1) {}

/**
 *  Destructor.
 */
worker::~worker() throw () {}

/**
 *  Set the exit flag.
 */
void worker::exit() {
  _should_exit = true;
  return ;
}

/**
 *  Run the statistics thread.
 *
 *  @param[in] fifo_file Path to the FIFO file.
 */
void worker::run(QString const& fifo_file) {
  // Close FD.
  _close();

  // Set FIFO file.
  _fifo = fifo_file;

  // Set exit flag.
  _should_exit = false;

  // Launch thread.
  start();

  return ;
}

/**************************************
*                                     *
*           Private Methods           *
*                                     *
**************************************/

/**
 *  Close FIFO fd.
 */
void worker::_close() {
  if (_fd >= 0) {
    close(_fd);
    _fd = -1;
  }
  return ;
}

/**
 *  Generate statistics.
 */
void worker::_generate_stats() {
  // Modules.
  config::applier::modules&
    mod_applier(config::applier::modules::instance());
  for (config::applier::modules::iterator
         it = mod_applier.begin(),
         end = mod_applier.end();
       it != end;
       ++it) {
    _buffer.append("module ");
    _buffer.append(it.key().toStdString());
    _buffer.append("\nstate=loaded\n");
    _buffer.append("\n");
  }

  // Endpoint applier.
  config::applier::endpoint&
    endp_applier(config::applier::endpoint::instance());

  // Print input endpoints.
  {
    QMutexLocker lock(&endp_applier.input_mutex());
    for (config::applier::endpoint::iterator
           it = endp_applier.input_begin(),
           end = endp_applier.input_end();
         it != end;
         ++it) {
      _generate_stats_for_endpoint(*it, _buffer, false);
      _buffer.append("\n");
    }
  }

  // Print output endpoints.
  {
    QMutexLocker lock(&endp_applier.output_mutex());
    for (config::applier::endpoint::iterator
           it = endp_applier.output_begin(),
           end = endp_applier.output_end();
         it != end;
         ++it) {
      _generate_stats_for_endpoint(*it, _buffer, true);
      _buffer.append("\n");
    }
  }

  return ;
}

/**
 *  Generate statistics for an endpoint.
 *
 *  @param[in]  fo     Failover thread of the endpoint.
 *  @param[out] buffer Buffer in which data will be printed.
 *  @param[in]  is_out true if fo manage an output endpoint.
 */
void worker::_generate_stats_for_endpoint(
               processing::failover* fo,
               std::string& buffer,
               bool is_out) {
  // Header.
  buffer.append(is_out ? "output " : "input ");
  buffer.append(fo->_name.toStdString());
  buffer.append("\n");

  // Choose stream we will work on.
  QReadWriteLock* first_rwl;
  misc::shared_ptr<io::stream>* first_s;
  QReadWriteLock* second_rwl;
  misc::shared_ptr<io::stream>* second_s;
  if (is_out) {
    first_rwl = &fo->_tom;
    first_s = &fo->_to;
    second_rwl = &fo->_fromm;
    second_s = &fo->_from;
  }
  else {
    first_rwl = &fo->_fromm;
    first_s = &fo->_from;
    second_rwl = &fo->_tom;
    second_s = &fo->_to;
  }

  {
    // Get primary state.
    buffer.append("state=");
    bool locked(first_rwl->tryLockForRead(10));
    try {
      // Could lock RWL.
      if (locked) {
        if (first_s->isNull()) {
          if (!fo->_last_error.isEmpty()) {
            buffer.append("disconnected");
            buffer.append(" (");
            buffer.append(fo->_last_error.toStdString());
            buffer.append(")\n");
          }
          else if (!fo->_endpoint.isNull()
                   && !fo->_endpoint->is_acceptor())
            buffer.append("connecting\n");
          else
            buffer.append("listening\n");
        }
        else if (!fo->_failover.isNull() && fo->_failover->isRunning()) {
          buffer.append("replaying\n");
          (*first_s)->statistics(buffer);
        }
        else {
          buffer.append("connected\n");
          (*first_s)->statistics(buffer);
        }
      }
      // Could not lock RWL.
      else
        buffer.append("blocked\n");
    }
    catch (...) {
      if (locked)
        first_rwl->unlock();
      throw ;
    }
    if (locked)
      first_rwl->unlock();
  }

  {
    // Get secondary state.
    QReadLocker rl(second_rwl);
    if (!second_s->isNull())
      (*second_s)->statistics(buffer);
  }

  {
    // Event processing stats.
    std::ostringstream oss;
    oss << "last event at=" << fo->get_last_event() << "\n"
        << "event processing speed=" << std::fixed
        << std::setprecision(1) << fo->get_event_processing_speed()
        << " events/s\n";
    buffer.append(oss.str());
  }

  // Endpoint stats.
  if (!fo->_endpoint.isNull())
    fo->_endpoint->stats(buffer);

  {
    // Last connection times.
    std::ostringstream oss;
    oss << "last connection attempt=" << fo->_last_connect_attempt
        << "\n" << "last connection success="
        << fo->_last_connect_success << "\n";
    buffer.append(oss.str());
  }

  // Failover.
  if (!fo->_failover.isNull() && fo->_failover->isRunning()) {
    buffer.append("failover\n");
    std::string subbuffer;
    _generate_stats_for_endpoint(
      fo->_failover.data(),
      subbuffer,
      is_out);
    subbuffer.insert(0, "  ");
    size_t pos(subbuffer.find('\n'));
    while ((pos != subbuffer.size() - 1)
           && (pos != std::string::npos)) {
      subbuffer.replace(pos, 1, "\n  ");
      pos = subbuffer.find('\n', pos + 3);
    }
    buffer.append(subbuffer);
  }

  return ;
}

/**
 *  Open FIFO.
 *
 *  @return true on success.
 */
bool worker::_open() {
  bool retval;
  _fd = open(qPrintable(_fifo), O_WRONLY | O_NONBLOCK);
  if (_fd < 0) {
    if (errno != ENXIO) {
      char const* msg(strerror(errno));
      throw (exceptions::msg() << "cannot open FIFO file: " << msg);
    }
    else
      retval = false;
  }
  else
    retval = true;
  return (retval);
}

/**
 *  Thread entry point.
 */
void worker::run() {
  try {
    while (!_should_exit) {
      // Check file opening.
      if (_buffer.empty()) {
        _close();
        usleep(100000);
        if (!_open())
          continue ;
      }

      // FD sets.
      pollfd fds;
      fds.fd = _fd;
      fds.events = POLLOUT;
      fds.revents = 0;

      // Multiplexing.
      int flagged(poll(&fds, 1, 1000));

      // Error.
      if (flagged < 0) {
        // Unrecoverable.
        if (errno != EINTR) {
          char const* msg(strerror(errno));
          throw (exceptions::msg() << "multiplexing failure: " << msg);
        }
      }
      else if (flagged > 0) {
        // FD error.
        if ((fds.revents & (POLLERR | POLLNVAL | POLLHUP)))
          throw (exceptions::msg() << "FIFO fd has pending error");
        // Readable.
        else if ((fds.revents & POLLOUT)) {
          if (_buffer.empty())
            // Generate statistics.
            _generate_stats();

          // Write data.
          ssize_t wb(write(_fd, _buffer.c_str(), _buffer.size()));
          if (wb > 0)
            _buffer.erase(0, wb);
          else
            _buffer.clear();
        }
      }
    }
  }
  catch (std::exception const& e) {
    logging::error(logging::high)
      << "stats: thread will exit due to the following error: "
      << e.what();
  }
  catch (...) {
    logging::error(logging::high)
      << "stats: thread will exit due to an unknown error";
  }
  ::unlink(qPrintable(_fifo));
  return ;
}
<|endoftext|>


<commit_before>#ifndef _options_hpp_INCLUDED
#define _options_hpp_INCLUDED

/*------------------------------------------------------------------------*/

// The 'check' option has by default '0' in optimized compilation, but for
// debugging and testing we want to set it to '1', by default.  Setting
// 'check' to '1' for instance triggers saving all the original clauses for
// checking witnesses and also learned clauses if a solution is provided.

#ifndef NDEBUG
#define DEBUG 1
#else
#define DEBUG 0
#endif

/*------------------------------------------------------------------------*/

// Some of the 'OPTION' macros below should only be included if certain
// compile time options are enabled.  This has the effect, that for instance
// if 'LOGGING' is defined, and thus logging code is included, then also the
// 'log' option is defined.  Otherwise the 'log' option is not included.

#ifdef LOGGING
#define LOGOPT OPTION
#else
#define LOGOPT(ARGS...) /**/
#endif

#ifdef QUIET
#define QUTOPT(ARGS...) /**/
#else
#define QUTOPT OPTION
#endif

/*------------------------------------------------------------------------*/

// In order to add new option, simply add a new line below.

#define OPTIONS \
\
/*     NAME             TYPE, VAL, LO, HI, USAGE */ \
\
OPTION(arena,            int,    3, 0,  3, "1=clause,2=var,3=queue") \
OPTION(arenacompact,    bool,    1, 0,  1, "keep clauses compact") \
OPTION(arenasort,        int,    1, 0,  1, "sort clauses after arenaing") \
OPTION(binary,          bool,    1, 0,  1, "use binary proof format") \
OPTION(check,           bool,DEBUG, 0,  1, "save & check original CNF") \
OPTION(clim,             int,   -1, 0,1e9, "conflict limit (-1=none)") \
OPTION(compact,         bool,    1, 0,  1, "enable compactification") \
OPTION(compactint,       int,  1e3, 1,1e9, "compactification conflic tlimit") \
OPTION(compactlim,    double,  0.1, 0,  1, "inactive variable limit") \
OPTION(compactmin,       int,  100, 1,1e9, "inactive variable limit") \
OPTION(dlim,             int,   -1, 0,1e9, "decision limit (-1=none)") \
OPTION(elim,            bool,    1, 0,  1, "bounded variable elimination") \
OPTION(elimclslim,       int,  1e3, 0,1e9, "ignore clauses of this size") \
OPTION(eliminit,         int,  1e3, 0,1e9, "initial conflict limit") \
OPTION(elimint,          int,  1e4, 1,1e9, "initial conflict interval") \
OPTION(elimocclim,       int,  100, 0,1e9, "one sided occurrence limit") \
OPTION(elimroundsinit,   int,    5, 1,1e9, "initial number of rounds") \
OPTION(elimrounds,       int,    2, 1,1e9, "usual number of rounds") \
OPTION(emagluefast,   double, 3e-2, 0,  1, "alpha fast glue") \
OPTION(emaglueslow,   double, 1e-5, 0,  1, "alpha slow glue") \
OPTION(emajump,       double, 1e-5, 0,  1, "alpha jump level") \
OPTION(emasize,       double, 1e-5, 0,  1, "alpha learned clause size") \
OPTION(decompose,       bool,    1, 0,  1, "SCC decompose BIG and ELS") \
OPTION(decomposerounds,  int,    1, 1,1e9, "number of decompose rounds") \
OPTION(force,           bool,    0, 0,  1, "force to read broken header") \
OPTION(hbr,             bool,    1, 0,  1, "learn hyper binary clauses") \
OPTION(hbrsizelim,       int, 1e9, 3, 1e9, "max size HBR base clause") \
OPTION(keepglue,         int,    3, 1,1e9, "glue kept learned clauses") \
OPTION(keepsize,         int,    4, 2,1e9, "size kept learned clauses") \
OPTION(leak,            bool,    1, 0,  1, "leak solver memory") \
LOGOPT(log,             bool,    0, 0,  1, "enable logging") \
LOGOPT(logsort,         bool,    0, 0,  1, "sort logged clauses") \
OPTION(minimize,        bool,    1, 0,  1, "minimize learned clauses") \
OPTION(minimizedepth,    int,  1e3, 0,1e9, "minimization depth") \
OPTION(phase,            int,    1, 0,  1, "initial phase: 0=neg,1=pos") \
OPTION(posize,           int,    4, 4,1e9, "size for saving position") \
OPTION(prefetch,        bool,    1, 0,  1, "prefetch watches") \
OPTION(probe,           bool,    1, 0,  1, "failed literal probing" ) \
OPTION(probeinit,        int,  500, 0,1e9, "initial probing interval" ) \
OPTION(probeint,         int,  1e4, 1,1e9, "probing interval increment" ) \
OPTION(probereleff,   double, 0.02, 0,  1, "relative probing efficiency") \
OPTION(probemaxeff,   double,  1e7, 0,  1, "maximum probing efficiency") \
OPTION(probemineff,   double,  1e5, 0,  1, "minimum probing efficiency") \
OPTION(profile,          int,    2, 0,  4, "profiling level") \
QUTOPT(quiet,           bool,    0, 0,  1, "disable all messages") \
OPTION(reduceinc,        int,  300, 1,1e6, "reduce limit increment") \
OPTION(reduceinit,       int, 2000, 0,1e6, "initial reduce limit") \
OPTION(rephase,         bool,    1, 0,  1, "enable rephasing") \
OPTION(rephaseint,       int,  1e5, 1,1e9, "rephasing interval") \
OPTION(restart,         bool,    1, 0,  1, "enable restarting") \
OPTION(restartint,       int,    6, 1,1e9, "restart base interval") \
OPTION(restartmargin, double,  1.1, 0, 10, "restart slow fast margin") \
OPTION(reusetrail,      bool,    1, 0,  1, "enable trail reuse") \
OPTION(simplify,        bool,    1, 0,  1, "enable simplifier") \
OPTION(strengthen,      bool,    1, 0,  1, "strengthen during subsume") \
OPTION(subsume,         bool,    1, 0,  1, "enable clause subsumption") \
OPTION(subsumebinlim,    int,  1e4, 0,1e9, "watch list length limit") \
OPTION(subsumeclslim,    int,  1e3, 0,1e9, "clause length limit") \
OPTION(subsumeinc,       int,  1e4, 1,1e9, "interval in conflicts") \
OPTION(subsumeinit,      int,  1e4, 0,1e9, "initial subsume limit") \
OPTION(subsumeocclim,    int,  100, 0,1e9, "watch list length limit") \
OPTION(transred,        bool,    1, 0,  1, "transitive reduction of BIG") \
OPTION(transredreleff,double, 0.10, 0,  1, "relative efficiency") \
OPTION(transredmaxeff,double,  1e7, 0,  1, "maximum efficiency") \
OPTION(transredmineff,double,  1e5, 0,  1, "minimum efficiency") \
QUTOPT(verbose,         int,     0, 0,  2, "more verbose messages") \
OPTION(vivify,          bool,    1, 0,  1, "vivification") \
OPTION(vivifyreleff,  double, 0.03, 0,  1, "relative efficiency") \
OPTION(vivifymaxeff,  double,  1e7, 0,  1, "maximum efficiency") \
OPTION(vivifymineff,  double,  1e5, 0,  1, "minimum efficiency") \
OPTION(witness,         bool,    1, 0,  1, "print witness") \

/*------------------------------------------------------------------------*/

namespace CaDiCaL {

class Internal;

class Options {

  Internal * internal;

  bool set (   int &, const char *, const char *, const    int, const    int);
  bool set (  bool &, const char *, const char *, const   bool, const   bool);
  bool set (double &, const char *, const char *, const double, const double);

  const char * match (const char *, const char *);

public:

  // Makes options directly accessible, e.g., for instance declares the
  // member 'bool Options.restart' here.  This will give fast and type save
  // access to option values (internally).  In principle one could make all
  // options simply 'double' though, but that requires double conversions
  // during accessing options at run-time and disregards the intended types,
  // e.g., one would need to allow fractional values for actual integer
  // or boolean options.  Keeping the different types makes the output of
  // 'print' and 'usage' also more appealing (since correctly typed values
  // are printed).

#define OPTION(N,T,V,L,H,D) \
  T N;
  OPTIONS
#undef OPTION

  Options (Internal *);

  // This sets the value of an option assuming a 'long' command line
  // argument form.  The argument 'arg' thus should look like
  //
  //  "--<NAME>=<VAL>", "--<NAME>" or "--no-<NAME>"
  //
  // where 'NAME' is one of the option names above.  Returns 'true' if the
  // option was parsed and set correctly.  For boolean values we strictly
  // only allow "true", "false", "0" and "1" as "<VAL>" string.  For 'int'
  // type options we parse "<VAL>" with 'atoi' and force the resulting 'int'
  // value to the 'LO' and 'HI' range and similarly for 'double' type
  // options using 'atof'.  If the string is not a valid 'int' for 'int'
  // options or a 'double' value for 'double' options, then the function
  // returns 'false'.
  //
  bool set (const char * arg);

  // Interface to options using in a certain sense non-type-safe 'double'
  // values even for 'int' and 'bool'.  However, 'double' can hold a 'bool'
  // as well an 'int' value precisely, e.g., if the result of 'get' is cast
  // down again by the client.  This would only fail for 64 byte 'long',
  // which we currently do not support as option type.
  //
  bool has (const char * name);
  double get (const char * name);
  bool set (const char * name, double);

  void print ();             // print current values in command line form
  static void usage ();      // print usage message for all options
};

};

#endif
<commit_msg>keepsize back to 3<commit_after>#ifndef _options_hpp_INCLUDED
#define _options_hpp_INCLUDED

/*------------------------------------------------------------------------*/

// The 'check' option has by default '0' in optimized compilation, but for
// debugging and testing we want to set it to '1', by default.  Setting
// 'check' to '1' for instance triggers saving all the original clauses for
// checking witnesses and also learned clauses if a solution is provided.

#ifndef NDEBUG
#define DEBUG 1
#else
#define DEBUG 0
#endif

/*------------------------------------------------------------------------*/

// Some of the 'OPTION' macros below should only be included if certain
// compile time options are enabled.  This has the effect, that for instance
// if 'LOGGING' is defined, and thus logging code is included, then also the
// 'log' option is defined.  Otherwise the 'log' option is not included.

#ifdef LOGGING
#define LOGOPT OPTION
#else
#define LOGOPT(ARGS...) /**/
#endif

#ifdef QUIET
#define QUTOPT(ARGS...) /**/
#else
#define QUTOPT OPTION
#endif

/*------------------------------------------------------------------------*/

// In order to add new option, simply add a new line below.

#define OPTIONS \
\
/*     NAME             TYPE, VAL, LO, HI, USAGE */ \
\
OPTION(arena,            int,    3, 0,  3, "1=clause,2=var,3=queue") \
OPTION(arenacompact,    bool,    1, 0,  1, "keep clauses compact") \
OPTION(arenasort,        int,    1, 0,  1, "sort clauses after arenaing") \
OPTION(binary,          bool,    1, 0,  1, "use binary proof format") \
OPTION(check,           bool,DEBUG, 0,  1, "save & check original CNF") \
OPTION(clim,             int,   -1, 0,1e9, "conflict limit (-1=none)") \
OPTION(compact,         bool,    1, 0,  1, "enable compactification") \
OPTION(compactint,       int,  1e3, 1,1e9, "compactification conflic tlimit") \
OPTION(compactlim,    double,  0.1, 0,  1, "inactive variable limit") \
OPTION(compactmin,       int,  100, 1,1e9, "inactive variable limit") \
OPTION(dlim,             int,   -1, 0,1e9, "decision limit (-1=none)") \
OPTION(elim,            bool,    1, 0,  1, "bounded variable elimination") \
OPTION(elimclslim,       int,  1e3, 0,1e9, "ignore clauses of this size") \
OPTION(eliminit,         int,  1e3, 0,1e9, "initial conflict limit") \
OPTION(elimint,          int,  1e4, 1,1e9, "initial conflict interval") \
OPTION(elimocclim,       int,  100, 0,1e9, "one sided occurrence limit") \
OPTION(elimroundsinit,   int,    5, 1,1e9, "initial number of rounds") \
OPTION(elimrounds,       int,    2, 1,1e9, "usual number of rounds") \
OPTION(emagluefast,   double, 3e-2, 0,  1, "alpha fast glue") \
OPTION(emaglueslow,   double, 1e-5, 0,  1, "alpha slow glue") \
OPTION(emajump,       double, 1e-5, 0,  1, "alpha jump level") \
OPTION(emasize,       double, 1e-5, 0,  1, "alpha learned clause size") \
OPTION(decompose,       bool,    1, 0,  1, "SCC decompose BIG and ELS") \
OPTION(decomposerounds,  int,    1, 1,1e9, "number of decompose rounds") \
OPTION(force,           bool,    0, 0,  1, "force to read broken header") \
OPTION(hbr,             bool,    1, 0,  1, "learn hyper binary clauses") \
OPTION(hbrsizelim,       int, 1e9, 3, 1e9, "max size HBR base clause") \
OPTION(keepglue,         int,    3, 1,1e9, "glue kept learned clauses") \
OPTION(keepsize,         int,    3, 2,1e9, "size kept learned clauses") \
OPTION(leak,            bool,    1, 0,  1, "leak solver memory") \
LOGOPT(log,             bool,    0, 0,  1, "enable logging") \
LOGOPT(logsort,         bool,    0, 0,  1, "sort logged clauses") \
OPTION(minimize,        bool,    1, 0,  1, "minimize learned clauses") \
OPTION(minimizedepth,    int,  1e3, 0,1e9, "minimization depth") \
OPTION(phase,            int,    1, 0,  1, "initial phase: 0=neg,1=pos") \
OPTION(posize,           int,    4, 4,1e9, "size for saving position") \
OPTION(prefetch,        bool,    1, 0,  1, "prefetch watches") \
OPTION(probe,           bool,    1, 0,  1, "failed literal probing" ) \
OPTION(probeinit,        int,  500, 0,1e9, "initial probing interval" ) \
OPTION(probeint,         int,  1e4, 1,1e9, "probing interval increment" ) \
OPTION(probereleff,   double, 0.02, 0,  1, "relative probing efficiency") \
OPTION(probemaxeff,   double,  1e7, 0,  1, "maximum probing efficiency") \
OPTION(probemineff,   double,  1e5, 0,  1, "minimum probing efficiency") \
OPTION(profile,          int,    2, 0,  4, "profiling level") \
QUTOPT(quiet,           bool,    0, 0,  1, "disable all messages") \
OPTION(reduceinc,        int,  300, 1,1e6, "reduce limit increment") \
OPTION(reduceinit,       int, 2000, 0,1e6, "initial reduce limit") \
OPTION(rephase,         bool,    1, 0,  1, "enable rephasing") \
OPTION(rephaseint,       int,  1e5, 1,1e9, "rephasing interval") \
OPTION(restart,         bool,    1, 0,  1, "enable restarting") \
OPTION(restartint,       int,    6, 1,1e9, "restart base interval") \
OPTION(restartmargin, double,  1.1, 0, 10, "restart slow fast margin") \
OPTION(reusetrail,      bool,    1, 0,  1, "enable trail reuse") \
OPTION(simplify,        bool,    1, 0,  1, "enable simplifier") \
OPTION(strengthen,      bool,    1, 0,  1, "strengthen during subsume") \
OPTION(subsume,         bool,    1, 0,  1, "enable clause subsumption") \
OPTION(subsumebinlim,    int,  1e4, 0,1e9, "watch list length limit") \
OPTION(subsumeclslim,    int,  1e3, 0,1e9, "clause length limit") \
OPTION(subsumeinc,       int,  1e4, 1,1e9, "interval in conflicts") \
OPTION(subsumeinit,      int,  1e4, 0,1e9, "initial subsume limit") \
OPTION(subsumeocclim,    int,  100, 0,1e9, "watch list length limit") \
OPTION(transred,        bool,    1, 0,  1, "transitive reduction of BIG") \
OPTION(transredreleff,double, 0.10, 0,  1, "relative efficiency") \
OPTION(transredmaxeff,double,  1e7, 0,  1, "maximum efficiency") \
OPTION(transredmineff,double,  1e5, 0,  1, "minimum efficiency") \
QUTOPT(verbose,         int,     0, 0,  2, "more verbose messages") \
OPTION(vivify,          bool,    1, 0,  1, "vivification") \
OPTION(vivifyreleff,  double, 0.03, 0,  1, "relative efficiency") \
OPTION(vivifymaxeff,  double,  1e7, 0,  1, "maximum efficiency") \
OPTION(vivifymineff,  double,  1e5, 0,  1, "minimum efficiency") \
OPTION(witness,         bool,    1, 0,  1, "print witness") \

/*------------------------------------------------------------------------*/

namespace CaDiCaL {

class Internal;

class Options {

  Internal * internal;

  bool set (   int &, const char *, const char *, const    int, const    int);
  bool set (  bool &, const char *, const char *, const   bool, const   bool);
  bool set (double &, const char *, const char *, const double, const double);

  const char * match (const char *, const char *);

public:

  // Makes options directly accessible, e.g., for instance declares the
  // member 'bool Options.restart' here.  This will give fast and type save
  // access to option values (internally).  In principle one could make all
  // options simply 'double' though, but that requires double conversions
  // during accessing options at run-time and disregards the intended types,
  // e.g., one would need to allow fractional values for actual integer
  // or boolean options.  Keeping the different types makes the output of
  // 'print' and 'usage' also more appealing (since correctly typed values
  // are printed).

#define OPTION(N,T,V,L,H,D) \
  T N;
  OPTIONS
#undef OPTION

  Options (Internal *);

  // This sets the value of an option assuming a 'long' command line
  // argument form.  The argument 'arg' thus should look like
  //
  //  "--<NAME>=<VAL>", "--<NAME>" or "--no-<NAME>"
  //
  // where 'NAME' is one of the option names above.  Returns 'true' if the
  // option was parsed and set correctly.  For boolean values we strictly
  // only allow "true", "false", "0" and "1" as "<VAL>" string.  For 'int'
  // type options we parse "<VAL>" with 'atoi' and force the resulting 'int'
  // value to the 'LO' and 'HI' range and similarly for 'double' type
  // options using 'atof'.  If the string is not a valid 'int' for 'int'
  // options or a 'double' value for 'double' options, then the function
  // returns 'false'.
  //
  bool set (const char * arg);

  // Interface to options using in a certain sense non-type-safe 'double'
  // values even for 'int' and 'bool'.  However, 'double' can hold a 'bool'
  // as well an 'int' value precisely, e.g., if the result of 'get' is cast
  // down again by the client.  This would only fail for 64 byte 'long',
  // which we currently do not support as option type.
  //
  bool has (const char * name);
  double get (const char * name);
  bool set (const char * name, double);

  void print ();             // print current values in command line form
  static void usage ();      // print usage message for all options
};

};

#endif
<|endoftext|>


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

#include <folly/futures/Future.h>
#include <folly/portability/GTest.h>

using namespace folly;

TEST(NonCopyableLambda, basic) {
  Promise<int> promise;
  Future<int> future = promise.getFuture();

  Future<Unit>().then(std::bind(
      [](Promise<int>& promise) mutable { promise.setValue(123); },
      std::move(promise)));

  // The previous statement can be simplified in C++14:
  //  Future<Unit>().then([promise = std::move(promise)]() mutable {
  //    promise.setValue(123);
  //  });

  EXPECT_TRUE(future.isReady());
  EXPECT_EQ(future.get(), 123);
}

TEST(NonCopyableLambda, unique_ptr) {
  Promise<Unit> promise;
  auto int_ptr = folly::make_unique<int>(1);

  EXPECT_EQ(*int_ptr, 1);

  auto future = promise.getFuture().then(std::bind(
      [](std::unique_ptr<int>& int_ptr) mutable {
        ++*int_ptr;
        return std::move(int_ptr);
      },
      std::move(int_ptr)));

  // The previous statement can be simplified in C++14:
  //  auto future =
  //      promise.getFuture().then([int_ptr = std::move(int_ptr)]() mutable {
  //        ++*int_ptr;
  //        return std::move(int_ptr);
  //      });

  EXPECT_FALSE(future.isReady());
  promise.setValue();
  EXPECT_TRUE(future.isReady());
  EXPECT_EQ(*future.get(), 2);
}

TEST(NonCopyableLambda, Function) {
  Promise<int> promise;

  Function<int(int)> callback = [](int x) { return x + 1; };

  auto future = promise.getFuture().then(std::move(callback));
  EXPECT_THROW(callback(0), std::bad_function_call);

  EXPECT_FALSE(future.isReady());
  promise.setValue(100);
  EXPECT_TRUE(future.isReady());
  EXPECT_EQ(future.get(), 101);
}

TEST(NonCopyableLambda, FunctionConst) {
  Promise<int> promise;

  Function<int(int) const> callback = [](int x) { return x + 1; };

  auto future = promise.getFuture().then(std::move(callback));
  EXPECT_THROW(callback(0), std::bad_function_call);

  EXPECT_FALSE(future.isReady());
  promise.setValue(100);
  EXPECT_TRUE(future.isReady());
  EXPECT_EQ(future.get(), 101);
}
<commit_msg>folly/futures/test/NonCopyableLambdaTest.cpp: avoid shadowing warnings<commit_after>/*
 * Copyright 2016 Facebook, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <folly/futures/Future.h>
#include <folly/portability/GTest.h>

using namespace folly;

TEST(NonCopyableLambda, basic) {
  Promise<int> promise;
  Future<int> future = promise.getFuture();

  Future<Unit>().then(std::bind(
      [](Promise<int>& p2) mutable { p2.setValue(123); },
      std::move(promise)));

  // The previous statement can be simplified in C++14:
  //  Future<Unit>().then([promise = std::move(promise)]() mutable {
  //    promise.setValue(123);
  //  });

  EXPECT_TRUE(future.isReady());
  EXPECT_EQ(future.get(), 123);
}

TEST(NonCopyableLambda, unique_ptr) {
  Promise<Unit> promise;
  auto int_ptr = folly::make_unique<int>(1);

  EXPECT_EQ(*int_ptr, 1);

  auto future = promise.getFuture().then(std::bind(
      [](std::unique_ptr<int>& p) mutable {
        ++*p;
        return std::move(p);
      },
      std::move(int_ptr)));

  // The previous statement can be simplified in C++14:
  //  auto future =
  //      promise.getFuture().then([int_ptr = std::move(int_ptr)]() mutable {
  //        ++*int_ptr;
  //        return std::move(int_ptr);
  //      });

  EXPECT_FALSE(future.isReady());
  promise.setValue();
  EXPECT_TRUE(future.isReady());
  EXPECT_EQ(*future.get(), 2);
}

TEST(NonCopyableLambda, Function) {
  Promise<int> promise;

  Function<int(int)> callback = [](int x) { return x + 1; };

  auto future = promise.getFuture().then(std::move(callback));
  EXPECT_THROW(callback(0), std::bad_function_call);

  EXPECT_FALSE(future.isReady());
  promise.setValue(100);
  EXPECT_TRUE(future.isReady());
  EXPECT_EQ(future.get(), 101);
}

TEST(NonCopyableLambda, FunctionConst) {
  Promise<int> promise;

  Function<int(int) const> callback = [](int x) { return x + 1; };

  auto future = promise.getFuture().then(std::move(callback));
  EXPECT_THROW(callback(0), std::bad_function_call);

  EXPECT_FALSE(future.isReady());
  promise.setValue(100);
  EXPECT_TRUE(future.isReady());
  EXPECT_EQ(future.get(), 101);
}
<|endoftext|>


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

#include <thrift/conformance/data/TestGenerator.h>

#include <boost/mp11.hpp>
#include <fmt/core.h>

#include <thrift/conformance/cpp2/Object.h>
#include <thrift/conformance/data/ValueGenerator.h>
#include <thrift/lib/cpp2/type/Name.h>
#include <thrift/lib/cpp2/type/Tag.h>
#include <thrift/lib/cpp2/type/ThriftType.h>
#include <thrift/test/testset/Testset.h>

namespace mp11 = boost::mp11;

namespace apache::thrift::conformance::data {

namespace {

template <typename TT>
Test createRoundTripTest(
    const AnyRegistry& registry, const Protocol& protocol) {
  using namespace apache::thrift::test::testset::detail;

  Test test;
  test.name() = protocol.name();
  test.tags()->emplace(getSpecDefinitionTag<TT>());
  for (const auto& value : ValueGenerator<TT>::getInterestingValues()) {
    // Test case #1: Use ValueStruct
    RoundTripTestCase roundTrip;
    roundTrip.request()->value() =
        registry.store(asValueStruct<TT>(value.value), protocol);

    auto& testCase1 = test.testCases()->emplace_back();
    testCase1.name() = fmt::format("{}/{}", type::getName<TT>(), value.name);
    testCase1.test()->set_roundTrip(roundTrip);

    // Test case #2: Use Testset
    typename struct_ByFieldType<TT, mod_set<>>::type data;
    data.field_1() = value.value;
    roundTrip.request()->value() = registry.store(data, protocol);
    auto& testCase2 = test.testCases()->emplace_back();
    testCase2.name() =
        fmt::format("testset.{}/{}", type::getName<TT>(), value.name);
    testCase2.test()->set_roundTrip(roundTrip);
  }

  return test;
}

void addRoundTripToSuite(
    const AnyRegistry& registry, const Protocol& protocol, TestSuite& suite) {
  mp11::mp_for_each<detail::PrimaryTypeTags>([&](auto tt) {
    suite.tests()->emplace_back(
        createRoundTripTest<decltype(tt)>(registry, protocol));
  });
}

} // namespace

TestSuite createRoundTripSuite(
    const std::set<Protocol>& protocols, const AnyRegistry& registry) {
  TestSuite suite;
  suite.name() = "RoundTripTest";
  for (const auto& protocol : protocols) {
    addRoundTripToSuite(registry, protocol, suite);
  }
  return suite;
}

} // namespace apache::thrift::conformance::data
<commit_msg>Refactor round-trip test<commit_after>/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <thrift/conformance/data/TestGenerator.h>

#include <boost/mp11.hpp>
#include <fmt/core.h>

#include <thrift/conformance/cpp2/Object.h>
#include <thrift/conformance/data/ValueGenerator.h>
#include <thrift/lib/cpp2/type/Name.h>
#include <thrift/lib/cpp2/type/Tag.h>
#include <thrift/lib/cpp2/type/ThriftType.h>
#include <thrift/test/testset/Testset.h>

namespace mp11 = boost::mp11;
using apache::thrift::test::testset::FieldModifier;
using apache::thrift::test::testset::detail::mod_set;

namespace apache::thrift::conformance::data {

namespace {

template <typename TT>
Test createRoundTripTest(
    const AnyRegistry& registry, const Protocol& protocol) {
  using namespace apache::thrift::test::testset::detail;

  Test test;
  test.name() = protocol.name();
  test.tags()->emplace(getSpecDefinitionTag<TT>());
  for (const auto& value : ValueGenerator<TT>::getInterestingValues()) {
    RoundTripTestCase roundTrip;
    {
      // Test case #1: Use ValueStruct
      roundTrip.request()->value() =
          registry.store(asValueStruct<TT>(value.value), protocol);

      auto& testCase = test.testCases()->emplace_back();
      testCase.name() = fmt::format("{}/{}", type::getName<TT>(), value.name);
      testCase.test()->roundTrip_ref() = roundTrip;
    }

    auto add = [&](auto modSet, auto msg) {
      using ModSet = decltype(modSet);
      typename struct_ByFieldType<TT, ModSet>::type data;
      data.field_1() = value.value;
      roundTrip.request()->value() = registry.store(data, protocol);
      auto& testCase = test.testCases()->emplace_back();
      testCase.name() =
          fmt::format("testset.{}{}/{}", msg, type::getName<TT>(), value.name);
      testCase.test()->roundTrip_ref() = roundTrip;
    };

    // Test case #2: Unqualified field
    add(mod_set<>{}, "");
  }

  return test;
}

void addRoundTripToSuite(
    const AnyRegistry& registry, const Protocol& protocol, TestSuite& suite) {
  mp11::mp_for_each<detail::PrimaryTypeTags>([&](auto tt) {
    suite.tests()->emplace_back(
        createRoundTripTest<decltype(tt)>(registry, protocol));
  });
}

} // namespace

TestSuite createRoundTripSuite(
    const std::set<Protocol>& protocols, const AnyRegistry& registry) {
  TestSuite suite;
  suite.name() = "RoundTripTest";
  for (const auto& protocol : protocols) {
    addRoundTripToSuite(registry, protocol, suite);
  }
  return suite;
}

} // namespace apache::thrift::conformance::data
<|endoftext|>


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

#include <folly/wangle/futures/Timekeeper.h>
#include <unistd.h>

using namespace folly::wangle;
using namespace std::chrono;
using folly::wangle::Timekeeper;
using Duration = folly::wangle::Duration;

std::chrono::milliseconds const one_ms(1);
std::chrono::milliseconds const awhile(10);

std::chrono::steady_clock::time_point now() {
  return std::chrono::steady_clock::now();
}

struct TimekeeperFixture : public testing::Test {
  TimekeeperFixture() :
    timeLord_(folly::wangle::detail::getTimekeeperSingleton())
  {}

  Timekeeper* timeLord_;
};

TEST_F(TimekeeperFixture, after) {
  Duration waited(0);

  auto t1 = now();
  auto f = timeLord_->after(awhile);
  EXPECT_FALSE(f.isReady());
  f.get();
  auto t2 = now();

  EXPECT_GE(t2 - t1, awhile);
}

TEST(Timekeeper, futureGet) {
  Promise<int> p;
  std::thread([&]{ p.setValue(42); }).detach();
  EXPECT_EQ(42, p.getFuture().get());
}

TEST(Timekeeper, futureGetBeforeTimeout) {
  Promise<int> p;
  std::thread([&]{ p.setValue(42); }).detach();
  // Technically this is a race and if the test server is REALLY overloaded
  // and it takes more than a second to do that thread it could be flaky. But
  // I want a low timeout (in human terms) so if this regresses and someone
  // runs it by hand they're not sitting there forever wondering why it's
  // blocked, and get a useful error message instead. If it does get flaky,
  // empirically increase the timeout to the point where it's very improbable.
  EXPECT_EQ(42, p.getFuture().get(seconds(2)));
}

TEST(Timekeeper, futureGetTimeout) {
  Promise<int> p;
  EXPECT_THROW(p.getFuture().get(Duration(1)), folly::wangle::TimedOut);
}

TEST(Timekeeper, futureSleep) {
  auto t1 = now();
  futures::sleep(one_ms).get();
  EXPECT_GE(now() - t1, one_ms);
}

TEST(Timekeeper, futureDelayed) {
  auto t1 = now();
  auto dur = makeFuture()
    .delayed(one_ms)
    .then([=]{ return now() - t1; })
    .get();

  EXPECT_GE(dur, one_ms);
}

TEST(Timekeeper, futureWithinThrows) {
  Promise<int> p;
  auto f = p.getFuture()
    .within(one_ms)
    .onError([](TimedOut&) { return -1; });

  EXPECT_EQ(-1, f.get());
}

TEST(Timekeeper, futureWithinAlreadyComplete) {
  auto f = makeFuture(42)
    .within(one_ms)
    .onError([&](TimedOut&){ return -1; });

  EXPECT_EQ(42, f.get());
}

TEST(Timekeeper, futureWithinFinishesInTime) {
  Promise<int> p;
  auto f = p.getFuture()
    .within(std::chrono::minutes(1))
    .onError([&](TimedOut&){ return -1; });
  p.setValue(42);

  EXPECT_EQ(42, f.get());
}

TEST(Timekeeper, futureWithinVoidSpecialization) {
  makeFuture().within(one_ms);
}

TEST(Timekeeper, futureWithinException) {
  Promise<void> p;
  auto f = p.getFuture().within(awhile, std::runtime_error("expected"));
  EXPECT_THROW(f.get(), std::runtime_error);
}
<commit_msg>join<commit_after>/*
 * Copyright 2014 Facebook, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <gtest/gtest.h>

#include <folly/wangle/futures/Timekeeper.h>
#include <unistd.h>

using namespace folly::wangle;
using namespace std::chrono;
using folly::wangle::Timekeeper;
using Duration = folly::wangle::Duration;

std::chrono::milliseconds const one_ms(1);
std::chrono::milliseconds const awhile(10);

std::chrono::steady_clock::time_point now() {
  return std::chrono::steady_clock::now();
}

struct TimekeeperFixture : public testing::Test {
  TimekeeperFixture() :
    timeLord_(folly::wangle::detail::getTimekeeperSingleton())
  {}

  Timekeeper* timeLord_;
};

TEST_F(TimekeeperFixture, after) {
  Duration waited(0);

  auto t1 = now();
  auto f = timeLord_->after(awhile);
  EXPECT_FALSE(f.isReady());
  f.get();
  auto t2 = now();

  EXPECT_GE(t2 - t1, awhile);
}

TEST(Timekeeper, futureGet) {
  Promise<int> p;
  std::thread([&]{ p.setValue(42); }).detach();
  EXPECT_EQ(42, p.getFuture().get());
}

TEST(Timekeeper, futureGetBeforeTimeout) {
  Promise<int> p;
  auto t = std::thread([&]{ p.setValue(42); });
  // Technically this is a race and if the test server is REALLY overloaded
  // and it takes more than a second to do that thread it could be flaky. But
  // I want a low timeout (in human terms) so if this regresses and someone
  // runs it by hand they're not sitting there forever wondering why it's
  // blocked, and get a useful error message instead. If it does get flaky,
  // empirically increase the timeout to the point where it's very improbable.
  EXPECT_EQ(42, p.getFuture().get(seconds(2)));
  t.join();
}

TEST(Timekeeper, futureGetTimeout) {
  Promise<int> p;
  EXPECT_THROW(p.getFuture().get(Duration(1)), folly::wangle::TimedOut);
}

TEST(Timekeeper, futureSleep) {
  auto t1 = now();
  futures::sleep(one_ms).get();
  EXPECT_GE(now() - t1, one_ms);
}

TEST(Timekeeper, futureDelayed) {
  auto t1 = now();
  auto dur = makeFuture()
    .delayed(one_ms)
    .then([=]{ return now() - t1; })
    .get();

  EXPECT_GE(dur, one_ms);
}

TEST(Timekeeper, futureWithinThrows) {
  Promise<int> p;
  auto f = p.getFuture()
    .within(one_ms)
    .onError([](TimedOut&) { return -1; });

  EXPECT_EQ(-1, f.get());
}

TEST(Timekeeper, futureWithinAlreadyComplete) {
  auto f = makeFuture(42)
    .within(one_ms)
    .onError([&](TimedOut&){ return -1; });

  EXPECT_EQ(42, f.get());
}

TEST(Timekeeper, futureWithinFinishesInTime) {
  Promise<int> p;
  auto f = p.getFuture()
    .within(std::chrono::minutes(1))
    .onError([&](TimedOut&){ return -1; });
  p.setValue(42);

  EXPECT_EQ(42, f.get());
}

TEST(Timekeeper, futureWithinVoidSpecialization) {
  makeFuture().within(one_ms);
}

TEST(Timekeeper, futureWithinException) {
  Promise<void> p;
  auto f = p.getFuture().within(awhile, std::runtime_error("expected"));
  EXPECT_THROW(f.get(), std::runtime_error);
}
<|endoftext|>