<commit_before>#include "setupdialog.h"
#include "debug.h"
#include "ui_setupdialog.h"
#include "utils.h"

#include <QDateTime>
#include <QDir>
#include <QFileDialog>
#include <QFileInfo>
#include <QHostInfo>
#include <QSettings>
#include <QStandardPaths>

SetupDialog::SetupDialog(QWidget *parent)
    : QDialog(parent),
      _ui(new Ui::SetupDialog),
      _loadingAnimation(":/icons/loading.gif"),
      _haveKey(false)
{
    _ui->setupUi(this);
    setWindowFlags((windowFlags() | Qt::CustomizeWindowHint) &
                   ~Qt::WindowMaximizeButtonHint);

    _ui->loadingIconLabel->setMovie(&_loadingAnimation);
    _ui->errorLabel->hide();
    _ui->machineKeyLabel->hide();
    _ui->machineKeyCombo->hide();
    _ui->browseKeyButton->hide();
    _ui->advancedCLIWidget->hide();

    connect(_ui->welcomePageRadioButton, &QRadioButton::clicked, this,
            &SetupDialog::skipToPage);
    connect(_ui->restorePageRadioButton, &QRadioButton::clicked, this,
            &SetupDialog::skipToPage);
    connect(_ui->advancedPageRadioButton, &QRadioButton::clicked, this,
            &SetupDialog::skipToPage);
    connect(_ui->registerPageRadioButton, &QRadioButton::clicked, this,
            &SetupDialog::skipToPage);
    connect(_ui->donePageRadioButton, &QRadioButton::clicked, this,
            &SetupDialog::skipToPage);

    connect(_ui->wizardStackedWidget, &QStackedWidget::currentChanged, this,
            &SetupDialog::wizardPageChanged);

    // Welcome page
    connect(_ui->welcomePageSkipButton, &QPushButton::clicked,
            [&]() { commitSettings(true); });
    connect(_ui->welcomePageProceedButton, &QPushButton::clicked, this,
            &SetupDialog::setNextPage);

    // Advanced setup page
    connect(_ui->advancedCLIButton, &QPushButton::toggled,
            _ui->advancedCLIWidget, &QWidget::setVisible);
    connect(_ui->tarsnapPathBrowseButton, &QPushButton::clicked, this,
            &SetupDialog::showTarsnapPathBrowse);
    connect(_ui->tarsnapPathLineEdit, &QLineEdit::textChanged, this,
            &SetupDialog::validateAdvancedSetupPage);
    connect(_ui->tarsnapCacheBrowseButton, &QPushButton::clicked, this,
            &SetupDialog::showTarsnapCacheBrowse);
    connect(_ui->tarsnapCacheLineEdit, &QLineEdit::textChanged, this,
            &SetupDialog::validateAdvancedSetupPage);
    connect(_ui->appDataBrowseButton, &QPushButton::clicked, this,
            &SetupDialog::showAppDataBrowse);
    connect(_ui->appDataPathLineEdit, &QLineEdit::textChanged, this,
            &SetupDialog::validateAdvancedSetupPage);
    connect(_ui->advancedPageProceedButton, &QPushButton::clicked, this,
            &SetupDialog::setNextPage);

    // Restore page
    connect(_ui->restoreNoButton, &QPushButton::clicked, this,
            &SetupDialog::restoreNo);
    connect(_ui->restoreYesButton, &QPushButton::clicked, this,
            &SetupDialog::restoreYes);

    // Register page
    connect(_ui->tarsnapUserLineEdit, &QLineEdit::textChanged, this,
            &SetupDialog::validateRegisterPage);
    connect(_ui->tarsnapPasswordLineEdit, &QLineEdit::textChanged, this,
            &SetupDialog::validateRegisterPage);
    connect(_ui->machineNameLineEdit, &QLineEdit::textChanged, this,
            &SetupDialog::validateRegisterPage);
    connect(_ui->machineKeyCombo, &QComboBox::currentTextChanged, this,
            &SetupDialog::validateRegisterPage);
    connect(_ui->browseKeyButton, &QPushButton::clicked, this,
            &SetupDialog::registerHaveKeyBrowse);
    connect(_ui->registerMachineButton, &QPushButton::clicked, this,
            &SetupDialog::registerMachine);

    // Done page
    connect(_ui->doneButton, &QPushButton::clicked, this,
            &SetupDialog::commitSettings);

    _appDataDir = QStandardPaths::writableLocation(APPDATA);
    QDir keysDir(_appDataDir);
    if(!keysDir.exists())
        keysDir.mkpath(_appDataDir);
    _ui->appDataPathLineEdit->setText(_appDataDir);

    _tarsnapCacheDir =
        QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
    QDir cacheDir(_tarsnapCacheDir);
    if(!cacheDir.exists())
        cacheDir.mkpath(_tarsnapCacheDir);
    _ui->tarsnapCacheLineEdit->setText(_tarsnapCacheDir);

    _tarsnapDir = Utils::findTarsnapClientInPath("", true);
    _ui->tarsnapPathLineEdit->setText(_tarsnapDir);
    _ui->machineNameLineEdit->setText(QHostInfo::localHostName());
    _ui->wizardStackedWidget->setCurrentWidget(_ui->welcomePage);
}

SetupDialog::~SetupDialog()
{
    delete _ui;
}

void SetupDialog::wizardPageChanged(int)
{
    if(_ui->wizardStackedWidget->currentWidget() == _ui->welcomePage)
    {
        _ui->welcomePageRadioButton->setChecked(true);
        _ui->titleLabel->setText(tr("Setup wizard"));
    }
    else if(_ui->wizardStackedWidget->currentWidget() == _ui->advancedPage)
    {
        _ui->advancedPageRadioButton->setChecked(true);
        _ui->titleLabel->setText(tr("Command-line utilities"));
    }
    else if(_ui->wizardStackedWidget->currentWidget() == _ui->restorePage)
    {
        _ui->restorePageRadioButton->setChecked(true);
        _ui->titleLabel->setText(tr("Machine key"));
    }
    else if(_ui->wizardStackedWidget->currentWidget() == _ui->registerPage)
    {
        _ui->registerPageRadioButton->setChecked(true);
        _ui->titleLabel->setText(tr("Register with server"));
    }
    else if(_ui->wizardStackedWidget->currentWidget() == _ui->donePage)
    {
        _ui->donePageRadioButton->setChecked(true);
        _ui->titleLabel->setText(tr("Setup complete!"));
    }
}

void SetupDialog::skipToPage()
{
    if(sender() == _ui->welcomePageRadioButton)
        _ui->wizardStackedWidget->setCurrentWidget(_ui->welcomePage);
    else if(sender() == _ui->restorePageRadioButton)
        _ui->wizardStackedWidget->setCurrentWidget(_ui->restorePage);
    else if(sender() == _ui->advancedPageRadioButton)
        _ui->wizardStackedWidget->setCurrentWidget(_ui->advancedPage);
    else if(sender() == _ui->registerPageRadioButton)
        _ui->wizardStackedWidget->setCurrentWidget(_ui->registerPage);
    else if(sender() == _ui->donePageRadioButton)
        _ui->wizardStackedWidget->setCurrentWidget(_ui->donePage);
}

void SetupDialog::setNextPage()
{
    if(_ui->wizardStackedWidget->currentWidget() == _ui->welcomePage)
    {
        _ui->wizardStackedWidget->setCurrentWidget(_ui->advancedPage);
        _ui->advancedPageRadioButton->setEnabled(true);
        validateAdvancedSetupPage();
    }
    else if(_ui->wizardStackedWidget->currentWidget() == _ui->advancedPage)
    {
        _ui->wizardStackedWidget->setCurrentWidget(_ui->restorePage);
        _ui->restorePageRadioButton->setEnabled(true);
    }
    else if(_ui->wizardStackedWidget->currentWidget() == _ui->restorePage)
    {
        _ui->wizardStackedWidget->setCurrentWidget(_ui->registerPage);
        _ui->registerPageRadioButton->setEnabled(true);
    }
    else if(_ui->wizardStackedWidget->currentWidget() == _ui->registerPage)
    {
        _ui->wizardStackedWidget->setCurrentWidget(_ui->donePage);
        _ui->donePageRadioButton->setEnabled(true);
    }
}

void SetupDialog::showTarsnapPathBrowse()
{
    QString tarsnapPath =
        QFileDialog::getExistingDirectory(this, tr("Find Tarsnap client"), "");
    _ui->tarsnapPathLineEdit->setText(tarsnapPath);
}

void SetupDialog::showTarsnapCacheBrowse()
{
    QString tarsnapCacheDir =
        QFileDialog::getExistingDirectory(this, tr("Tarsnap cache location"),
                                          _tarsnapCacheDir);
    _ui->tarsnapCacheLineEdit->setText(tarsnapCacheDir);
}

void SetupDialog::showAppDataBrowse()
{
    QString appDataDir =
        QFileDialog::getExistingDirectory(this, tr("App data location"), "");
    _ui->appDataPathLineEdit->setText(appDataDir);
}

bool SetupDialog::validateAdvancedSetupPage()
{
    bool result = false;

    setTarsnapVersion("");

    _tarsnapDir =
        Utils::findTarsnapClientInPath(_ui->tarsnapPathLineEdit->text(), true);
    _tarsnapCacheDir =
        Utils::validateTarsnapCache(_ui->tarsnapCacheLineEdit->text());
    QFileInfo appDataDir(_ui->appDataPathLineEdit->text());
    if(appDataDir.exists() && appDataDir.isDir() && appDataDir.isWritable())
        _appDataDir = _ui->appDataPathLineEdit->text();
    else
        _appDataDir.clear();

    if(_tarsnapDir.isEmpty() || _tarsnapCacheDir.isEmpty() ||
       _appDataDir.isEmpty())
        result = false;
    else
        result = true;

    if(result)
        emit getTarsnapVersion(_tarsnapDir);

    _ui->advancedPageProceedButton->setEnabled(result);

    return result;
}

void SetupDialog::restoreNo()
{
    _haveKey = false;
    _ui->machineKeyLabel->hide();
    _ui->machineKeyCombo->hide();
    _ui->browseKeyButton->hide();
    _ui->tarsnapUserLabel->show();
    _ui->tarsnapUserLineEdit->show();
    _ui->tarsnapPasswordLabel->show();
    _ui->tarsnapPasswordLineEdit->show();
    _ui->registerPageInfoLabel->setText(
        tr("Please use your Tarsnap account "
           "credentials to register this machine with the service "
           "and create a local key for this machine."));
    _ui->registerPageInfoLabelAside->setText(
        tr("Don't have an account? Register on "
        "tarsnap.com, "
        "then come back."));
    _ui->errorLabel->clear();
    setNextPage();
}

void SetupDialog::restoreYes()
{
    _haveKey = true;
    _ui->tarsnapUserLabel->hide();
    _ui->tarsnapUserLineEdit->hide();
    _ui->tarsnapPasswordLabel->hide();
    _ui->tarsnapPasswordLineEdit->hide();
    _ui->machineKeyLabel->show();
    _ui->machineKeyCombo->show();
    _ui->browseKeyButton->show();
    _ui->registerPageInfoLabel->setText(
        tr("Please use your existing machine key "
           "and choose a machine name. "));
    _ui->registerPageInfoLabelAside->setText(
        tr("The registration will also "
           "verify the archive integrity and consistency, "
           "so please be patient."));
    _ui->errorLabel->clear();
    _ui->machineKeyCombo->clear();
    foreach(QFileInfo file, Utils::findKeysInPath(_appDataDir))
        _ui->machineKeyCombo->addItem(file.canonicalFilePath());
    setNextPage();
}

void SetupDialog::validateRegisterPage()
{
    bool result = false;
    if(_haveKey)
    {
        // user specified key
        QFileInfo machineKeyFile(_ui->machineKeyCombo->currentText());
        if(!_ui->machineNameLineEdit->text().isEmpty() &&
           machineKeyFile.exists() && machineKeyFile.isFile() &&
           machineKeyFile.isReadable())
        {
            result = true;
        }
    }
    else
    {
        if(!_ui->tarsnapUserLineEdit->text().isEmpty() &&
           !_ui->tarsnapPasswordLineEdit->text().isEmpty() &&
           !_ui->machineNameLineEdit->text().isEmpty())
        {
            result = true;
        }
    }

    _ui->registerMachineButton->setEnabled(result);
}

void SetupDialog::registerHaveKeyBrowse()
{
    QString existingMachineKey =
        QFileDialog::getOpenFileName(this,
                                     tr("Browse for existing machine key"));
    if(!existingMachineKey.isEmpty())
        _ui->machineKeyCombo->setCurrentText(existingMachineKey);
}

void SetupDialog::registerMachine()
{
    _ui->registerMachineButton->setEnabled(false);
    _ui->errorLabel->clear();
    if(_haveKey)
        _tarsnapKeyFile = _ui->machineKeyCombo->currentText();
    else
        _tarsnapKeyFile =
            _appDataDir + QDir::separator() + _ui->machineNameLineEdit->text() +
            "-" + QDateTime::currentDateTime().toString("yyyy-MM-dd-HH-mm-ss") +
            ".key";

    DEBUG << "Registration details >>\n" << _tarsnapDir << ::endl
          << _appDataDir << ::endl
          << _tarsnapKeyFile << ::endl
          << _tarsnapCacheDir;

    emit requestRegisterMachine(_ui->tarsnapUserLineEdit->text(),
                                _ui->tarsnapPasswordLineEdit->text(),
                                _ui->machineNameLineEdit->text(),
                                _tarsnapKeyFile, _tarsnapDir, _tarsnapCacheDir);
}

void SetupDialog::registerMachineStatus(TaskStatus status, QString reason)
{
    switch(status)
    {
    case TaskStatus::Completed:
        _ui->errorLabel->clear();
        _ui->doneKeyFileNameLabel->setText(
                    QString("%2")
                           .arg(QUrl::fromLocalFile(_tarsnapKeyFile).toString())
                           .arg(_tarsnapKeyFile));
        _ui->doneButton->setEnabled(true);
        setNextPage();
        break;
    case TaskStatus::Failed:
        _ui->errorLabel->setText(reason);
        _ui->errorLabel->show();
        _ui->registerMachineButton->setEnabled(true);
        resize(sizeHint());
        break;
    default:
        break;
    }
}

void SetupDialog::updateLoadingAnimation(bool idle)
{
    if(idle)
    {
        _loadingAnimation.stop();
        _ui->loadingIconLabel->hide();
    }
    else
    {
        _loadingAnimation.start();
        _ui->loadingIconLabel->show();
    }
}

void SetupDialog::setTarsnapVersion(QString versionString)
{
    _tarsnapVersion = versionString;
    if(_tarsnapVersion.isEmpty())
    {
        _ui->clientVersionLabel->setText(
            tr("Tarsnap utilities not found. Visit "
               "tarsnap.com "
               "for help with acquiring them."));
    }
    else
    {
        _ui->clientVersionLabel->setText(tr("Tarsnap CLI version ") +
                                         _tarsnapVersion + tr(" detected.  ✔"));
    }
}

void SetupDialog::commitSettings(bool skipped)
{
    QSettings settings;

    DEBUG << "Settings location is " << settings.fileName();

    settings.setValue("app/wizard_done", true);

    if(!skipped)
    {
        QSettings settings;
        settings.setValue("app/app_data",    _appDataDir);
        settings.setValue("tarsnap/path",    _tarsnapDir);
        settings.setValue("tarsnap/version", _tarsnapVersion);
        settings.setValue("tarsnap/cache",   _tarsnapCacheDir);
        settings.setValue("tarsnap/key",     _tarsnapKeyFile);
        settings.setValue("tarsnap/user",    _ui->tarsnapUserLineEdit->text());
        settings.setValue("tarsnap/machine", _ui->machineNameLineEdit->text());
    }
    settings.sync();

    accept();
}
<commit_msg>Open residing dir, instead of the key file itself.<commit_after>#include "setupdialog.h"
#include "debug.h"
#include "ui_setupdialog.h"
#include "utils.h"

#include <QDateTime>
#include <QDir>
#include <QFileDialog>
#include <QFileInfo>
#include <QHostInfo>
#include <QSettings>
#include <QStandardPaths>

SetupDialog::SetupDialog(QWidget *parent)
    : QDialog(parent),
      _ui(new Ui::SetupDialog),
      _loadingAnimation(":/icons/loading.gif"),
      _haveKey(false)
{
    _ui->setupUi(this);
    setWindowFlags((windowFlags() | Qt::CustomizeWindowHint) &
                   ~Qt::WindowMaximizeButtonHint);

    _ui->loadingIconLabel->setMovie(&_loadingAnimation);
    _ui->errorLabel->hide();
    _ui->machineKeyLabel->hide();
    _ui->machineKeyCombo->hide();
    _ui->browseKeyButton->hide();
    _ui->advancedCLIWidget->hide();

    connect(_ui->welcomePageRadioButton, &QRadioButton::clicked, this,
            &SetupDialog::skipToPage);
    connect(_ui->restorePageRadioButton, &QRadioButton::clicked, this,
            &SetupDialog::skipToPage);
    connect(_ui->advancedPageRadioButton, &QRadioButton::clicked, this,
            &SetupDialog::skipToPage);
    connect(_ui->registerPageRadioButton, &QRadioButton::clicked, this,
            &SetupDialog::skipToPage);
    connect(_ui->donePageRadioButton, &QRadioButton::clicked, this,
            &SetupDialog::skipToPage);

    connect(_ui->wizardStackedWidget, &QStackedWidget::currentChanged, this,
            &SetupDialog::wizardPageChanged);

    // Welcome page
    connect(_ui->welcomePageSkipButton, &QPushButton::clicked,
            [&]() { commitSettings(true); });
    connect(_ui->welcomePageProceedButton, &QPushButton::clicked, this,
            &SetupDialog::setNextPage);

    // Advanced setup page
    connect(_ui->advancedCLIButton, &QPushButton::toggled,
            _ui->advancedCLIWidget, &QWidget::setVisible);
    connect(_ui->tarsnapPathBrowseButton, &QPushButton::clicked, this,
            &SetupDialog::showTarsnapPathBrowse);
    connect(_ui->tarsnapPathLineEdit, &QLineEdit::textChanged, this,
            &SetupDialog::validateAdvancedSetupPage);
    connect(_ui->tarsnapCacheBrowseButton, &QPushButton::clicked, this,
            &SetupDialog::showTarsnapCacheBrowse);
    connect(_ui->tarsnapCacheLineEdit, &QLineEdit::textChanged, this,
            &SetupDialog::validateAdvancedSetupPage);
    connect(_ui->appDataBrowseButton, &QPushButton::clicked, this,
            &SetupDialog::showAppDataBrowse);
    connect(_ui->appDataPathLineEdit, &QLineEdit::textChanged, this,
            &SetupDialog::validateAdvancedSetupPage);
    connect(_ui->advancedPageProceedButton, &QPushButton::clicked, this,
            &SetupDialog::setNextPage);

    // Restore page
    connect(_ui->restoreNoButton, &QPushButton::clicked, this,
            &SetupDialog::restoreNo);
    connect(_ui->restoreYesButton, &QPushButton::clicked, this,
            &SetupDialog::restoreYes);

    // Register page
    connect(_ui->tarsnapUserLineEdit, &QLineEdit::textChanged, this,
            &SetupDialog::validateRegisterPage);
    connect(_ui->tarsnapPasswordLineEdit, &QLineEdit::textChanged, this,
            &SetupDialog::validateRegisterPage);
    connect(_ui->machineNameLineEdit, &QLineEdit::textChanged, this,
            &SetupDialog::validateRegisterPage);
    connect(_ui->machineKeyCombo, &QComboBox::currentTextChanged, this,
            &SetupDialog::validateRegisterPage);
    connect(_ui->browseKeyButton, &QPushButton::clicked, this,
            &SetupDialog::registerHaveKeyBrowse);
    connect(_ui->registerMachineButton, &QPushButton::clicked, this,
            &SetupDialog::registerMachine);

    // Done page
    connect(_ui->doneButton, &QPushButton::clicked, this,
            &SetupDialog::commitSettings);

    _appDataDir = QStandardPaths::writableLocation(APPDATA);
    QDir keysDir(_appDataDir);
    if(!keysDir.exists())
        keysDir.mkpath(_appDataDir);
    _ui->appDataPathLineEdit->setText(_appDataDir);

    _tarsnapCacheDir =
        QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
    QDir cacheDir(_tarsnapCacheDir);
    if(!cacheDir.exists())
        cacheDir.mkpath(_tarsnapCacheDir);
    _ui->tarsnapCacheLineEdit->setText(_tarsnapCacheDir);

    _tarsnapDir = Utils::findTarsnapClientInPath("", true);
    _ui->tarsnapPathLineEdit->setText(_tarsnapDir);
    _ui->machineNameLineEdit->setText(QHostInfo::localHostName());
    _ui->wizardStackedWidget->setCurrentWidget(_ui->welcomePage);
}

SetupDialog::~SetupDialog()
{
    delete _ui;
}

void SetupDialog::wizardPageChanged(int)
{
    if(_ui->wizardStackedWidget->currentWidget() == _ui->welcomePage)
    {
        _ui->welcomePageRadioButton->setChecked(true);
        _ui->titleLabel->setText(tr("Setup wizard"));
    }
    else if(_ui->wizardStackedWidget->currentWidget() == _ui->advancedPage)
    {
        _ui->advancedPageRadioButton->setChecked(true);
        _ui->titleLabel->setText(tr("Command-line utilities"));
    }
    else if(_ui->wizardStackedWidget->currentWidget() == _ui->restorePage)
    {
        _ui->restorePageRadioButton->setChecked(true);
        _ui->titleLabel->setText(tr("Machine key"));
    }
    else if(_ui->wizardStackedWidget->currentWidget() == _ui->registerPage)
    {
        _ui->registerPageRadioButton->setChecked(true);
        _ui->titleLabel->setText(tr("Register with server"));
    }
    else if(_ui->wizardStackedWidget->currentWidget() == _ui->donePage)
    {
        _ui->donePageRadioButton->setChecked(true);
        _ui->titleLabel->setText(tr("Setup complete!"));
    }
}

void SetupDialog::skipToPage()
{
    if(sender() == _ui->welcomePageRadioButton)
        _ui->wizardStackedWidget->setCurrentWidget(_ui->welcomePage);
    else if(sender() == _ui->restorePageRadioButton)
        _ui->wizardStackedWidget->setCurrentWidget(_ui->restorePage);
    else if(sender() == _ui->advancedPageRadioButton)
        _ui->wizardStackedWidget->setCurrentWidget(_ui->advancedPage);
    else if(sender() == _ui->registerPageRadioButton)
        _ui->wizardStackedWidget->setCurrentWidget(_ui->registerPage);
    else if(sender() == _ui->donePageRadioButton)
        _ui->wizardStackedWidget->setCurrentWidget(_ui->donePage);
}

void SetupDialog::setNextPage()
{
    if(_ui->wizardStackedWidget->currentWidget() == _ui->welcomePage)
    {
        _ui->wizardStackedWidget->setCurrentWidget(_ui->advancedPage);
        _ui->advancedPageRadioButton->setEnabled(true);
        validateAdvancedSetupPage();
    }
    else if(_ui->wizardStackedWidget->currentWidget() == _ui->advancedPage)
    {
        _ui->wizardStackedWidget->setCurrentWidget(_ui->restorePage);
        _ui->restorePageRadioButton->setEnabled(true);
    }
    else if(_ui->wizardStackedWidget->currentWidget() == _ui->restorePage)
    {
        _ui->wizardStackedWidget->setCurrentWidget(_ui->registerPage);
        _ui->registerPageRadioButton->setEnabled(true);
    }
    else if(_ui->wizardStackedWidget->currentWidget() == _ui->registerPage)
    {
        _ui->wizardStackedWidget->setCurrentWidget(_ui->donePage);
        _ui->donePageRadioButton->setEnabled(true);
    }
}

void SetupDialog::showTarsnapPathBrowse()
{
    QString tarsnapPath =
        QFileDialog::getExistingDirectory(this, tr("Find Tarsnap client"), "");
    _ui->tarsnapPathLineEdit->setText(tarsnapPath);
}

void SetupDialog::showTarsnapCacheBrowse()
{
    QString tarsnapCacheDir =
        QFileDialog::getExistingDirectory(this, tr("Tarsnap cache location"),
                                          _tarsnapCacheDir);
    _ui->tarsnapCacheLineEdit->setText(tarsnapCacheDir);
}

void SetupDialog::showAppDataBrowse()
{
    QString appDataDir =
        QFileDialog::getExistingDirectory(this, tr("App data location"), "");
    _ui->appDataPathLineEdit->setText(appDataDir);
}

bool SetupDialog::validateAdvancedSetupPage()
{
    bool result = false;

    setTarsnapVersion("");

    _tarsnapDir =
        Utils::findTarsnapClientInPath(_ui->tarsnapPathLineEdit->text(), true);
    _tarsnapCacheDir =
        Utils::validateTarsnapCache(_ui->tarsnapCacheLineEdit->text());
    QFileInfo appDataDir(_ui->appDataPathLineEdit->text());
    if(appDataDir.exists() && appDataDir.isDir() && appDataDir.isWritable())
        _appDataDir = _ui->appDataPathLineEdit->text();
    else
        _appDataDir.clear();

    if(_tarsnapDir.isEmpty() || _tarsnapCacheDir.isEmpty() ||
       _appDataDir.isEmpty())
        result = false;
    else
        result = true;

    if(result)
        emit getTarsnapVersion(_tarsnapDir);

    _ui->advancedPageProceedButton->setEnabled(result);

    return result;
}

void SetupDialog::restoreNo()
{
    _haveKey = false;
    _ui->machineKeyLabel->hide();
    _ui->machineKeyCombo->hide();
    _ui->browseKeyButton->hide();
    _ui->tarsnapUserLabel->show();
    _ui->tarsnapUserLineEdit->show();
    _ui->tarsnapPasswordLabel->show();
    _ui->tarsnapPasswordLineEdit->show();
    _ui->registerPageInfoLabel->setText(
        tr("Please use your Tarsnap account "
           "credentials to register this machine with the service "
           "and create a local key for this machine."));
    _ui->registerPageInfoLabelAside->setText(
        tr("Don't have an account? Register on "
        "tarsnap.com, "
        "then come back."));
    _ui->errorLabel->clear();
    setNextPage();
}

void SetupDialog::restoreYes()
{
    _haveKey = true;
    _ui->tarsnapUserLabel->hide();
    _ui->tarsnapUserLineEdit->hide();
    _ui->tarsnapPasswordLabel->hide();
    _ui->tarsnapPasswordLineEdit->hide();
    _ui->machineKeyLabel->show();
    _ui->machineKeyCombo->show();
    _ui->browseKeyButton->show();
    _ui->registerPageInfoLabel->setText(
        tr("Please use your existing machine key "
           "and choose a machine name. "));
    _ui->registerPageInfoLabelAside->setText(
        tr("The registration will also "
           "verify the archive integrity and consistency, "
           "so please be patient."));
    _ui->errorLabel->clear();
    _ui->machineKeyCombo->clear();
    foreach(QFileInfo file, Utils::findKeysInPath(_appDataDir))
        _ui->machineKeyCombo->addItem(file.canonicalFilePath());
    setNextPage();
}

void SetupDialog::validateRegisterPage()
{
    bool result = false;
    if(_haveKey)
    {
        // user specified key
        QFileInfo machineKeyFile(_ui->machineKeyCombo->currentText());
        if(!_ui->machineNameLineEdit->text().isEmpty() &&
           machineKeyFile.exists() && machineKeyFile.isFile() &&
           machineKeyFile.isReadable())
        {
            result = true;
        }
    }
    else
    {
        if(!_ui->tarsnapUserLineEdit->text().isEmpty() &&
           !_ui->tarsnapPasswordLineEdit->text().isEmpty() &&
           !_ui->machineNameLineEdit->text().isEmpty())
        {
            result = true;
        }
    }

    _ui->registerMachineButton->setEnabled(result);
}

void SetupDialog::registerHaveKeyBrowse()
{
    QString existingMachineKey =
        QFileDialog::getOpenFileName(this,
                                     tr("Browse for existing machine key"));
    if(!existingMachineKey.isEmpty())
        _ui->machineKeyCombo->setCurrentText(existingMachineKey);
}

void SetupDialog::registerMachine()
{
    _ui->registerMachineButton->setEnabled(false);
    _ui->errorLabel->clear();
    if(_haveKey)
        _tarsnapKeyFile = _ui->machineKeyCombo->currentText();
    else
        _tarsnapKeyFile =
            _appDataDir + QDir::separator() + _ui->machineNameLineEdit->text() +
            "-" + QDateTime::currentDateTime().toString("yyyy-MM-dd-HH-mm-ss") +
            ".key";

    DEBUG << "Registration details >>\n" << _tarsnapDir << ::endl
          << _appDataDir << ::endl
          << _tarsnapKeyFile << ::endl
          << _tarsnapCacheDir;

    emit requestRegisterMachine(_ui->tarsnapUserLineEdit->text(),
                                _ui->tarsnapPasswordLineEdit->text(),
                                _ui->machineNameLineEdit->text(),
                                _tarsnapKeyFile, _tarsnapDir, _tarsnapCacheDir);
}

void SetupDialog::registerMachineStatus(TaskStatus status, QString reason)
{
    switch(status)
    {
    case TaskStatus::Completed:
        _ui->errorLabel->clear();
        _ui->doneKeyFileNameLabel->setText(
                    QString("%2")
                           .arg(QUrl::fromLocalFile(QFileInfo(_tarsnapKeyFile).absolutePath()).toString())
                           .arg(_tarsnapKeyFile));
        _ui->doneButton->setEnabled(true);
        setNextPage();
        break;
    case TaskStatus::Failed:
        _ui->errorLabel->setText(reason);
        _ui->errorLabel->show();
        _ui->registerMachineButton->setEnabled(true);
        resize(sizeHint());
        break;
    default:
        break;
    }
}

void SetupDialog::updateLoadingAnimation(bool idle)
{
    if(idle)
    {
        _loadingAnimation.stop();
        _ui->loadingIconLabel->hide();
    }
    else
    {
        _loadingAnimation.start();
        _ui->loadingIconLabel->show();
    }
}

void SetupDialog::setTarsnapVersion(QString versionString)
{
    _tarsnapVersion = versionString;
    if(_tarsnapVersion.isEmpty())
    {
        _ui->clientVersionLabel->setText(
            tr("Tarsnap utilities not found. Visit "
               "tarsnap.com "
               "for help with acquiring them."));
    }
    else
    {
        _ui->clientVersionLabel->setText(tr("Tarsnap CLI version ") +
                                         _tarsnapVersion + tr(" detected.  ✔"));
    }
}

void SetupDialog::commitSettings(bool skipped)
{
    QSettings settings;

    DEBUG << "Settings location is " << settings.fileName();

    settings.setValue("app/wizard_done", true);

    if(!skipped)
    {
        QSettings settings;
        settings.setValue("app/app_data",    _appDataDir);
        settings.setValue("tarsnap/path",    _tarsnapDir);
        settings.setValue("tarsnap/version", _tarsnapVersion);
        settings.setValue("tarsnap/cache",   _tarsnapCacheDir);
        settings.setValue("tarsnap/key",     _tarsnapKeyFile);
        settings.setValue("tarsnap/user",    _ui->tarsnapUserLineEdit->text());
        settings.setValue("tarsnap/machine", _ui->machineNameLineEdit->text());
    }
    settings.sync();

    accept();
}
<|endoftext|>


<commit_before>/**
 * \file
 * \brief Mutex class header
 *
 * \author Copyright (C) 2014-2017 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#ifndef INCLUDE_DISTORTOS_MUTEX_HPP_
#define INCLUDE_DISTORTOS_MUTEX_HPP_

#include "distortos/internal/synchronization/MutexControlBlock.hpp"

namespace distortos
{

/**
 * \brief Mutex is the basic synchronization primitive
 *
 * Similar to std::mutex - http://en.cppreference.com/w/cpp/thread/mutex
 * Similar to POSIX pthread_mutex_t -
 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_09 -> 2.9.3 Thread Mutexes
 *
 * \ingroup synchronization
 */

class Mutex : private internal::MutexControlBlock
{
public:

	/// mutex protocols
	using Protocol = MutexProtocol;

	/// type used for counting recursive locks
	using RecursiveLocksCount = MutexControlBlock::RecursiveLocksCount;

	/// type of mutex
	using Type = MutexType;

	/**
	 * \brief Gets the maximum number of recursive locks possible before returning EAGAIN
	 *
	 * \note Actual number of lock() operations possible is getMaxRecursiveLocks() + 1.
	 *
	 * \return maximum number of recursive locks possible before returning EAGAIN
	 */

	constexpr static RecursiveLocksCount getMaxRecursiveLocks()
	{
		return std::numeric_limits<RecursiveLocksCount>::max();
	}

	/**
	 * \brief Mutex's constructor
	 *
	 * Similar to std::mutex::mutex() - http://en.cppreference.com/w/cpp/thread/mutex/mutex
	 * Similar to pthread_mutex_init() -
	 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_init.html
	 *
	 * \param [in] type is the type of mutex, default - Type::normal
	 * \param [in] protocol is the mutex protocol, default - Protocol::none
	 * \param [in] priorityCeiling is the priority ceiling of mutex, ignored when protocol != Protocol::priorityProtect,
	 * default - 0
	 */

	constexpr explicit Mutex(const Type type = Type::normal, const Protocol protocol = Protocol::none,
			const uint8_t priorityCeiling = {}) :
					MutexControlBlock{type, protocol, priorityCeiling}
	{

	}

	/**
	 * \brief Mutex's constructor, overload for "normal" type
	 *
	 * Similar to std::mutex::mutex() - http://en.cppreference.com/w/cpp/thread/mutex/mutex
	 * Similar to pthread_mutex_init() -
	 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_init.html
	 *
	 * \param [in] protocol is the mutex protocol
	 * \param [in] priorityCeiling is the priority ceiling of mutex, ignored when protocol != Protocol::priorityProtect,
	 * default - 0
	 */

	constexpr explicit Mutex(const Protocol protocol, const uint8_t priorityCeiling = {}) :
			Mutex{Type::normal, protocol, priorityCeiling}
	{

	}

	/**
	 * \brief Mutex's destructor
	 *
	 * Similar to std::mutex::~mutex() - http://en.cppreference.com/w/cpp/thread/mutex/~mutex
	 * Similar to pthread_mutex_destroy() -
	 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_destroy.html
	 *
	 * It shall be safe to destroy an initialized mutex that is unlocked. Attempting to destroy a locked mutex, or a
	 * mutex that another thread is attempting to lock, or a mutex that is currently being used by a condition variable
	 * in another thread, results in undefined behavior.
	 */

	~Mutex() = default;

	/**
	 * \brief Locks the mutex.
	 *
	 * Similar to std::mutex::lock() - http://en.cppreference.com/w/cpp/thread/mutex/lock
	 * Similar to pthread_mutex_lock() -
	 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_lock.html#
	 *
	 * If the mutex is already locked by another thread, the calling thread shall block until the mutex becomes
	 * available. This function shall return with the mutex in the locked state with the calling thread as its owner. If
	 * a thread attempts to relock a mutex that it has already locked, deadlock occurs.
	 *
	 * \warning This function must not be called from interrupt context!
	 *
	 * \return 0 if the caller successfully locked the mutex, error code otherwise:
	 * - EAGAIN - the mutex could not be acquired because the maximum number of recursive locks for mutex has been
	 * exceeded;
	 * - EDEADLK - the mutex type is errorChecking and the current thread already owns the mutex;
	 * - EINVAL - the mutex was created with the protocol attribute having the value priorityProtect and the calling
	 * thread's priority is higher than the mutex's current priority ceiling;
	 */

	int lock();

	/**
	 * \brief Tries to lock the mutex.
	 *
	 * Similar to std::mutex::try_lock() - http://en.cppreference.com/w/cpp/thread/mutex/try_lock
	 * Similar to pthread_mutex_trylock() -
	 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_lock.html#
	 *
	 * This function shall be equivalent to lock(), except that if the mutex is currently locked (by any thread,
	 * including the current thread), the call shall return immediately.
	 *
	 * \warning This function must not be called from interrupt context!
	 *
	 * \return 0 if the caller successfully locked the mutex, error code otherwise:
	 * - EAGAIN - the mutex could not be acquired because the maximum number of recursive locks for mutex has been
	 * exceeded;
	 * - EBUSY - the mutex could not be acquired because it was already locked;
	 * - EINVAL - the mutex was created with the protocol attribute having the value priorityProtect and the calling
	 * thread's priority is higher than the mutex's current priority ceiling;
	 */

	int tryLock();

	/**
	 * \brief Tries to lock the mutex for given duration of time.
	 *
	 * Similar to std::timed_mutex::try_lock_for() - http://en.cppreference.com/w/cpp/thread/timed_mutex/try_lock_for
	 * Similar to pthread_mutex_timedlock() -
	 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_timedlock.html#
	 *
	 * If the mutex is already locked, the calling thread shall block until the mutex becomes available as in lock()
	 * function. If the mutex cannot be locked without waiting for another thread to unlock the mutex, this wait shall
	 * be terminated when the specified timeout expires.
	 *
	 * Under no circumstance shall the function fail with a timeout if the mutex can be locked immediately. The validity
	 * of the duration parameter need not be checked if the mutex can be locked immediately.
	 *
	 * \warning This function must not be called from interrupt context!
	 *
	 * \param [in] duration is the duration after which the wait will be terminated without locking the mutex
	 *
	 * \return 0 if the caller successfully locked the mutex, error code otherwise:
	 * - EAGAIN - the mutex could not be acquired because the maximum number of recursive locks for mutex has been
	 * exceeded;
	 * - EDEADLK - the mutex type is errorChecking and the current thread already owns the mutex;
	 * - EINVAL - the mutex was created with the protocol attribute having the value priorityProtect and the calling
	 * thread's priority is higher than the mutex's current priority ceiling;
	 * - ETIMEDOUT - the mutex could not be locked before the specified timeout expired;
	 */

	int tryLockFor(TickClock::duration duration);

	/**
	 * Tries to lock the mutex for given duration of time.
	 *
	 * Template variant of tryLockFor(TickClock::duration duration).
	 *
	 * \warning This function must not be called from interrupt context!
	 *
	 * \tparam Rep is type of tick counter
	 * \tparam Period is std::ratio type representing the tick period of the clock, seconds
	 *
	 * \param [in] duration is the duration after which the wait will be terminated without locking the mutex
	 *
	 * \return 0 if the caller successfully locked the mutex, error code otherwise:
	 * - EAGAIN - the mutex could not be acquired because the maximum number of recursive locks for mutex has been
	 * exceeded;
	 * - EDEADLK - the mutex type is errorChecking and the current thread already owns the mutex;
	 * - EINVAL - the mutex was created with the protocol attribute having the value priorityProtect and the calling
	 * thread's priority is higher than the mutex's current priority ceiling;
	 * - ETIMEDOUT - the mutex could not be locked before the specified timeout expired;
	 */

	template<typename Rep, typename Period>
	int tryLockFor(const std::chrono::duration<Rep, Period> duration)
	{
		return tryLockFor(std::chrono::duration_cast<TickClock::duration>(duration));
	}

	/**
	 * \brief Tries to lock the mutex until given time point.
	 *
	 * Similar to std::timed_mutex::try_lock_until() -
	 * http://en.cppreference.com/w/cpp/thread/timed_mutex/try_lock_until
	 * Similar to pthread_mutex_timedlock() -
	 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_timedlock.html#
	 *
	 * If the mutex is already locked, the calling thread shall block until the mutex becomes available as in lock()
	 * function. If the mutex cannot be locked without waiting for another thread to unlock the mutex, this wait shall
	 * be terminated when the specified timeout expires.
	 *
	 * Under no circumstance shall the function fail with a timeout if the mutex can be locked immediately. The validity
	 * of the timePoint parameter need not be checked if the mutex can be locked immediately.
	 *
	 * \warning This function must not be called from interrupt context!
	 *
	 * \param [in] timePoint is the time point at which the wait will be terminated without locking the mutex
	 *
	 * \return 0 if the caller successfully locked the mutex, error code otherwise:
	 * - EAGAIN - the mutex could not be acquired because the maximum number of recursive locks for mutex has been
	 * exceeded;
	 * - EDEADLK - the mutex type is errorChecking and the current thread already owns the mutex;
	 * - EINVAL - the mutex was created with the protocol attribute having the value priorityProtect and the calling
	 * thread's priority is higher than the mutex's current priority ceiling;
	 * - ETIMEDOUT - the mutex could not be locked before the specified timeout expired;
	 */

	int tryLockUntil(TickClock::time_point timePoint);

	/**
	 * \brief Tries to lock the mutex until given time point.
	 *
	 * Template variant of tryLockUntil(TickClock::time_point timePoint).
	 *
	 * \warning This function must not be called from interrupt context!
	 *
	 * \tparam Duration is a std::chrono::duration type used to measure duration
	 *
	 * \param [in] timePoint is the time point at which the wait will be terminated without locking the mutex
	 *
	 * \return 0 if the caller successfully locked the mutex, error code otherwise:
	 * - EAGAIN - the mutex could not be acquired because the maximum number of recursive locks for mutex has been
	 * exceeded;
	 * - EDEADLK - the mutex type is errorChecking and the current thread already owns the mutex;
	 * - EINVAL - the mutex was created with the protocol attribute having the value priorityProtect and the calling
	 * thread's priority is higher than the mutex's current priority ceiling;
	 * - ETIMEDOUT - the mutex could not be locked before the specified timeout expired;
	 */

	template<typename Duration>
	int tryLockUntil(const std::chrono::time_point<TickClock, Duration> timePoint)
	{
		return tryLockUntil(std::chrono::time_point_cast<TickClock::duration>(timePoint));
	}

	/**
	 * \brief Unlocks the mutex.
	 *
	 * Similar to std::mutex::unlock() - http://en.cppreference.com/w/cpp/thread/mutex/unlock
	 * Similar to pthread_mutex_unlock() -
	 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_lock.html#
	 *
	 * The mutex must be locked by the current thread, otherwise, the behavior is undefined. If there are threads
	 * blocked on this mutex, the highest priority waiting thread shall be unblocked, and if there is more than one
	 * highest priority thread blocked waiting, then the highest priority thread that has been waiting the longest shall
	 * be unblocked.
	 *
	 * \warning This function must not be called from interrupt context!
	 *
	 * \return 0 if the caller successfully unlocked the mutex, error code otherwise:
	 * - EPERM - the mutex type is errorChecking or recursive, and the current thread does not own the mutex;
	 */

	int unlock();

	Mutex(const Mutex&) = delete;
	Mutex(Mutex&&) = default;
	const Mutex& operator=(const Mutex&) = delete;
	Mutex& operator=(Mutex&&) = delete;

private:

	/**
	 * \brief Internal version of tryLock().
	 *
	 * Internal version with no interrupt masking and additional code for errorChecking type (which is not required for
	 * tryLock()).
	 *
	 * \return 0 if the caller successfully locked the mutex, error code otherwise:
	 * - EAGAIN - the mutex could not be acquired because the maximum number of recursive locks for mutex has been
	 * exceeded;
	 * - EBUSY - the mutex could not be acquired because it was already locked;
	 * - EDEADLK - the mutex type is errorChecking and the current thread already owns the mutex;
	 * - EINVAL - the mutex was created with the protocol attribute having the value priorityProtect and the calling
	 * thread's priority is higher than the mutex's current priority ceiling;
	 */

	int tryLockInternal();
};

}	// namespace distortos

#endif	// INCLUDE_DISTORTOS_MUTEX_HPP_
<commit_msg>Minor comment fix in Mutex.hpp<commit_after>/**
 * \file
 * \brief Mutex class header
 *
 * \author Copyright (C) 2014-2019 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#ifndef INCLUDE_DISTORTOS_MUTEX_HPP_
#define INCLUDE_DISTORTOS_MUTEX_HPP_

#include "distortos/internal/synchronization/MutexControlBlock.hpp"

namespace distortos
{

/**
 * \brief Mutex is the basic synchronization primitive
 *
 * Similar to std::mutex - http://en.cppreference.com/w/cpp/thread/mutex
 * Similar to POSIX pthread_mutex_t -
 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_09 -> 2.9.3 Thread Mutexes
 *
 * \ingroup synchronization
 */

class Mutex : private internal::MutexControlBlock
{
public:

	/// mutex protocols
	using Protocol = MutexProtocol;

	/// type used for counting recursive locks
	using RecursiveLocksCount = MutexControlBlock::RecursiveLocksCount;

	/// type of mutex
	using Type = MutexType;

	/**
	 * \brief Gets the maximum number of recursive locks possible before returning EAGAIN
	 *
	 * \note Actual number of lock() operations possible is getMaxRecursiveLocks() + 1.
	 *
	 * \return maximum number of recursive locks possible before returning EAGAIN
	 */

	constexpr static RecursiveLocksCount getMaxRecursiveLocks()
	{
		return std::numeric_limits<RecursiveLocksCount>::max();
	}

	/**
	 * \brief Mutex's constructor
	 *
	 * Similar to std::mutex::mutex() - http://en.cppreference.com/w/cpp/thread/mutex/mutex
	 * Similar to pthread_mutex_init() -
	 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_init.html
	 *
	 * \param [in] type is the type of mutex, default - Type::normal
	 * \param [in] protocol is the mutex protocol, default - Protocol::none
	 * \param [in] priorityCeiling is the priority ceiling of mutex, ignored when protocol != Protocol::priorityProtect,
	 * default - 0
	 */

	constexpr explicit Mutex(const Type type = Type::normal, const Protocol protocol = Protocol::none,
			const uint8_t priorityCeiling = {}) :
					MutexControlBlock{type, protocol, priorityCeiling}
	{

	}

	/**
	 * \brief Mutex's constructor, overload for "normal" type
	 *
	 * Similar to std::mutex::mutex() - http://en.cppreference.com/w/cpp/thread/mutex/mutex
	 * Similar to pthread_mutex_init() -
	 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_init.html
	 *
	 * \param [in] protocol is the mutex protocol
	 * \param [in] priorityCeiling is the priority ceiling of mutex, ignored when protocol != Protocol::priorityProtect,
	 * default - 0
	 */

	constexpr explicit Mutex(const Protocol protocol, const uint8_t priorityCeiling = {}) :
			Mutex{Type::normal, protocol, priorityCeiling}
	{

	}

	/**
	 * \brief Mutex's destructor
	 *
	 * Similar to std::mutex::~mutex() - http://en.cppreference.com/w/cpp/thread/mutex/~mutex
	 * Similar to pthread_mutex_destroy() -
	 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_destroy.html
	 *
	 * It shall be safe to destroy an initialized mutex that is unlocked. Attempting to destroy a locked mutex, or a
	 * mutex that another thread is attempting to lock, or a mutex that is currently being used by a condition variable
	 * in another thread, results in undefined behavior.
	 */

	~Mutex() = default;

	/**
	 * \brief Locks the mutex.
	 *
	 * Similar to std::mutex::lock() - http://en.cppreference.com/w/cpp/thread/mutex/lock
	 * Similar to pthread_mutex_lock() -
	 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_lock.html#
	 *
	 * If the mutex is already locked by another thread, the calling thread shall block until the mutex becomes
	 * available. This function shall return with the mutex in the locked state with the calling thread as its owner. If
	 * a thread attempts to relock a mutex that it has already locked, deadlock occurs.
	 *
	 * \warning This function must not be called from interrupt context!
	 *
	 * \return 0 if the caller successfully locked the mutex, error code otherwise:
	 * - EAGAIN - the mutex could not be acquired because the maximum number of recursive locks for mutex has been
	 * exceeded;
	 * - EDEADLK - the mutex type is errorChecking and the current thread already owns the mutex;
	 * - EINVAL - the mutex was created with the protocol attribute having the value priorityProtect and the calling
	 * thread's priority is higher than the mutex's current priority ceiling;
	 */

	int lock();

	/**
	 * \brief Tries to lock the mutex.
	 *
	 * Similar to std::mutex::try_lock() - http://en.cppreference.com/w/cpp/thread/mutex/try_lock
	 * Similar to pthread_mutex_trylock() -
	 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_lock.html#
	 *
	 * This function shall be equivalent to lock(), except that if the mutex is currently locked (by any thread,
	 * including the current thread), the call shall return immediately.
	 *
	 * \warning This function must not be called from interrupt context!
	 *
	 * \return 0 if the caller successfully locked the mutex, error code otherwise:
	 * - EAGAIN - the mutex could not be acquired because the maximum number of recursive locks for mutex has been
	 * exceeded;
	 * - EBUSY - the mutex could not be acquired because it was already locked;
	 * - EINVAL - the mutex was created with the protocol attribute having the value priorityProtect and the calling
	 * thread's priority is higher than the mutex's current priority ceiling;
	 */

	int tryLock();

	/**
	 * \brief Tries to lock the mutex for given duration of time.
	 *
	 * Similar to std::timed_mutex::try_lock_for() - http://en.cppreference.com/w/cpp/thread/timed_mutex/try_lock_for
	 * Similar to pthread_mutex_timedlock() -
	 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_timedlock.html#
	 *
	 * If the mutex is already locked, the calling thread shall block until the mutex becomes available as in lock()
	 * function. If the mutex cannot be locked without waiting for another thread to unlock the mutex, this wait shall
	 * be terminated when the specified timeout expires.
	 *
	 * Under no circumstance shall the function fail with a timeout if the mutex can be locked immediately. The validity
	 * of the duration parameter need not be checked if the mutex can be locked immediately.
	 *
	 * \warning This function must not be called from interrupt context!
	 *
	 * \param [in] duration is the duration after which the wait will be terminated without locking the mutex
	 *
	 * \return 0 if the caller successfully locked the mutex, error code otherwise:
	 * - EAGAIN - the mutex could not be acquired because the maximum number of recursive locks for mutex has been
	 * exceeded;
	 * - EDEADLK - the mutex type is errorChecking and the current thread already owns the mutex;
	 * - EINVAL - the mutex was created with the protocol attribute having the value priorityProtect and the calling
	 * thread's priority is higher than the mutex's current priority ceiling;
	 * - ETIMEDOUT - the mutex could not be locked before the specified timeout expired;
	 */

	int tryLockFor(TickClock::duration duration);

	/**
	 * \brief Tries to lock the mutex for given duration of time.
	 *
	 * Template variant of tryLockFor(TickClock::duration duration).
	 *
	 * \warning This function must not be called from interrupt context!
	 *
	 * \tparam Rep is type of tick counter
	 * \tparam Period is std::ratio type representing the tick period of the clock, seconds
	 *
	 * \param [in] duration is the duration after which the wait will be terminated without locking the mutex
	 *
	 * \return 0 if the caller successfully locked the mutex, error code otherwise:
	 * - EAGAIN - the mutex could not be acquired because the maximum number of recursive locks for mutex has been
	 * exceeded;
	 * - EDEADLK - the mutex type is errorChecking and the current thread already owns the mutex;
	 * - EINVAL - the mutex was created with the protocol attribute having the value priorityProtect and the calling
	 * thread's priority is higher than the mutex's current priority ceiling;
	 * - ETIMEDOUT - the mutex could not be locked before the specified timeout expired;
	 */

	template<typename Rep, typename Period>
	int tryLockFor(const std::chrono::duration<Rep, Period> duration)
	{
		return tryLockFor(std::chrono::duration_cast<TickClock::duration>(duration));
	}

	/**
	 * \brief Tries to lock the mutex until given time point.
	 *
	 * Similar to std::timed_mutex::try_lock_until() -
	 * http://en.cppreference.com/w/cpp/thread/timed_mutex/try_lock_until
	 * Similar to pthread_mutex_timedlock() -
	 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_timedlock.html#
	 *
	 * If the mutex is already locked, the calling thread shall block until the mutex becomes available as in lock()
	 * function. If the mutex cannot be locked without waiting for another thread to unlock the mutex, this wait shall
	 * be terminated when the specified timeout expires.
	 *
	 * Under no circumstance shall the function fail with a timeout if the mutex can be locked immediately. The validity
	 * of the timePoint parameter need not be checked if the mutex can be locked immediately.
	 *
	 * \warning This function must not be called from interrupt context!
	 *
	 * \param [in] timePoint is the time point at which the wait will be terminated without locking the mutex
	 *
	 * \return 0 if the caller successfully locked the mutex, error code otherwise:
	 * - EAGAIN - the mutex could not be acquired because the maximum number of recursive locks for mutex has been
	 * exceeded;
	 * - EDEADLK - the mutex type is errorChecking and the current thread already owns the mutex;
	 * - EINVAL - the mutex was created with the protocol attribute having the value priorityProtect and the calling
	 * thread's priority is higher than the mutex's current priority ceiling;
	 * - ETIMEDOUT - the mutex could not be locked before the specified timeout expired;
	 */

	int tryLockUntil(TickClock::time_point timePoint);

	/**
	 * \brief Tries to lock the mutex until given time point.
	 *
	 * Template variant of tryLockUntil(TickClock::time_point timePoint).
	 *
	 * \warning This function must not be called from interrupt context!
	 *
	 * \tparam Duration is a std::chrono::duration type used to measure duration
	 *
	 * \param [in] timePoint is the time point at which the wait will be terminated without locking the mutex
	 *
	 * \return 0 if the caller successfully locked the mutex, error code otherwise:
	 * - EAGAIN - the mutex could not be acquired because the maximum number of recursive locks for mutex has been
	 * exceeded;
	 * - EDEADLK - the mutex type is errorChecking and the current thread already owns the mutex;
	 * - EINVAL - the mutex was created with the protocol attribute having the value priorityProtect and the calling
	 * thread's priority is higher than the mutex's current priority ceiling;
	 * - ETIMEDOUT - the mutex could not be locked before the specified timeout expired;
	 */

	template<typename Duration>
	int tryLockUntil(const std::chrono::time_point<TickClock, Duration> timePoint)
	{
		return tryLockUntil(std::chrono::time_point_cast<TickClock::duration>(timePoint));
	}

	/**
	 * \brief Unlocks the mutex.
	 *
	 * Similar to std::mutex::unlock() - http://en.cppreference.com/w/cpp/thread/mutex/unlock
	 * Similar to pthread_mutex_unlock() -
	 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_lock.html#
	 *
	 * The mutex must be locked by the current thread, otherwise, the behavior is undefined. If there are threads
	 * blocked on this mutex, the highest priority waiting thread shall be unblocked, and if there is more than one
	 * highest priority thread blocked waiting, then the highest priority thread that has been waiting the longest shall
	 * be unblocked.
	 *
	 * \warning This function must not be called from interrupt context!
	 *
	 * \return 0 if the caller successfully unlocked the mutex, error code otherwise:
	 * - EPERM - the mutex type is errorChecking or recursive, and the current thread does not own the mutex;
	 */

	int unlock();

	Mutex(const Mutex&) = delete;
	Mutex(Mutex&&) = default;
	const Mutex& operator=(const Mutex&) = delete;
	Mutex& operator=(Mutex&&) = delete;

private:

	/**
	 * \brief Internal version of tryLock().
	 *
	 * Internal version with no interrupt masking and additional code for errorChecking type (which is not required for
	 * tryLock()).
	 *
	 * \return 0 if the caller successfully locked the mutex, error code otherwise:
	 * - EAGAIN - the mutex could not be acquired because the maximum number of recursive locks for mutex has been
	 * exceeded;
	 * - EBUSY - the mutex could not be acquired because it was already locked;
	 * - EDEADLK - the mutex type is errorChecking and the current thread already owns the mutex;
	 * - EINVAL - the mutex was created with the protocol attribute having the value priorityProtect and the calling
	 * thread's priority is higher than the mutex's current priority ceiling;
	 */

	int tryLockInternal();
};

}	// namespace distortos

#endif	// INCLUDE_DISTORTOS_MUTEX_HPP_
<|endoftext|>


<commit_before>/*
This file is part of Bohrium and copyright (c) 2012 the Bohrium
team <http://www.bh107.org>.

Bohrium is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3
of the License, or (at your option) any later version.

Bohrium 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 Lesser General Public License along with Bohrium.

If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef __BH_JITK_STATISTICS_H
#define __BH_JITK_STATISTICS_H

#include <chrono>
#include <string>
#include <ostream>
#include <sstream>

namespace bohrium {
namespace jitk {

namespace {
// Pretty print the ratio: 'a/b'
std::string pprint_ratio(uint64_t a, uint64_t b) {
    std::stringstream ss;
    ss << a << "/" << b << " (" << 100.0 * a / b << "%)";
    return ss.str();
}
}

/* BaseDB is a database over base arrays. The main feature is getBases(),
 * which always returns the bases in the order they where inserted.
 */
class Statistics {
  public:
    uint64_t num_base_arrays=0;
    uint64_t num_temp_arrays=0;
    uint64_t max_memory_usage=0;
    uint64_t totalwork=0;
    uint64_t threading_below_threshold=0;
    uint64_t kernel_cache_lookups=0;
    uint64_t kernel_cache_misses=0;
    uint64_t fuser_cache_lookups=0;
    uint64_t fuser_cache_misses=0;
    std::chrono::duration<double> time_total_execution{0};
    std::chrono::duration<double> time_fusion{0};
    std::chrono::duration<double> time_exec{0};
    std::chrono::duration<double> time_build{0};
    std::chrono::duration<double> time_offload{0};
    std::chrono::time_point<std::chrono::steady_clock> time_started{std::chrono::steady_clock::now()};

    void pprint(std::string backend_name, std::ostream &out) {
        using namespace std;
        const chrono::duration<double> wallclock{chrono::steady_clock::now() - time_started};

        out << "[" << backend_name << "] Profiling: \n";
        out << "\tFuse Cache Hits:     " << pprint_ratio(fuser_cache_lookups - fuser_cache_misses, fuser_cache_lookups) << "\n";
        out << "\tKernel Cache Hits    " << pprint_ratio(kernel_cache_lookups - kernel_cache_misses, kernel_cache_lookups) << "\n";
        out << "\tArray contractions:  " << pprint_ratio(num_temp_arrays, num_base_arrays) << "\n";
        out << "\tMaximum Memory Usage: " << max_memory_usage / 1024 / 1024 << " MB\n";
        out << "\tTotal Work: " << (double) totalwork << " operations\n";
        out << "\tWork below par-threshold(1000): " << threading_below_threshold / (double)totalwork * 100 << "%\n";
        out << "\tWall clock:  " << wallclock.count() << "s\n";
        out << "\tFLOPS:  " << totalwork / (double)wallclock.count() << "s\n";
        out << "\tTotal Execution:  " << time_total_execution.count() << "s\n";
        out << "\t  Fusion:  " << time_fusion.count() << "s\n";
        out << "\t  Build:   " << time_build.count() << "s\n";
        out << "\t  Exec:    " << time_exec.count() << "s\n";
        out << "\t  Offload: " << time_offload.count() << "s\n";
        out << endl;
    }

};

} // jitk
} // bohrium

#endif
<commit_msg>statistics: cleanup<commit_after>/*
This file is part of Bohrium and copyright (c) 2012 the Bohrium
team <http://www.bh107.org>.

Bohrium is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3
of the License, or (at your option) any later version.

Bohrium 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 Lesser General Public License along with Bohrium.

If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef __BH_JITK_STATISTICS_H
#define __BH_JITK_STATISTICS_H

#include <chrono>
#include <string>
#include <ostream>
#include <sstream>

namespace bohrium {
namespace jitk {

namespace {
// Pretty print the ratio: 'a/b'
std::string pprint_ratio(uint64_t a, uint64_t b) {
    std::stringstream ss;
    ss << a << "/" << b << " (" << 100.0 * a / b << "%)";
    return ss.str();
}
}

/* BaseDB is a database over base arrays. The main feature is getBases(),
 * which always returns the bases in the order they where inserted.
 */
class Statistics {
  public:
    uint64_t num_base_arrays=0;
    uint64_t num_temp_arrays=0;
    uint64_t max_memory_usage=0;
    uint64_t totalwork=0;
    uint64_t threading_below_threshold=0;
    uint64_t kernel_cache_lookups=0;
    uint64_t kernel_cache_misses=0;
    uint64_t fuser_cache_lookups=0;
    uint64_t fuser_cache_misses=0;
    std::chrono::duration<double> time_total_execution{0};
    std::chrono::duration<double> time_fusion{0};
    std::chrono::duration<double> time_exec{0};
    std::chrono::duration<double> time_build{0};
    std::chrono::duration<double> time_offload{0};
    std::chrono::time_point<std::chrono::steady_clock> time_started{std::chrono::steady_clock::now()};

    // Pretty print the recorded statistics into 'out' where 'backend_name' is the name of the caller
    void pprint(std::string backend_name, std::ostream &out) {
        using namespace std;
        const chrono::duration<double> wallclock{chrono::steady_clock::now() - time_started};

        out << "[" << backend_name << "] Profiling: \n";
        out << "\tFuse Cache Hits:     " << pprint_ratio(fuser_cache_lookups - fuser_cache_misses, fuser_cache_lookups) << "\n";
        out << "\tKernel Cache Hits    " << pprint_ratio(kernel_cache_lookups - kernel_cache_misses, kernel_cache_lookups) << "\n";
        out << "\tArray contractions:  " << pprint_ratio(num_temp_arrays, num_base_arrays) << "\n";
        out << "\tMaximum Memory Usage: " << max_memory_usage / 1024 / 1024 << " MB\n";
        out << "\tTotal Work: " << (double) totalwork << " operations\n";
        out << "\tWork below par-threshold(1000): " << threading_below_threshold / (double)totalwork * 100 << "%\n";
        out << "\tWall clock:  " << wallclock.count() << "s\n";
        out << "\tThroughput:  " << totalwork / (double)wallclock.count() << "AOPS\n";
        out << "\tTotal Execution:  " << time_total_execution.count() << "s\n";
        out << "\t  Fusion:  " << time_fusion.count() << "s\n";
        out << "\t  Build:   " << time_build.count() << "s\n";
        out << "\t  Exec:    " << time_exec.count() << "s\n";
        out << "\t  Offload: " << time_offload.count() << "s\n";
        out << endl;
    }
};

} // jitk
} // bohrium

#endif
<|endoftext|>


<commit_before>#ifndef KGR_KANGARU_INCLUDE_KANGARU_TYPE_ID_HPP
#define KGR_KANGARU_INCLUDE_KANGARU_TYPE_ID_HPP

#include <cstdint>

namespace kgr {
namespace detail {

template<typename>
struct index_storage;

struct override_storage_service;

template<typename>
struct is_index_storage : std::false_type {};

template<typename T>
struct is_index_storage<index_storage<T>> : std::true_type {};

struct type_id_data {
	enum struct kind_t : std::uint8_t { normal, override_storage, index_storage } kind;

	template<typename T>
	static constexpr auto kind_for() noexcept -> type_id_data {
		return type_id_data{
			std::is_same<T, override_storage_service>::value
			? kind_t::override_storage
			: is_index_storage<T>::value ? kind_t::index_storage : kind_t::normal
		};
	}
};

/*
 * Template class that hold the declaration of the id.
 *
 * We use the pointer of this id as type id.
 */
template<typename T>
struct type_id_ptr {
	struct id_t {
		type_id_data data;
	};

	// Having a static data member will ensure us that it has only one address for the whole program.
	// Furthermore, the static data member having different types will ensure it won't get optimized.
	static constexpr id_t id = id_t{type_id_data::kind_for<T>()};
};

/*
 * Definition of the id.
 *
 * Before that, we used the pointer to a function as type_id.
 *
 * However, on some platform, the rule that a pointer to a function
 * always yeild to the same value, no matter the dll or TU was not always respected.
 *
 * Using the pointer of a static data member is more stable.
 */
template<typename T>
constexpr typename type_id_ptr<T>::id_t const type_id_ptr<T>::id;

inline constexpr auto type_id_kind(void const* id) -> type_id_data::kind_t {
	return static_cast<type_id_data const*>(id)->kind;
}

} // namespace detail

/*
 * The type of a type id.
 */
using type_id_t = void const*;

/*
 * The function that returns the type id.
 *
 * It uses the pointer to the static data member of a class template to achieve this.
 * Altough the value is not predictible, it's stable.
 */
template <typename T>
constexpr auto type_id() -> type_id_t {
	return &detail::type_id_ptr<T>::id;
}

} // namespace kgr

#endif // KGR_KANGARU_INCLUDE_KANGARU_TYPE_ID_HPP
<commit_msg>Added missing include<commit_after>#ifndef KGR_KANGARU_INCLUDE_KANGARU_TYPE_ID_HPP
#define KGR_KANGARU_INCLUDE_KANGARU_TYPE_ID_HPP

#include <cstdint>
#include <type_traits>

namespace kgr {
namespace detail {

template<typename>
struct index_storage;

struct override_storage_service;

template<typename>
struct is_index_storage : std::false_type {};

template<typename T>
struct is_index_storage<index_storage<T>> : std::true_type {};

struct type_id_data {
	enum struct kind_t : std::uint8_t { normal, override_storage, index_storage } kind;

	template<typename T>
	static constexpr auto kind_for() noexcept -> type_id_data {
		return type_id_data{
			std::is_same<T, override_storage_service>::value
			? kind_t::override_storage
			: is_index_storage<T>::value ? kind_t::index_storage : kind_t::normal
		};
	}
};

/*
 * Template class that hold the declaration of the id.
 *
 * We use the pointer of this id as type id.
 */
template<typename T>
struct type_id_ptr {
	struct id_t {
		type_id_data data;
	};

	// Having a static data member will ensure us that it has only one address for the whole program.
	// Furthermore, the static data member having different types will ensure it won't get optimized.
	static constexpr id_t id = id_t{type_id_data::kind_for<T>()};
};

/*
 * Definition of the id.
 *
 * Before that, we used the pointer to a function as type_id.
 *
 * However, on some platform, the rule that a pointer to a function
 * always yeild to the same value, no matter the dll or TU was not always respected.
 *
 * Using the pointer of a static data member is more stable.
 */
template<typename T>
constexpr typename type_id_ptr<T>::id_t const type_id_ptr<T>::id;

inline constexpr auto type_id_kind(void const* id) -> type_id_data::kind_t {
	return static_cast<type_id_data const*>(id)->kind;
}

} // namespace detail

/*
 * The type of a type id.
 */
using type_id_t = void const*;

/*
 * The function that returns the type id.
 *
 * It uses the pointer to the static data member of a class template to achieve this.
 * Altough the value is not predictible, it's stable.
 */
template <typename T>
constexpr auto type_id() -> type_id_t {
	return &detail::type_id_ptr<T>::id;
}

} // namespace kgr

#endif // KGR_KANGARU_INCLUDE_KANGARU_TYPE_ID_HPP
<|endoftext|>


<commit_before>/*
 * Copyright (C) Andrey Pikas
 */

#pragma once

#include <memory>

#include <uv.h>

#include <pruv/list_node.hpp>
#include <pruv/process.hpp>
#include <pruv/shmem_buffer.hpp>
#include <pruv/tcp_con.hpp>

namespace pruv {

class dispatcher {
public:
    virtual ~dispatcher();
    /// new_loop, worker_name and worker_args must be valid until stop called.
    void start(uv_loop_t *new_loop, const char *ip, int port,
            size_t workers_max, const char *worker_name,
            const char * const *worker_args) noexcept;
    void stop() noexcept;
    /// Stop timer.
    void on_loop_exit() noexcept;

private:
    struct shmem_buffer_node : shmem_buffer, list_node<shmem_buffer_node> {};

    struct worker_process;

protected:
    /// Buffered tcp connection
    class tcp_context : private tcp_con, list_node<tcp_context> {
    protected:
        /// Called before reading new message.
        /// If returns false, connection will be closed.
        /// It can be used for moving to the begin of buffer those part of
        /// message which will be readed with previous message at the same
        /// time (when pipelining used).
        /// It can be used to pass to the worker some headers from
        /// PROXY-protocol for every message, not only at connection start.
        virtual bool prepare_for_request(shmem_buffer *) noexcept = 0;
        /// Called after reading len bytes into buffer at position s.
        /// If returns false, connection will be closed.
        virtual bool parse_request(shmem_buffer *buf, size_t len)
            noexcept = 0;
        /// Called after request parsing.
        /// When this returns non zero length, message of this size will be
        /// passed to the worker and reading from socket will be stopped until
        /// worker finished.
        virtual size_t request_size() const noexcept = 0;
        /// Returns protocol name for passing to worker.
        virtual const char * request_protocol() const noexcept = 0;
        /// Called before starting new response message, received from worker.
        /// If returns false, connection will be closed.
        virtual bool prepare_for_response() noexcept = 0;
        /// Called before sending data currently mapped in the buf.
        /// If returns false, connection will be closed.
        virtual bool parse_response(shmem_buffer *buf) noexcept = 0;
        /// Called after writing last response chunk.
        /// If returns false, connection will be closed.
        virtual bool finish_response() noexcept = 0;

    private:
        friend dispatcher;
        friend list_node;
        /// Remove connection from any dispatcher's list.
        /// Return used buffers into dispatcher.
        /// Break reference from worker to this connection.
        void remove_from_dispatcher() noexcept;

        /// Buffer for reading last request.
        shmem_buffer_node *read_buffer = nullptr;
        /// Buffers with responses.
        list_node<shmem_buffer_node> resp_buffers;
        /// Worker processing last request. On EOF there is no need to stop it,
        /// but its result must be ignored. Stored in connection to break
        /// reference worker->processed_con on EOF received.
        worker_process *worker = nullptr;
        uv_write_t write_req;
        uint64_t timeout;
        /// Number of elements in resp_buffers list.
        unsigned resp_buffers_num = 0;
        enum list_id_enum {
            LIST_IDLE,
            LIST_READING,
            LIST_SCHEDULING,
            LIST_PROCESSING,
            LIST_WRITING
        } list_id;
    };

    /// Allocate connection structure.
    virtual tcp_context * create_connection() noexcept = 0;
    /// Destruct and free connection structure.
    virtual void free_connection(tcp_context *con) noexcept = 0;

private:
    struct worker_process : public process, list_node<worker_process> {
        /// Connection, which request this worker process now.
        /// On exit connection must be closed.
        tcp_context *processed_con = nullptr;

        /// Buffers with request and response.
        /// When connection closed, but worker still process request, buffers
        /// can't be reused. Therefore buffers owned by worker for processing
        /// time.
        shmem_buffer_node *in_buf = nullptr;
        shmem_buffer_node *out_buf = nullptr;

        /// io_state used to protect single pipe_buf.
        /// Buffer can be used for reading when IO_READ,
        /// for writing when IO_WRITE, and can't be used when IO_IDLE.
        /// Also it allows not stop polling pipe when worker has no work.
        enum io_state_type {
            IO_IDLE,
            IO_READ,
            IO_WRITE
        };
        io_state_type io_state = IO_IDLE;
        /// Time when current operation must be finished.
        uint64_t timeout;

        /// Buffer for request to worker and for response from it.
        char pipe_buf[256];
        /// Pointer inside to pipe_buf for reading response by chunks.
        char *pipe_buf_ptr = pipe_buf;
        /// Request for writing task into worker's pipe.
        uv_write_t write_req;
    };

    bool start_server(const char *ip, int port) noexcept;
    void on_connection(uv_stream_t *server, int status) noexcept;
    void stop_server() noexcept;

    /// Close connections and release its resources.
    void close_connections(list_node<tcp_context> &list) noexcept;

    /// Take one request and one worker and send request into worker.
    void schedule() noexcept;

    /// Spawn worker process and put it into free_workers list.
    void spawn_worker() noexcept;
    /// Kill worker and stop reading it's pipe. Worker must be in some list.
    void kill_worker(worker_process *w) noexcept;

    void on_worker_exit(worker_process *w, int64_t exit_code, int signal)
        noexcept;
    void on_worker_read(worker_process *w, ssize_t nread, const uv_buf_t *buf)
        noexcept;

    /// Take buffer from cache or create new. Puts buffer into in_use_bufs.
    shmem_buffer_node * get_buffer(bool for_request) noexcept;

    /// Return buffer into cache. Remove buffer from in_use_bufs.
    void return_buffer(shmem_buffer_node *buf, bool for_request) noexcept;

    /// Close shared memory objects and free memory.
    /// Must be called only when there is no references to any buffer
    /// in the buf_list.
    void close_buffers(list_node<shmem_buffer_node> &buf_list) noexcept;

    /// Start reading connection in loop.
    bool read_connection(tcp_context *con) noexcept;
    /// Prepare buffer for reading data from connection.
    void read_con_alloc_cb(tcp_context *con, size_t suggested_size,
            uv_buf_t *buf) noexcept;
    /// Process data readed from connection.
    void read_con_cb(tcp_context *con, ssize_t nread, const uv_buf_t *buf)
        noexcept;
    /// Write data from con->resp_buffers to connection by chunks.
    void write_con(tcp_context *con) noexcept;
    /// Called after writing last chunk of response to connection.
    /// Prepares connection for reading next request.
    void on_end_write_con(tcp_context *con) noexcept;
    /// Remove connection from current list and push_back to specified.
    /// Update connection timeout.
    void move_to(tcp_context::list_id_enum dst, tcp_context *con) noexcept;

    bool start_timer() noexcept;
    void close_timer() noexcept;
    void on_timer_tick() noexcept;
    void close_old_connections(list_node<tcp_context> &list) noexcept;

    static constexpr unsigned IDLE_TIMEOUT = 30'000;
    static constexpr unsigned READ_TIMEOUT = 10'000;
    static constexpr unsigned WRITE_TIMEOUT = 15'000;
    static constexpr unsigned PROCESSING_TIMEOUT = 10'000;
    static constexpr unsigned KILL_TIMEOUT = 10'000;
    static constexpr unsigned TIMER_PERIOD = 5'000;
    static constexpr unsigned RESPONSES_MAXDEPTH = 10;

    uv_loop_t *loop = nullptr;
    const char *worker_name = nullptr;
    const char * const *worker_args = nullptr;
    size_t workers_cnt = 0;
    size_t workers_max = 0;

    uv_tcp_t tcp_server;
    uv_timer_t timer;

    /// Connections in this list are inactive.
    list_node<tcp_context> clients_idle;
    /// Connections in this list are readed.
    list_node<tcp_context> clients_reading;
    /// Connections with fully read request without worker.
    /// Reading stopped for this connections.
    list_node<tcp_context> clients_scheduling;
    /// Connections waits response from worker.
    /// Reading stopped for this connections.
    list_node<tcp_context> clients_processing;
    /// Connections with partially writed response.
    /// If connection readed and writed at the same time, it's in this list,
    /// because writing response is more important, then reading new request
    /// (because responses make buffers queue).
    /// Reading may be stopped (but may not).
    list_node<tcp_context> clients_writing;

    /// Idle workers.
    list_node<worker_process> free_workers;
    /// Workers serving some request now.
    list_node<worker_process> in_use_workers;
    /// Workers to which SIGTERM signal is sent.
    list_node<worker_process> terminated_workers;

    /// free buffers for reading request
    list_node<shmem_buffer_node> req_bufs;
    /// free buffers for writing response
    list_node<shmem_buffer_node> resp_bufs;
    /// non free buffers
    list_node<shmem_buffer_node> in_use_bufs;
};

} // namespace pruv
<commit_msg>Virtual destructor.<commit_after>/*
 * Copyright (C) Andrey Pikas
 */

#pragma once

#include <memory>

#include <uv.h>

#include <pruv/list_node.hpp>
#include <pruv/process.hpp>
#include <pruv/shmem_buffer.hpp>
#include <pruv/tcp_con.hpp>

namespace pruv {

class dispatcher {
public:
    virtual ~dispatcher();
    /// new_loop, worker_name and worker_args must be valid until stop called.
    void start(uv_loop_t *new_loop, const char *ip, int port,
            size_t workers_max, const char *worker_name,
            const char * const *worker_args) noexcept;
    void stop() noexcept;
    /// Stop timer.
    void on_loop_exit() noexcept;

private:
    struct shmem_buffer_node : shmem_buffer, list_node<shmem_buffer_node> {};

    struct worker_process;

protected:
    /// Buffered tcp connection
    class tcp_context : private tcp_con, list_node<tcp_context> {
    public:
        virtual ~tcp_context() {}
    protected:
        /// Called before reading new message.
        /// If returns false, connection will be closed.
        /// It can be used for moving to the begin of buffer those part of
        /// message which will be readed with previous message at the same
        /// time (when pipelining used).
        /// It can be used to pass to the worker some headers from
        /// PROXY-protocol for every message, not only at connection start.
        virtual bool prepare_for_request(shmem_buffer *) noexcept = 0;
        /// Called after reading len bytes into buffer at position s.
        /// If returns false, connection will be closed.
        virtual bool parse_request(shmem_buffer *buf, size_t len)
            noexcept = 0;
        /// Called after request parsing.
        /// When this returns non zero length, message of this size will be
        /// passed to the worker and reading from socket will be stopped until
        /// worker finished.
        virtual size_t request_size() const noexcept = 0;
        /// Returns protocol name for passing to worker.
        virtual const char * request_protocol() const noexcept = 0;
        /// Called before starting new response message, received from worker.
        /// If returns false, connection will be closed.
        virtual bool prepare_for_response() noexcept = 0;
        /// Called before sending data currently mapped in the buf.
        /// If returns false, connection will be closed.
        virtual bool parse_response(shmem_buffer *buf) noexcept = 0;
        /// Called after writing last response chunk.
        /// If returns false, connection will be closed.
        virtual bool finish_response() noexcept = 0;

    private:
        friend dispatcher;
        friend list_node;
        /// Remove connection from any dispatcher's list.
        /// Return used buffers into dispatcher.
        /// Break reference from worker to this connection.
        void remove_from_dispatcher() noexcept;

        /// Buffer for reading last request.
        shmem_buffer_node *read_buffer = nullptr;
        /// Buffers with responses.
        list_node<shmem_buffer_node> resp_buffers;
        /// Worker processing last request. On EOF there is no need to stop it,
        /// but its result must be ignored. Stored in connection to break
        /// reference worker->processed_con on EOF received.
        worker_process *worker = nullptr;
        uv_write_t write_req;
        uint64_t timeout;
        /// Number of elements in resp_buffers list.
        unsigned resp_buffers_num = 0;
        enum list_id_enum {
            LIST_IDLE,
            LIST_READING,
            LIST_SCHEDULING,
            LIST_PROCESSING,
            LIST_WRITING
        } list_id;
    };

    /// Allocate connection structure.
    virtual tcp_context * create_connection() noexcept = 0;
    /// Destruct and free connection structure.
    virtual void free_connection(tcp_context *con) noexcept = 0;

private:
    struct worker_process : public process, list_node<worker_process> {
        /// Connection, which request this worker process now.
        /// On exit connection must be closed.
        tcp_context *processed_con = nullptr;

        /// Buffers with request and response.
        /// When connection closed, but worker still process request, buffers
        /// can't be reused. Therefore buffers owned by worker for processing
        /// time.
        shmem_buffer_node *in_buf = nullptr;
        shmem_buffer_node *out_buf = nullptr;

        /// io_state used to protect single pipe_buf.
        /// Buffer can be used for reading when IO_READ,
        /// for writing when IO_WRITE, and can't be used when IO_IDLE.
        /// Also it allows not stop polling pipe when worker has no work.
        enum io_state_type {
            IO_IDLE,
            IO_READ,
            IO_WRITE
        };
        io_state_type io_state = IO_IDLE;
        /// Time when current operation must be finished.
        uint64_t timeout;

        /// Buffer for request to worker and for response from it.
        char pipe_buf[256];
        /// Pointer inside to pipe_buf for reading response by chunks.
        char *pipe_buf_ptr = pipe_buf;
        /// Request for writing task into worker's pipe.
        uv_write_t write_req;
    };

    bool start_server(const char *ip, int port) noexcept;
    void on_connection(uv_stream_t *server, int status) noexcept;
    void stop_server() noexcept;

    /// Close connections and release its resources.
    void close_connections(list_node<tcp_context> &list) noexcept;

    /// Take one request and one worker and send request into worker.
    void schedule() noexcept;

    /// Spawn worker process and put it into free_workers list.
    void spawn_worker() noexcept;
    /// Kill worker and stop reading it's pipe. Worker must be in some list.
    void kill_worker(worker_process *w) noexcept;

    void on_worker_exit(worker_process *w, int64_t exit_code, int signal)
        noexcept;
    void on_worker_read(worker_process *w, ssize_t nread, const uv_buf_t *buf)
        noexcept;

    /// Take buffer from cache or create new. Puts buffer into in_use_bufs.
    shmem_buffer_node * get_buffer(bool for_request) noexcept;

    /// Return buffer into cache. Remove buffer from in_use_bufs.
    void return_buffer(shmem_buffer_node *buf, bool for_request) noexcept;

    /// Close shared memory objects and free memory.
    /// Must be called only when there is no references to any buffer
    /// in the buf_list.
    void close_buffers(list_node<shmem_buffer_node> &buf_list) noexcept;

    /// Start reading connection in loop.
    bool read_connection(tcp_context *con) noexcept;
    /// Prepare buffer for reading data from connection.
    void read_con_alloc_cb(tcp_context *con, size_t suggested_size,
            uv_buf_t *buf) noexcept;
    /// Process data readed from connection.
    void read_con_cb(tcp_context *con, ssize_t nread, const uv_buf_t *buf)
        noexcept;
    /// Write data from con->resp_buffers to connection by chunks.
    void write_con(tcp_context *con) noexcept;
    /// Called after writing last chunk of response to connection.
    /// Prepares connection for reading next request.
    void on_end_write_con(tcp_context *con) noexcept;
    /// Remove connection from current list and push_back to specified.
    /// Update connection timeout.
    void move_to(tcp_context::list_id_enum dst, tcp_context *con) noexcept;

    bool start_timer() noexcept;
    void close_timer() noexcept;
    void on_timer_tick() noexcept;
    void close_old_connections(list_node<tcp_context> &list) noexcept;

    static constexpr unsigned IDLE_TIMEOUT = 30'000;
    static constexpr unsigned READ_TIMEOUT = 10'000;
    static constexpr unsigned WRITE_TIMEOUT = 15'000;
    static constexpr unsigned PROCESSING_TIMEOUT = 10'000;
    static constexpr unsigned KILL_TIMEOUT = 10'000;
    static constexpr unsigned TIMER_PERIOD = 5'000;
    static constexpr unsigned RESPONSES_MAXDEPTH = 10;

    uv_loop_t *loop = nullptr;
    const char *worker_name = nullptr;
    const char * const *worker_args = nullptr;
    size_t workers_cnt = 0;
    size_t workers_max = 0;

    uv_tcp_t tcp_server;
    uv_timer_t timer;

    /// Connections in this list are inactive.
    list_node<tcp_context> clients_idle;
    /// Connections in this list are readed.
    list_node<tcp_context> clients_reading;
    /// Connections with fully read request without worker.
    /// Reading stopped for this connections.
    list_node<tcp_context> clients_scheduling;
    /// Connections waits response from worker.
    /// Reading stopped for this connections.
    list_node<tcp_context> clients_processing;
    /// Connections with partially writed response.
    /// If connection readed and writed at the same time, it's in this list,
    /// because writing response is more important, then reading new request
    /// (because responses make buffers queue).
    /// Reading may be stopped (but may not).
    list_node<tcp_context> clients_writing;

    /// Idle workers.
    list_node<worker_process> free_workers;
    /// Workers serving some request now.
    list_node<worker_process> in_use_workers;
    /// Workers to which SIGTERM signal is sent.
    list_node<worker_process> terminated_workers;

    /// free buffers for reading request
    list_node<shmem_buffer_node> req_bufs;
    /// free buffers for writing response
    list_node<shmem_buffer_node> resp_bufs;
    /// non free buffers
    list_node<shmem_buffer_node> in_use_bufs;
};

} // namespace pruv
<|endoftext|>


<commit_before>/**
 * @file llnamebox.cpp
 * @brief A text display widget
 *
 * $LicenseInfo:firstyear=2001&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010, Linden Research, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

#include "llviewerprecompiledheaders.h"

#include "llnamebox.h"

#include "llerror.h"
#include "llfontgl.h"
#include "llui.h"
#include "llviewercontrol.h"
#include "lluuid.h"

#include "llcachename.h"

// statics
std::set<LLNameBox*> LLNameBox::sInstances;

static LLDefaultChildRegistry::Register<LLNameBox> r("name_box");

LLNameBox::LLNameBox(const Params& p)
:	LLTextBox(p)
{
	mNameID = LLUUID::null;
	mLink = p.link;
	mInitialValue = p.initial_value().asString();
	LLNameBox::sInstances.insert(this);
	setText(LLStringUtil::null);
}

LLNameBox::~LLNameBox()
{
	LLNameBox::sInstances.erase(this);
}

void LLNameBox::setNameID(const LLUUID& name_id, BOOL is_group)
{
	mNameID = name_id;

	std::string name;
	BOOL got_name = FALSE;

	if (!is_group)
	{
		got_name = gCacheName->getFullName(name_id, name);
	}
	else
	{
		got_name = gCacheName->getGroupName(name_id, name);
	}

	// Got the name already? Set it.
	// Otherwise it will be set later in refresh().
	if (got_name)
		setName(name, is_group);
	else
		setText(mInitialValue);
}

void LLNameBox::refresh(const LLUUID& id, const std::string& firstname,
						const std::string& lastname, BOOL is_group)
{
	if (id == mNameID)
	{
		std::string name;
		if (!is_group)
		{
			name = firstname + " " + lastname;
		}
		else
		{
			name = firstname;
		}
		setName(name, is_group);
	}
}

void LLNameBox::refreshAll(const LLUUID& id, const std::string& firstname,
						   const std::string& lastname, BOOL is_group)
{
	std::set<LLNameBox*>::iterator it;
	for (it = LLNameBox::sInstances.begin();
		 it != LLNameBox::sInstances.end();
		 ++it)
	{
		LLNameBox* box = *it;
		box->refresh(id, firstname, lastname, is_group);
	}
}

void LLNameBox::setName(const std::string& name, BOOL is_group)
{
	if (mLink)
	{
		std::string url;

		if (is_group)
			url = "[secondlife:///app/group/" + LLURI::escape(name) + "/about " + name + "]";
		else
			url = "[secondlife:///app/agent/" + mNameID.asString() + "/about " + name + "]";

		setText(url);
	}
	else
	{
		setText(name);
	}
}
<commit_msg>STORM-215 FIXED Disabled URL highlighting in name_box'es.<commit_after>/**
 * @file llnamebox.cpp
 * @brief A text display widget
 *
 * $LicenseInfo:firstyear=2001&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010, Linden Research, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

#include "llviewerprecompiledheaders.h"

#include "llnamebox.h"

#include "llerror.h"
#include "llfontgl.h"
#include "llui.h"
#include "llviewercontrol.h"
#include "lluuid.h"

#include "llcachename.h"

// statics
std::set<LLNameBox*> LLNameBox::sInstances;

static LLDefaultChildRegistry::Register<LLNameBox> r("name_box");

LLNameBox::LLNameBox(const Params& p)
:	LLTextBox(p)
{
	mNameID = LLUUID::null;
	mLink = p.link;
	mParseHTML = mLink; // STORM-215
	mInitialValue = p.initial_value().asString();
	LLNameBox::sInstances.insert(this);
	setText(LLStringUtil::null);
}

LLNameBox::~LLNameBox()
{
	LLNameBox::sInstances.erase(this);
}

void LLNameBox::setNameID(const LLUUID& name_id, BOOL is_group)
{
	mNameID = name_id;

	std::string name;
	BOOL got_name = FALSE;

	if (!is_group)
	{
		got_name = gCacheName->getFullName(name_id, name);
	}
	else
	{
		got_name = gCacheName->getGroupName(name_id, name);
	}

	// Got the name already? Set it.
	// Otherwise it will be set later in refresh().
	if (got_name)
		setName(name, is_group);
	else
		setText(mInitialValue);
}

void LLNameBox::refresh(const LLUUID& id, const std::string& firstname,
						const std::string& lastname, BOOL is_group)
{
	if (id == mNameID)
	{
		std::string name;
		if (!is_group)
		{
			name = firstname + " " + lastname;
		}
		else
		{
			name = firstname;
		}
		setName(name, is_group);
	}
}

void LLNameBox::refreshAll(const LLUUID& id, const std::string& firstname,
						   const std::string& lastname, BOOL is_group)
{
	std::set<LLNameBox*>::iterator it;
	for (it = LLNameBox::sInstances.begin();
		 it != LLNameBox::sInstances.end();
		 ++it)
	{
		LLNameBox* box = *it;
		box->refresh(id, firstname, lastname, is_group);
	}
}

void LLNameBox::setName(const std::string& name, BOOL is_group)
{
	if (mLink)
	{
		std::string url;

		if (is_group)
			url = "[secondlife:///app/group/" + mNameID.asString() + "/about " + name + "]";
		else
			url = "[secondlife:///app/agent/" + mNameID.asString() + "/about " + name + "]";

		setText(url);
	}
	else
	{
		setText(name);
	}
}
<|endoftext|>


<commit_before><commit_msg>fix windows build break.<commit_after><|endoftext|>


<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/renderer_host/sync_resource_handler.h"

#include "base/logging.h"
#include "chrome/common/render_messages.h"
#include "net/http/http_response_headers.h"

SyncResourceHandler::SyncResourceHandler(
    ResourceDispatcherHost::Receiver* receiver,
    const GURL& url,
    IPC::Message* result_message)
    : read_buffer_(new net::IOBuffer(kReadBufSize)),
      receiver_(receiver),
      result_message_(result_message) {
  result_.final_url = url;
}

SyncResourceHandler::~SyncResourceHandler() {
}

bool SyncResourceHandler::OnUploadProgress(int request_id,
                                           uint64 position,
                                           uint64 size) {
  return true;
}

bool SyncResourceHandler::OnRequestRedirected(int request_id,
                                              const GURL& new_url,
                                              ResourceResponse* response,
                                              bool* defer) {
  // TODO(darin): It would be much better if this could live in WebCore, but
  // doing so requires API changes at all levels.  Similar code exists in
  // WebCore/platform/network/cf/ResourceHandleCFNet.cpp :-(
  if (new_url.GetOrigin() != result_.final_url.GetOrigin()) {
    LOG(ERROR) << "Cross origin redirect denied";
    return false;
  }
  result_.final_url = new_url;
  return true;
}

bool SyncResourceHandler::OnResponseStarted(int request_id,
                                            ResourceResponse* response) {
  // We don't care about copying the status here.
  result_.headers = response->response_head.headers;
  result_.mime_type = response->response_head.mime_type;
  result_.charset = response->response_head.charset;
  return true;
}

bool SyncResourceHandler::OnWillStart(int request_id,
                                      const GURL& url,
                                      bool* defer) {
  return true;
}

bool SyncResourceHandler::OnWillRead(int request_id, net::IOBuffer** buf,
                                     int* buf_size, int min_size) {
  DCHECK(min_size == -1);
  *buf = read_buffer_.get();
  // TODO(willchan): Remove after debugging bug 16371.
  CHECK(read_buffer_->data());
  *buf_size = kReadBufSize;
  return true;
}

bool SyncResourceHandler::OnReadCompleted(int request_id, int* bytes_read) {
  if (!*bytes_read)
    return true;
  result_.data.append(read_buffer_->data(), *bytes_read);
  return true;
}

bool SyncResourceHandler::OnResponseCompleted(
    int request_id,
    const URLRequestStatus& status,
    const std::string& security_info) {
  result_.status = status;

  ViewHostMsg_SyncLoad::WriteReplyParams(result_message_, result_);
  receiver_->Send(result_message_);
  result_message_ = NULL;
  return true;
}

void SyncResourceHandler::OnRequestClosed() {
  if (!result_message_)
    return;

  result_message_->set_reply_error();
  receiver_->Send(result_message_);
  receiver_ = NULL;  // URLRequest is gone, and perhaps also the receiver.
}
<commit_msg>Fix a bad line ending in sync_resource_handler.cc.<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/renderer_host/sync_resource_handler.h"

#include "base/logging.h"
#include "chrome/common/render_messages.h"
#include "net/http/http_response_headers.h"

SyncResourceHandler::SyncResourceHandler(
    ResourceDispatcherHost::Receiver* receiver,
    const GURL& url,
    IPC::Message* result_message)
    : read_buffer_(new net::IOBuffer(kReadBufSize)),
      receiver_(receiver),
      result_message_(result_message) {
  result_.final_url = url;
}

SyncResourceHandler::~SyncResourceHandler() {
}

bool SyncResourceHandler::OnUploadProgress(int request_id,
                                           uint64 position,
                                           uint64 size) {
  return true;
}

bool SyncResourceHandler::OnRequestRedirected(int request_id,
                                              const GURL& new_url,
                                              ResourceResponse* response,
                                              bool* defer) {
  // TODO(darin): It would be much better if this could live in WebCore, but
  // doing so requires API changes at all levels.  Similar code exists in
  // WebCore/platform/network/cf/ResourceHandleCFNet.cpp :-(
  if (new_url.GetOrigin() != result_.final_url.GetOrigin()) {
    LOG(ERROR) << "Cross origin redirect denied";
    return false;
  }
  result_.final_url = new_url;
  return true;
}

bool SyncResourceHandler::OnResponseStarted(int request_id,
                                            ResourceResponse* response) {
  // We don't care about copying the status here.
  result_.headers = response->response_head.headers;
  result_.mime_type = response->response_head.mime_type;
  result_.charset = response->response_head.charset;
  return true;
}

bool SyncResourceHandler::OnWillStart(int request_id,
                                      const GURL& url,
                                      bool* defer) {
  return true;
}

bool SyncResourceHandler::OnWillRead(int request_id, net::IOBuffer** buf,
                                     int* buf_size, int min_size) {
  DCHECK(min_size == -1);
  *buf = read_buffer_.get();
  // TODO(willchan): Remove after debugging bug 16371.
  CHECK(read_buffer_->data());
  *buf_size = kReadBufSize;
  return true;
}

bool SyncResourceHandler::OnReadCompleted(int request_id, int* bytes_read) {
  if (!*bytes_read)
    return true;
  result_.data.append(read_buffer_->data(), *bytes_read);
  return true;
}

bool SyncResourceHandler::OnResponseCompleted(
    int request_id,
    const URLRequestStatus& status,
    const std::string& security_info) {
  result_.status = status;

  ViewHostMsg_SyncLoad::WriteReplyParams(result_message_, result_);
  receiver_->Send(result_message_);
  result_message_ = NULL;
  return true;
}

void SyncResourceHandler::OnRequestClosed() {
  if (!result_message_)
    return;

  result_message_->set_reply_error();
  receiver_->Send(result_message_);
  receiver_ = NULL;  // URLRequest is gone, and perhaps also the receiver.
}
<|endoftext|>